Reported at http://exult.sourceforge.net/forum/read.php?f=1&i=949028&t=949028 .
Exult hangs when leaving the SI dream world.
The immediate cause is Actor::remove() calling unready usecode that already removes the item from the Actor. The following Container_game_object::remove(obj); call then assumes the object is still in the inventory, and continues to corrupt the world state. (We should add asserts to check for this.)
The usecode script running at the time is apparently the tournament script that returns items after returning from the dream world. At some point the black sword gets unequipped from an automaton (unclear why; maybe used as temp. storage), and this triggers the crash above.
As an aside, while this usecode is running, we add the black sword to the last_created stack twice. The second push gets intercepted because we don't allow this, and this causes additional last_created stack problems, on top of the crash above. (Unsure if this is a problem in itself or not.)
We may have to delay calling the unequip script for the black sword in SI (since it is significantly more complex than most unequip scripts), or maybe we shouldn't be calling unequip scripts at all when they're called from usecode. This needs testing in the original engine.
Diff:
Original and Exult saves before swamp and at fire in dreamland.
Trying to take a look at this one. Very hairy though as far as tracking all of the classes/objects that do various remove functions and in some cases call multiple times.
Here's a sample stack trace while exiting SI dream world thru death...was just watching for Actor::remove.
Last edit: Phillip T. George 2016-05-06
Validated that this has something to do with a specific item being unreadied or the unready usecode itself in this context. Does appear to be the black sword to me as well. If I use lldb to return from call_usecode when its being unreadied, then its able to continue on. I think I'll be able to figure this out eventually. Assigning myself.
Last edit: Phillip T. George 2016-05-06
I believe I have this fixed. Actor::remove has a check to see if the Actor is dead...if it is dead, it will not run "call_readied_usecode" which unreadies equiped items. However, when dying in the SI dream world, the avatar is NOT showing as dead. Anyway, the fix was to check and see if a "died" usecode event running (only if playing SI) for the main actor. If I tried just checking for the NPC's death in queue, but that doesn't make sense as part of the problem seems to be the automation NPC unreadying the weapon during the SI dream world death. I tested to make sure this does not impact normal death, and it should not, since the "is_dead" flag is set anyway, that part of the code is not ran. Also checked SI training and it still works...so I think we're good. I'll let someone else verify and close this.
Last edit: Phillip T. George 2016-05-09
thanks for looking into it.
This fix works for me. I asked Marzo in irc, and he thinks the fix is fine.
Marzo: The original had a hack for the black sword: its unready event was done by way of a script instead of directly
This can be seen by opening a gump and removing the black sword from inventory: it only returns when you close the gump
So the "correct" solution would be to add a special case for the black sword
His fix is simpler; if it works, I have no problems with it
Malignant_Manor: Do you foresee any issues?
Marzo: No; but Exult is a complex enough system that it is impossible to be sure
Great -- glad to hear.