Clear references to map data on level change
These shouldn't be left as they'll now point towards potentially invalid memory and also cause errors with serializing. Arrays and maps holding them are cleared. Also unlinks and relinks inventory items correctly from the hashmap on traveling.
This commit is contained in:
parent
29b2253bdb
commit
269689703d
6 changed files with 87 additions and 3 deletions
|
|
@ -417,6 +417,67 @@ size_t DObject::PropagateMark()
|
|||
//
|
||||
//==========================================================================
|
||||
|
||||
void DObject::ClearNativePointerFields(const TArray<FName>& types)
|
||||
{
|
||||
auto cls = GetClass();
|
||||
if (cls->VMType == nullptr)
|
||||
return;
|
||||
|
||||
auto it = cls->VMType->Symbols.GetIterator();
|
||||
TMap<FName, PSymbol*>::Pair* sym = nullptr;
|
||||
while (it.NextPair(sym))
|
||||
{
|
||||
auto field = dyn_cast<PField>(sym->Value);
|
||||
if (field == nullptr)
|
||||
continue;
|
||||
|
||||
PType* base = field->Type;
|
||||
PType* t = base;
|
||||
if (base->isArray() && !base->isStaticArray())
|
||||
t = static_cast<PArray*>(base)->ElementType;
|
||||
else if (base->isDynArray())
|
||||
t = static_cast<PDynArray*>(base)->ElementType;
|
||||
else if (base->isMap())
|
||||
t = static_cast<PMap*>(base)->ValueType;
|
||||
|
||||
if (!t->isRealPointer())
|
||||
continue;
|
||||
|
||||
auto pType = static_cast<PPointer*>(t)->PointedType;
|
||||
if (!pType->isStruct() || !static_cast<PStruct*>(pType)->isNative || types.Find(static_cast<PStruct*>(pType)->TypeName) >= types.Size())
|
||||
continue;
|
||||
|
||||
if (base->isArray() && !base->isStaticArray())
|
||||
{
|
||||
auto arr = (void**)ScriptVar(sym->Key, nullptr);
|
||||
const size_t count = static_cast<PArray*>(base)->ElementCount;
|
||||
for (size_t i = 0u; i < count; ++i)
|
||||
arr[i] = nullptr;
|
||||
}
|
||||
else if (base->isDynArray())
|
||||
{
|
||||
static_cast<TArray<void*>*>(ScriptVar(sym->Key, nullptr))->Clear();
|
||||
}
|
||||
else if (base->isMap())
|
||||
{
|
||||
if (static_cast<PMap*>(base)->BackingClass == PMap::MAP_I32_PTR)
|
||||
static_cast<ZSMap<int, void*>*>(ScriptVar(sym->Key, nullptr))->Clear();
|
||||
else
|
||||
static_cast<ZSMap<FString, void*>*>(ScriptVar(sym->Key, nullptr))->Clear();
|
||||
}
|
||||
else
|
||||
{
|
||||
PointerVar<void>(sym->Key) = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//==========================================================================
|
||||
//
|
||||
//
|
||||
//
|
||||
//==========================================================================
|
||||
|
||||
template<typename M>
|
||||
static void MapPointerSubstitution(M *map, size_t &changed, DObject *old, DObject *notOld, const bool shouldSwap)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -245,6 +245,9 @@ public:
|
|||
template<class T> T*& PointerVar(FName field);
|
||||
inline int* IntArray(FName field);
|
||||
|
||||
// Make sure native data is wiped correctly since it has no read barriers.
|
||||
void ClearNativePointerFields(const TArray<FName>& types);
|
||||
|
||||
// This is only needed for swapping out PlayerPawns and absolutely nothing else!
|
||||
virtual size_t PointerSubstitution (DObject *old, DObject *notOld, bool nullOnFail);
|
||||
|
||||
|
|
|
|||
|
|
@ -234,8 +234,8 @@ public:
|
|||
|
||||
TArray (std::initializer_list<T> list)
|
||||
{
|
||||
Most = list.size;
|
||||
Count = list.size;
|
||||
Most = list.size();
|
||||
Count = list.size();
|
||||
|
||||
if (Count > 0)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1646,6 +1646,9 @@ void FLevelLocals::StartTravel ()
|
|||
inv->UnlinkFromWorld (nullptr);
|
||||
inv->UnlinkBehaviorsFromLevel();
|
||||
inv->DeleteAttachedLights();
|
||||
tid = inv->tid;
|
||||
inv->SetTID(0);
|
||||
inv->tid = tid;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1751,7 +1754,7 @@ int FLevelLocals::FinishTravel ()
|
|||
pawn->LinkBehaviorsToLevel();
|
||||
pawn->ClearInterpolation();
|
||||
pawn->ClearFOVInterpolation();
|
||||
const int tid = pawn->tid; // Save TID (actor isn't linked into the hash chain yet)
|
||||
int tid = pawn->tid; // Save TID (actor isn't linked into the hash chain yet)
|
||||
pawn->tid = 0; // Reset TID
|
||||
pawn->SetTID(tid); // Set TID (and link actor into the hash chain)
|
||||
pawn->SetState(pawn->SpawnState);
|
||||
|
|
@ -1763,6 +1766,9 @@ int FLevelLocals::FinishTravel ()
|
|||
inv->LinkToWorld (nullptr);
|
||||
P_FindFloorCeiling(inv, FFCF_ONLYSPAWNPOS);
|
||||
inv->LinkBehaviorsToLevel();
|
||||
tid = inv->tid;
|
||||
inv->tid = 0;
|
||||
inv->SetTID(tid);
|
||||
|
||||
IFVIRTUALPTRNAME(inv, NAME_Inventory, Travelled)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -568,6 +568,13 @@ xx(Offsety)
|
|||
xx(Texturetop)
|
||||
xx(Texturebottom)
|
||||
xx(Texturemiddle)
|
||||
xx(SectorPortal)
|
||||
xx(LinePortal)
|
||||
xx(Vertex)
|
||||
xx(Side)
|
||||
xx(Line)
|
||||
xx(SecPlane)
|
||||
xx(F3DFloor)
|
||||
xx(Sector)
|
||||
xx(Heightfloor)
|
||||
xx(Heightceiling)
|
||||
|
|
|
|||
|
|
@ -293,9 +293,16 @@ void FLevelLocals::ClearLevelData(bool fullgc)
|
|||
auto it = GetThinkerIterator<AActor>(NAME_None, STAT_TRAVELLING);
|
||||
for (AActor *actor = it.Next(); actor != nullptr; actor = it.Next())
|
||||
{
|
||||
actor->BlockingMobj = nullptr;
|
||||
actor->BlockingLine = actor->MovementBlockingLine = nullptr;
|
||||
actor->BlockingFloor = actor->BlockingCeiling = actor->Blocking3DFloor = nullptr;
|
||||
}
|
||||
|
||||
// Make sure map data gets cleared appropriately so any leftover Objects aren't pointing
|
||||
// towards anything invalid.
|
||||
TArray<FName> fieldTypes = { NAME_SectorPortal, NAME_LinePortal, NAME_Vertex, NAME_Side, NAME_Line, NAME_SecPlane, NAME_F3DFloor, NAME_Sector };
|
||||
for (DObject* probe = GC::Root; probe != nullptr; probe = probe->ObjNext)
|
||||
probe->ClearNativePointerFields(fieldTypes);
|
||||
}
|
||||
|
||||
interpolator.ClearInterpolations(); // [RH] Nothing to interpolate on a fresh level.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue