Load assert, getObjectByUID and "wait until afterLoad()"

Discussion and feedback on Construct 2

Post » Mon Nov 02, 2015 3:11 pm

Hi all -

I'm facing a problem with loading/saving, and I'm not too sure what's triggering an assert.

The assert is : "Do not call getObjectByUID in loadFromJSON: wait until afterLoad() to look up", and it's coming from preview.js:2863

Runtime.prototype.getObjectByUID = function (uid_)
{
assert2(!this.isLoadingState, "Do not call getObjectByUID in loadFromJSON: wait until afterLoad() to look up");
var uidstr = uid_.toString();

if (this.objectsByUid.hasOwnProperty(uidstr))
return this.objectsByUid[uidstr];
else
return null;
};

Initially I thought it was to do with objects dynamically created and destroyed by other objects (typically : on created > create some more stuff, on destroyed > destroy some more stuff the was previously created)

I had a nice repro-case with a pickup object. A pickup item is manually placed in the level ; on creation, the pickup item creates a "floater" item (could be some text, icon, effect, etc. floating above the pickup) ; the pickup keeps a reference (uid) to the floater it creates ; when the pickup item is collected by the player, it is destroyed , which also destroys the floater.

I moved things around a bit to avoid looking up UIDs in "on destroyed" triggers, and it seems that helped a bit.

Still, I have more cases of this assert firing when loading game states from a different layout (game menu) :

I breakpointed on the "return null;" and that never triggered, so I *think* this has actually no impact on the game (and I can't see anything wrong when playing the game after loading with the assert).

Nevertheless I'm still confused - what's the actual sequence of events when loading a game state, esp. regarding object creation/destruction ?

Has anyone ever faced this assert when loading save states ? Any idea what might be triggering it ?

I've been investigating on smaller test scenes and binary chopping through my events, but at this stage I don't have much left to go on and I haven't been able to narrow it down any further ; any pointer or random guess would be appreciated !
Image
Game Producer & Independent Developer - http://raphaelgervaise.com
B
23
S
9
Posts: 237
Reputation: 2,207

Post » Tue Nov 03, 2015 6:42 am

That's odd, I haven't encountered that. Sounds like a bug since I'd assume no events run during loading. Also "getObjectByUID" isn't just called when using "pick by uid", it's a helper function that's used elsewhere.

If you put a breakpoint inside the function definition of "assert2" (i think it's in common.js), you could then look at the call stack to see what functions were called before that. Still, that may not give you anything useful.

If it's your events causing it, i'd be it would be under a trigger. If it happens in the "on created" or "on destroyed" triggers then I'd say it's a bug to report. What other triggers do you use?
B
92
S
32
G
106
Posts: 5,273
Reputation: 69,457

Post » Tue Nov 03, 2015 9:30 am

Thanks for the input, 'really appreciated -

I tried going through the call stack, but but with limited understanding of the underlying low-level logic it doesn't give me much to go on to be honest ; it's definitely fired during the loading process, and it does seem to have to do with "on created" triggers, at which point it all becomes indices in condition arrays and event blocks and I get a bit lost in the events that are currently running when the assert triggers.

assert2 (preview_prelude.js:11)
Runtime.getObjectByUID (preview.js:2863)
cr.add_common_aces.cnds.PickByUID (commonace.js:475)
Condition.run_static (eveng.js:1303)
EventBlock.run (eveng.js:868)
EventBlock.run_subevents (eveng.js:996)
EventBlock.run_actions_and_subevents (eveng.js:935)
EventBlock.run (eveng.js:888)
Runtime.executeSingleTrigger (preview.js:4843)
Runtime.triggerOnSheetForTypeName (preview.js:4762)
Runtime.triggerOnSheet (preview.js:4694)
Runtime.trigger (preview.js:4665)
Acts.CallFunction (Function_plugin.js:180)
Action.run_object (eveng.js:1852)
EventBlock.run_actions_and_subevents (eveng.js:931)
EventBlock.run (eveng.js:888)
Runtime.executeSingleTrigger (preview.js:4843)
Runtime.triggerOnSheetForTypeName (preview.js:4762)
Runtime.triggerOnSheet (preview.js:4694)
Runtime.trigger (preview.js:4665)
Layout.startRunning (layout.js:381)
Runtime.doChangeLayout (preview.js:2743)
Runtime.loadFromJSONString (preview.js:5499)
Runtime.handleSaveLoad (preview.js:5302)
Runtime.logic (preview.js:2610)
Runtime.tick (preview.js:2322)
Runtime.initRendererAndLoader.tickFunc (preview.js:649)

As for the triggers I am using, well... Quite a lot ; it's a full platformer game - I'm unable to reproduce the issue on small test scenes, and binary chopping through my events doesn't get me very far in terms of narrowing things down.
Image
Game Producer & Independent Developer - http://raphaelgervaise.com
B
23
S
9
Posts: 237
Reputation: 2,207

Post » Tue Nov 03, 2015 9:46 am

*edit* Hmm on some further investigation, it seems it might be coming from the system I use to mimic "inheritance" via families and function calls -

Basically, most of my entities follow this pattern :
- Entity belongs to Family ; Family is used as an "Interface" to factor some data and logic
- "on Entity created" > call function "Family_method(Entity.UID)" (almost like a constructor calling a super-constructor to initialise data of higher-level interfaces)

A concrete example : Guard entity (a simple sprite of a guard patrolling left/right with a sine behaviour) that belongs to my SuspendableIF "interface" family (factored logic I use to suspend/resume entities when far away from the player so they don't cost any performance in my large levels when not close to the action)

Guard : on created
> set some member data / behaviours (sine magnitude and period)
> calls SuspendableIF_Init(Guard.UID) ; this generates some functions names (Guard_Suspend, Guard_Resume, Guard_Advance) that are cached in the SuspendableIF to be called later (removes the cost of dynamically generating these names from "class" info lookups)

Anyway, short version : the problem I'm facing might be coming from these UID look-ups in "on created" triggers during the loading phase !

I might try to summon Ashley on this one to confirm how things actually work in terms of entity creation during loading :
@Ashley : using UID lookups in "on created" triggers when loading game states that cause a layout transition seems to fire a "Do not call getObjectByUID in loadFromJSON: wait until afterLoad() to look up" assert. Does this seem like an expected behaviour ? If it is, are there any good recommendation to easily work around this ?
Last edited by Refeuh on Tue Nov 03, 2015 10:22 am, edited 3 times in total.
Image
Game Producer & Independent Developer - http://raphaelgervaise.com
B
23
S
9
Posts: 237
Reputation: 2,207

Post » Tue Nov 03, 2015 9:49 am

In the meantime I'll try to play around with my "on created" events to try to eliminate/delay all UID lookups. Hopefully this will solve the problem...
Image
Game Producer & Independent Developer - http://raphaelgervaise.com
B
23
S
9
Posts: 237
Reputation: 2,207

Post » Tue Nov 03, 2015 10:33 am

Confirmed the hunch !

OK, this is indeed coming from UID lookups in "on created" triggers during loading phases that cause a layout transition.

I worked around the issue by duplicating a bit of code in all entity creations, or delaying UID references (lazy-ctor style), as I was mostly mimic-ing a super-constructor behaviour via function calls ; this makes it virtually impossible to factor creation logic with "interfaces", but isn't too bad to deal with once you're aware of the limitation.

Thanks @R0J0hound for the input, just boucing some ideas and formulating possible causes to explain the problem help to think properly, in these cases.

@Ashley if I have time I'll try to recreate the assert in a smaller test scene to report it properly as a possible "bug" (or undocumented unexpected behaviour)
Image
Game Producer & Independent Developer - http://raphaelgervaise.com
B
23
S
9
Posts: 237
Reputation: 2,207

Post » Tue Nov 03, 2015 3:27 pm

Yeah, I think it's a bug. The warning is there because asking for an object by its UID when halfway through loading a project is dangerous, because the object you want may not have been loaded yet and therefore not be found. So in the SDK that warning is there to make sure you move your lookup to afterLoad() when all objects are available. But it shouldn't turn up in normal use of the events. So please file a bug report!
Scirra Founder
B
395
S
232
G
88
Posts: 24,368
Reputation: 193,746

Post » Thu Nov 05, 2015 9:06 am

Thanks for the confirmation, 'much appreciated.

I've created a minimal test scene demonstrating the problem ; bug reported : load-assert-uid-lookup-during-object-creation-in-transition_t163473
Image
Game Producer & Independent Developer - http://raphaelgervaise.com
B
23
S
9
Posts: 237
Reputation: 2,207

Post » Tue Jan 26, 2016 7:59 am

Hey @Refeuh, i'm still receiving this error on version 216 of C2 and was curious what your workaround exactly was? Specifically the part about delaying the lookup?

Thanks!
Made Cosmochoria - www.cosmochoria.com
Currently working on Slayaway Camp - www.slayawaycamp.com
B
27
S
8
G
3
Posts: 384
Reputation: 5,020

Post » Tue Jan 26, 2016 11:44 pm

No general pattern that worked everywhere tbh, just modifying the logic slightly to demote the dependency, or some kind of lazy init where the entity isn't fully initialised after creation and needs to go through a manual init() process before it gets activated for gameplay.

In some places, I used to do things like (pseudo-code)

class Entity : protected SuspendableIF
{
Entity()
{
SuspendableIF::Suspend(this);
}
}

(calling a factored method from a family acting as interface in the constructor)

I just changed it by duplicating the factored code SuspendableIF::Suspend() in each "sub-class" ; and I stored specialised function names, i.e. (Entity::s_className)&_Suspend(), as member data to make it accessible in Family/Interface
Image
Game Producer & Independent Developer - http://raphaelgervaise.com
B
23
S
9
Posts: 237
Reputation: 2,207

Next

Return to Construct 2 General

Who is online

Users browsing this forum: Artpunk and 6 guests