Improved Object handling while predicting

Added a warning if a networked Object or non-client-side Thinker was erroneously destroyed manually while predicting. This only applies to ZScript since this is the only place it's relevant. Added a warning if a non-client-side Thinker was spawned while predicting. Sweep objects before backing up while predicting to ensure invalid pointers aren't captured.
This commit is contained in:
Boondorl 2025-07-29 15:29:23 -04:00 committed by Ricardo Luís Vaz Silva
commit 1fd267a96a
4 changed files with 24 additions and 2 deletions

View file

@ -333,10 +333,22 @@ void DObject::Destroy ()
GC::WriteBarrier(this);
}
DEFINE_ACTION_FUNCTION(DObject, Destroy)
// This will be here until prediction can be reworked.
// TODO: Fix prediction by serializing instead of using this terrible memcpy method.
bool bPredictionGuard = false;
static void NativeDestroy(DObject* self)
{
if (bPredictionGuard && !(self->ObjectFlags & OF_ClientSide) && ((self->ObjectFlags & OF_Networked) || self->IsKindOf(NAME_Thinker)))
DPrintf(DMSG_WARNING, TEXTCOLOR_RED "Destroyed non-client-side Object %s while predicting\n", self->GetClass()->TypeName.GetChars());
if (!(self->ObjectFlags & OF_EuthanizeMe))
self->Destroy();
}
DEFINE_ACTION_FUNCTION_NATIVE(DObject, Destroy, NativeDestroy)
{
PARAM_SELF_PROLOGUE(DObject);
self->Destroy();
NativeDestroy(self);
return 0;
}

View file

@ -369,6 +369,8 @@ public:
virtual void EnableNetworking(const bool enable);
};
extern bool bPredictionGuard;
// This is the only method aside from calling CreateNew that should be used for creating DObjects
// to ensure that the Class pointer is always set.
template<typename T, typename... Args>

View file

@ -456,6 +456,8 @@ public:
DThinker *CreateThinker(PClass *cls, int statnum = STAT_DEFAULT)
{
if (bPredictionGuard)
DPrintf(DMSG_WARNING, TEXTCOLOR_RED "Spawned non-client-side Thinker %s while predicting\n", cls->TypeName.GetChars());
DThinker *thinker = static_cast<DThinker*>(cls->CreateNew());
assert(thinker->IsKindOf(RUNTIME_CLASS(DThinker)));
thinker->ObjectFlags |= OF_JustSpawned;

View file

@ -1482,6 +1482,10 @@ void P_PredictPlayer (player_t *player)
return;
}
bPredictionGuard = true;
// Avoid memcpying in bad pointers.
GC::CheckGC();
FRandom::SaveRNGState(PredictionRNG);
// Save original values for restoration later
@ -1713,6 +1717,8 @@ void P_UnPredictPlayer ()
actInvSel = InvSel;
player->inventorytics = inventorytics;
bPredictionGuard = false;
}
}