Apparently NOBODY EVER realized why the garbage collector keeps crashing over and over when you reference level actors within the transient package objects (console, windows) or Entry.
Guess what?
Modders still reference level actors in their transient sh*t.
There's a reason UT has a GetPlayerOwner() function for UWindows, but when the time comes and you need to operate quickly on something that isn't the player, or referenced by the player, things get ugly.
In this case, 'Client' variable on the nexgen panels.
What happens...
> Map switch occurs
> Level is forcibly destroyed with all it's actors
> Garbage collector runs, finds references to these actors
> Garbage collector attempts to serialize these actors, instead hits unallocated memory and segfaults.
Valid solutions:
> Set the variable to NONE before map switch.
--- Good luck with this, actors don't receive a 'Destroy()' notification, you'll never find out when level switch occurs from actor code.
> Get NotifyLevelChange notification from Console and propagate the event on the nexgen windows.
--- Basically, you'r building a destructor to every single class you're using, loads of work.
> Disable garbage collection for this variable, add a re-initializator.
---- My approach and the cheapest way... now how do we do this?
Very, very simple.
Code: Select all
var Actor UnsafeReference;
var native Actor SafeReference;
Now the garbage collector won't serialize SafeReference variable, nulling it out when it passes.
Why should we add a re-initializator then?
This:
Code: Select all
SafeReference = Something;
... something calls OBJ GARBAGE mid game lol ...
SafeReference = none;
And boom, this object can no longer interact with 'Something'.
So the reinitializator is an additional characteristic unique to 'Something' you can use to find it again.
Like this:
Code: Select all
var native actor SafeReference;
var int OwnerPlayerId; //Reinitializator
function DoSomething()
{
local actor NewSomething;
if ( SafeReference == none )
ForEach GetPlayerOwner().AllActors (class'Something.class', NewSomething)
if ( Pawn(NewSomething.Owner) != none && Pawn(NewSomething.Owner).PlayerReplicationInfo != none && Pawn(NewSomething.Owner).PlayerReplicationInfo.PlayerID == OwnerPlayerID )
{
SafeReference = NewSomething; //Found 'Something' again post garbage collector.
break;
}
if ( SafeReference == none )
{
//Wait, not found? Level switch or 'Something' was destroyed and garbage collector ran
Deinitialize();
return;
}
DoStuffWith( SafeReference);
}
Congratulations, your code references level actors all over the transient package and will never crash.
This method was recently picked up by ~V~ on his XConsole after a simple bytehack (turning a variable into Native) solved a level switch crash.