ObjectReference Broken Pointer Bug

From the CreationKit Wiki
Revision as of 13:59, 30 October 2013 by imported>Taleden (Created page with "==Introduction== These are my (taleden's) logs and notes regarding the ObjectReference broken pointer bug, whose symptom is the log error "no native object bound to the scrip...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

Introduction

These are my (taleden's) logs and notes regarding the ObjectReference broken pointer bug, whose symptom is the log error "no native object bound to the script object, or object is of incorrect type". On 2013-10-25 I posted the following to Bethesda's CreationKit forum:

The folks at the Unofficial Skyrim Patch (and probably lots of other modders) have been struggling for months with ObjectReference pointers that go bad, and I think it may actually be a bug in the Papyrus scripting engine, or the game engine, or maybe in the interface between the two.

The symptom of this bug is an error in the debug log that reads "Unable to call [Method] - no native object bound to the script object, or object is of incorrect type". This happens even when the pointer on which the method is being called is not None; when that is the case, the error instead says "Cannot call [Method] on a None object, aborting function call".

I believe the bug arises whenever an object reference has an extra script attached to it (extending the ObjectReference script), and that item is moved from a container/inventory into the world, while it also has a persistent reference.

Here's a short example that illustrates the problem:

Form item = Game.GetForm(0x10AA19) ; Silver Sword
ObjectReference ref = Game.GetPlayer().PlaceAtMe(item)
Debug.Trace(ref+" GetFormID()="+ref.GetFormID()+", SilverPerk="+(ref as SilverSwordScript).SilverPerk)
Game.GetPlayer().AddItem(ref)
Debug.Trace(ref+" GetFormID()="+ref.GetFormID()+", SilverPerk="+(ref as SilverSwordScript).SilverPerk)
ref = Game.GetPlayer().DropObject(item)
Debug.Trace(ref+" GetFormID()="+ref.GetFormID()+", SilverPerk="+(ref as SilverSwordScript).SilverPerk) ; line 33

The log then reads:

[10/25/2013 - 02:10:58PM] [SilverSwordScript < (FF000898)>] GetFormID()=-16775016, SilverPerk=[Perk < (0010D685)>] [10/25/2013 - 02:10:58PM] [SilverSwordScript <Item 3 in container (00000014)>] GetFormID()=-16775016, SilverPerk=[Perk < (0010D685)>] [10/25/2013 - 02:10:58PM] error: Unable to call GetFormID - no native object bound to the script object, or object is of incorrect type stack: [Item 3 in container (00000014)].SilverSwordScript.GetFormID() - "<native>" Line ? [alias PlayerRef on quest TestQuest (0A000D6A)].TestQuest_PlayerRef_Script.OnUpdate() - "TestQuest_PlayerRef_Script.psc" Line 33 [10/25/2013 - 02:10:58PM] warning: Assigning None to a non-object variable named "::temp6" stack: [alias PlayerRef on quest TestQuest (0A000D6A)].TestQuest_PlayerRef_Script.OnUpdate() - "TestQuest_PlayerRef_Script.psc" Line 33 [10/25/2013 - 02:10:58PM] [SilverSwordScript <Item 3 in container (00000014)>] GetFormID()=0, SilverPerk=[Perk < (0010D685)>]

Note that the item must have a script either on its base form (as the Silver Sword does here) or on the particular cell-placed reference that the player picks up (such as Ghostblade and Zephyr), and the reference must be persistent at the moment it is dropped back into the world (as is done here by having the PlaceAtMe()-created reference stored in a running script variable at the time DropObject() is called; PlaceAtMe() can also force the created reference to be persistent, but the result is the same in this test).

When these conditions are met, then any ObjectReference pointer which refers to the item is prone to break, as seen here in the pointer returned from DropObject(). When a pointer breaks in this way, then no native methods can be called on it (such as GetFormID()), however methods which are fully defined in Papyrus on the attached script or any of its parent scripts can still be called normally (such as the implicit SilverPerk property getter function). This implies that the Papyrus script object is no longer correctly linked to its corresponding game engine object, so when it tries to call into the game engine to evaluate a native method, the engine reports that it has no (compatible) object to run it on.

Note that the bug is not limited to DropObject(), that's just the quickest way to invoke it. For example, the player can drop the item manually, and any (persistent) ObjectReference pointer which was previously received and cached in OnItemAdded(), OnItemEquipped() etc. will immediately become broken. Or, the player can drop the item while it is equipped, and then OnItemUnequipped() receives a broken pointer, but OnItemRemoved() receives a functional pointer. The key elements are the persistent object reference, and the extra attached script.

I have a lot more log data if anyone is interested, plus an object reference testing mod that I've been using to diagnose this issue. Apologies also if this has been reported and discussed before, I did a search and only found other players with the same log error, but no investigation or explanation.

This article contains the log data mentioned above.

Testing Methodology