no native object ... or object is of incorrect type

Post » Sat Oct 26, 2013 12:30 pm

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 SwordObjectReference 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 ] 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 typestack:    [Item 3 in container  (00000014)].SilverSwordScript.GetFormID() - "" 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 ] 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 method can be called on it which is defined by any parent script (such as GetFormID() from the Form script), however methods which are defined by the extra attached script can still be called normally (such as the implicit SilverPerk property getter function). The implication seems to be that class-inheritance data has been lost somehow, either on the script object in the Papyrus engine, or on the native reference object in the game engine. This in turn causes Papyrus to fail the implicit cast which is required to call methods defined on parent scripts.
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.
User avatar
Kaley X
 
Posts: 3372
Joined: Wed Jul 05, 2006 5:46 pm

Post » Sat Oct 26, 2013 11:13 am

Seems related to this issue: http://www.gamesas.com/topic/1472522-wipz-skyrim-script-extender-skse/?p=23079468

After seeing your GetFormID() calls, are we running into crap from the negative number thing?

FF000898 spitting back -16775016 is clearly wrong. That should be 4278192280 instead. If I plug -16775016 into the Windows calculator and hit "hex" this comes back: FFFFFFFFFF000898 which is interesting because of the last 8 digits there.

User avatar
Mrs shelly Sugarplum
 
Posts: 3440
Joined: Thu Jun 15, 2006 2:16 am

Post » Sat Oct 26, 2013 12:58 pm

That's because your windows calculator is 64 bit, but Skyrim is 32 bit. In 32 bit math, the translation is correct: any hex number beginning with 0x8 or higher is considered negative when converted to a signed integer (the most significant bit is 1).

User avatar
Yvonne Gruening
 
Posts: 3503
Joined: Mon Apr 23, 2007 7:31 pm

Post » Sun Oct 27, 2013 3:14 am

Just to make sure, I adapted the test case to use a cell-placed reference (with a nice low FormID) instead of the engine-generated 0xFF------ reference:

ObjectReference ref = Game.GetForm(0x94A2C) as ObjectReference ; Ghostblade cell-placed referenceDebug.Trace(ref+" GetFormID()="+ref.GetFormID()+", havokOnHit="+(ref as defaultDisableHavokOnLoad).havokOnHit)ref.Enable()Form item = ref.GetBaseObject()Game.GetPlayer().AddItem(ref)Debug.Trace(ref+" GetFormID()="+ref.GetFormID()+", havokOnHit="+(ref as defaultDisableHavokOnLoad).havokOnHit)ref = Game.GetPlayer().DropObject(item)Debug.Trace(ref+" GetFormID()="+ref.GetFormID()+", havokOnHit="+(ref as defaultDisableHavokOnLoad).havokOnHit)

And the result is the same:

[10/25/2013 - 03:27:36PM] [defaultDisableHavokOnLoad < (00094A2C)>] GetFormID()=608812, havokOnHit=TRUE[10/25/2013 - 03:27:36PM] [defaultDisableHavokOnLoad ] GetFormID()=608812, havokOnHit=TRUE[10/25/2013 - 03:27:37PM] error: Unable to call GetFormID - no native object bound to the script object, or object is of incorrect typestack:    [Item 3 in container  (00000014)].defaultDisableHavokOnLoad.GetFormID() - "" Line ?    [alias PlayerRef on quest TestQuest (0A000D6A)].TestQuest_PlayerRef_Script.OnUpdate() - "TestQuest_PlayerRef_Script.psc" Line 43[10/25/2013 - 03:27:37PM] 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 43[10/25/2013 - 03:27:37PM] [defaultDisableHavokOnLoad ] GetFormID()=0, havokOnHit=TRUE

I added ref.Enable() since that Ghostblade reference starts disabled, but it's not necessary: the log looks identical either way, you just can't see it drop in-game if the ref stays disabled.

This also demonstrates that the bug is the same no matter if the script is attached to a base form, or to the cell-placed reference. Scripts attached via quest reference aliases do not seem to cause the same problem, however, but I'm not sure if that's because aliases are just different or because alias scripts extend ReferenceAlias instead of ObjectReference.

User avatar
Lexy Dick
 
Posts: 3459
Joined: Mon Feb 12, 2007 12:15 pm

Post » Sat Oct 26, 2013 9:29 pm

A quick additional note on the "[Item xx in container yy]" type references (such as [Item 3 in container (00000014)] in Taleden's log). For lack of a better name, we termed them "transient references" (Arthmoor's proposal) or simply "IxxCyy". These are my experiences so far:

  • [Item xx in container yy] references behave in most situations like "none" references: attempts at running member functions of the objectreference script on them will fail, and the papyrus log will spam the same errors as one would expect from trying to run those functions on a "none" reference.
  • Logically, however, an [Item xx in container yy] reference is NOT none: A check such as "If = None" will ALWAYS return false when is an [Item xx in container yy] type reference.

In other words "[Item xx in container yy]" is a disguised "none" which slips through all conventional sanity checks!

User avatar
Kelly Osbourne Kelly
 
Posts: 3426
Joined: Sun Nov 05, 2006 6:56 pm

Post » Sat Oct 26, 2013 11:25 am

My guess is that the None-ish-ness of those "transient references" is due to the Papyrus scripting engine and the native game engine being fairly separate pieces of code which only talk to each other through some limited API. So when a reference pointer breaks, Papyrus still has what it thinks is a normal ObjectReference data structure, which contains some information that identifies a corresponding game object data structure inside the game engine. So long as you only deal with this pointer within the Papyrus engine (i.e. calling script-defined methods on its extended script), the broken connection is irrelevant, but as soon as you want to call a native method, then Papyrus has to query the game engine, and this is where it discovers that its matching data structure is no longer valid somehow. Hence the phrasing of the error "no native object bound to the script object, or object is of incorrect type" -- I think there are two objects (data structures) involved, one in each of the separate engines, and the bug causes them to become out-of-sync.

Edit: this may also explain why most (all?) native method calls are latent and seem to be synchronized to the frame rate. When Papyrus has to call over to the main game engine, it has to wait for next time the main engine checks its message queue, processes the request and returns data to Papyrus, which probably happens once per main engine rendering loop.

User avatar
Camden Unglesbee
 
Posts: 3467
Joined: Wed Aug 15, 2007 8:30 am


Return to V - Skyrim