Correct practice for clearing actor refs on level change?

Ask about ACS, DECORATE, ZScript, or any other scripting questions here!

Moderator: GZDoom Developers

Forum rules
Before asking on how to use a ZDoom feature, read the ZDoom wiki first. If you still don't understand how to use a feature, then ask here.

Please bear in mind that the people helping you do not automatically know how much you know. You may be asked to upload your project file to look at. Don't be afraid to ask questions about what things mean, but also please be patient with the people trying to help you. (And helpers, please be patient with the person you're trying to help!)
User avatar
JPL
 
 
Posts: 523
Joined: Mon Apr 09, 2012 12:27 pm

Correct practice for clearing actor refs on level change?

Post by JPL »

In my mod, I use an "inventory stack"-like Thinker class to keep track of available items in the current level. When the player exits the level, I want to clear this and avoid any broken references to objects from another level. See this minimal example code and attached PK3 (same as below plus a MapInfo that sets the custom player class). The VM abort can be triggered by opening a level, then exiting to a new level and firing a weapon once.

This could be a bug but I wasn't sure what the intended usage/behavior is, or if I'm doing something in a really unwise way.
Spoiler:
You do not have the required permissions to view the files attached to this post.
Last edited by JPL on Sat Mar 10, 2018 9:15 am, edited 2 times in total.
_mental_
 
 
Posts: 3819
Joined: Sun Aug 07, 2011 4:32 am

Re: Correct (non-crashy) practice for clearing arrays of act

Post by _mental_ »

Before destruction of old player's actor its clone is created in the current level.
This happens after destruction of thinkers and before destruction of old player pawn.
For this reason stack array from TestPlayer objects survives map change.
Although it contains nulls because objects were already destroyed and garbage collected.
Both OnDestroy() overrides are rather pointless, everything will work in the same way without them.
In other words, this is not a problem of dynamic arrays, Clear() or any other method.

And the last but not least. Could you please stop calling VM abort a crash? This is very misleading but continue to happen regularly.
Crash is unhandled exception after which OS has nothing else to do except to terminate guilty process.
When script is trying to access null object, GZDoom handles this gracefully and displays corresponding error(s) in console.
User avatar
JPL
 
 
Posts: 523
Joined: Mon Apr 09, 2012 12:27 pm

Re: Correct practice for clearing actor refs on level change

Post by JPL »

_mental_ wrote:Before destruction of old player's actor its clone is created in the current level.
This happens after destruction of thinkers and before destruction of old player pawn.
For this reason stack array from TestPlayer objects survives map change.
Although it contains nulls because objects were already destroyed and garbage collected.
Both OnDestroy() overrides are rather pointless, everything will work in the same way without them.
In other words, this is not a problem of dynamic arrays, Clear() or any other method.
I wasn't aware it created a copy of the player, is the level load process described anywhere?

I tried replacing the OnDestroy calls with a TestPlayer.BeginPlay override that clears the stacks list, but this didn't seem to change anything.

I added some logging and noticed the flow of operations was this:
(start a new game in MAP01)
TestPlayer.BeginPlay
TestPlayer.PostBeginPlay (stacks are created)
(gameplay begins)
(player fires weapon)
TestPlayer.LogStacks
(player exits level)
(new level MAP02 loads)
TestStacks.OnDestroy (does nothing, I just left in the logging)
TestPlayer.BeginPlay
TestPlayer.BeginPlay
TestPlayer.OnDestroy (does nothing, I just left in the logging)
TestPlayer.OnDestroy
(gameplay begins)
(player fires weapon)
VM Abort during TestPlayer.LogStacks

I can see that a new player is getting created before the old one is destroyed, but why do those two functions run twice? Regardless, I can't figure out why the stacks.Clear() in TestPlayer.BeginPlay doesn't remove the old actor references and prevent the VM abort.
_mental_ wrote:And the last but not least. Could you please stop calling VM abort a crash? This is very misleading but continue to happen regularly.
Crash is unhandled exception after which OS has nothing else to do except to terminate guilty process.
When script is trying to access null object, GZDoom handles this gracefully and displays corresponding error(s) in console.
Okay, I've edited the OP with better language and will use this in the future.
_mental_
 
 
Posts: 3819
Joined: Sun Aug 07, 2011 4:32 am

Re: Correct practice for clearing actor refs on level change

Post by _mental_ »

JPL wrote:I wasn't aware it created a copy of the player, is this process described anywhere?
No, I doubt so. The code responsible for whole process is in G_FinishTravel() function.
JPL wrote:I can see that a new player is getting created before the old one is destroyed, but why do those two functions run twice?
The same function, these lines to be precise.
JPL wrote:I tried replacing the OnDestroy calls with a TestPlayer.BeginPlay override that clears the stacks list, but this didn't seem to change anything.
JPL wrote:Regardless, I can't figure out why the stacks.Clear() in TestPlayer.BeginPlay doesn't remove the old actor references and prevent the VM abort.
In both invocations of TestPlayer.BeginPlay() stack is still empty. Its content (bunch of nulls in our case) is copied after exiting from overridden BeginPlay() method.

Player traveling from map to map is quite weird process indeed. I remember some questions about it and suggestions to improve it.
However simplifying this process is quite problematic. There is mostly no chance that it will be changed to be more comprehensible.
User avatar
JPL
 
 
Posts: 523
Joined: Mon Apr 09, 2012 12:27 pm

Re: Correct practice for clearing actor refs on level change

Post by JPL »

Hmm okay, thanks for the info. I was able to avoid the VM abort by moving the "create stacks" logic out of TestPlayer.PostBeginPlay into its own function that is called during TestPlayer.Tick when level.maptime == 0 (ie the first tick), just after a stacks.Clear(). This runs every map and seems safe so far.

My last question is, is there a method that runs on every map load, after the G_FinishTravel logic has finished, that is most appropriate for this kind of bookkeeping? Running on the first tick works but feels kinda hacky.
_mental_
 
 
Posts: 3819
Joined: Sun Aug 07, 2011 4:32 am

Re: Correct practice for clearing actor refs on level change

Post by _mental_ »

You can override StaticEventHandler.PlayerEntered() method. It will be called right after G_FinishTravel() functions.

Return to “Scripting”