- added a pseudo-serializer for FLevelLocals pointers.

This doesn't really write out any info for the pointer, if the level does not match it just errors out.
This is both for quick detection of badly used level data and for automatic restoring of the pointer from the serializer's working level.

This also removed the temporary workarounds in DAutomap and DLevelScript to restore these pointers when a savegame is loaded.
This commit is contained in:
Christoph Oelckers 2019-01-26 21:23:19 +01:00
parent e5139cc325
commit 9b1b6db85d
12 changed files with 152 additions and 90 deletions

View file

@ -3205,7 +3205,8 @@ void DAutomap::Serialize(FSerializer &arc)
("max_h", max_h) ("max_h", max_h)
("min_scale_mtof", min_scale_mtof) ("min_scale_mtof", min_scale_mtof)
("max_scale_mtof", max_scale_mtof) ("max_scale_mtof", max_scale_mtof)
("mapback", mapback); ("mapback", mapback)
("level", Level);
} }

View file

@ -1811,7 +1811,7 @@ void G_DoLoadGame ()
SaveVersion = 0; SaveVersion = 0;
void *data = info->CacheLump(); void *data = info->CacheLump();
FSerializer arc; FSerializer arc(nullptr);
if (!arc.OpenReader((const char *)data, info->LumpSize)) if (!arc.OpenReader((const char *)data, info->LumpSize))
{ {
Printf("Failed to access savegame info\n"); Printf("Failed to access savegame info\n");
@ -2129,7 +2129,7 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio
insave = true; insave = true;
try try
{ {
G_SnapshotLevel(); level.SnapshotLevel();
} }
catch(CRecoverableError &err) catch(CRecoverableError &err)
{ {
@ -2152,8 +2152,8 @@ void G_DoSaveGame (bool okForQuicksave, FString filename, const char *descriptio
} }
BufferWriter savepic; BufferWriter savepic;
FSerializer savegameinfo; // this is for displayable info about the savegame FSerializer savegameinfo(nullptr); // this is for displayable info about the savegame
FSerializer savegameglobals; // and this for non-level related info that must be saved. FSerializer savegameglobals(nullptr); // and this for non-level related info that must be saved.
savegameinfo.OpenWriter(true); savegameinfo.OpenWriter(true);
savegameglobals.OpenWriter(save_formatted); savegameglobals.OpenWriter(save_formatted);

View file

@ -856,7 +856,7 @@ void G_DoCompleted (void)
{ // Remember the level's state for re-entry. { // Remember the level's state for re-entry.
if (!(level.flags2 & LEVEL2_FORGETSTATE)) if (!(level.flags2 & LEVEL2_FORGETSTATE))
{ {
G_SnapshotLevel (); level.SnapshotLevel ();
// Do not free any global strings this level might reference // Do not free any global strings this level might reference
// while it's not loaded. // while it's not loaded.
level.Behaviors.LockLevelVarStrings(level.levelnum); level.Behaviors.LockLevelVarStrings(level.levelnum);
@ -1060,7 +1060,7 @@ void G_DoLoadLevel (int position, bool autosave, bool newGame)
level.starttime = gametic; level.starttime = gametic;
G_UnSnapshotLevel (!savegamerestore); // [RH] Restore the state of the level. level.UnSnapshotLevel (!savegamerestore); // [RH] Restore the state of the level.
int pnumerr = G_FinishTravel (); int pnumerr = G_FinishTravel ();
if (!level.FromSnapshot) if (!level.FromSnapshot)
@ -1109,7 +1109,7 @@ void G_DoLoadLevel (int position, bool autosave, bool newGame)
E_WorldLoadedUnsafe(); E_WorldLoadedUnsafe();
// regular world load (savegames are handled internally) // regular world load (savegames are handled internally)
E_WorldLoaded(); E_WorldLoaded();
P_DoDeferedScripts (); // [RH] Do script actions that were triggered on another map. level.DoDeferedScripts (); // [RH] Do script actions that were triggered on another map.
if (demoplayback || oldgs == GS_STARTUP || oldgs == GS_TITLELEVEL) if (demoplayback || oldgs == GS_STARTUP || oldgs == GS_TITLELEVEL)
C_HideConsole (); C_HideConsole ();
@ -1647,19 +1647,19 @@ void G_AirControlChanged ()
// //
//========================================================================== //==========================================================================
void G_SnapshotLevel () void FLevelLocals::SnapshotLevel ()
{ {
level.info->Snapshot.Clean(); info->Snapshot.Clean();
if (level.info->isValid()) if (info->isValid())
{ {
FSerializer arc; FSerializer arc(this);
if (arc.OpenWriter(save_formatted)) if (arc.OpenWriter(save_formatted))
{ {
SaveVersion = SAVEVER; SaveVersion = SAVEVER;
level.Serialize(arc, false); Serialize(arc, false);
level.info->Snapshot = arc.GetCompressedOutput(); info->Snapshot = arc.GetCompressedOutput();
} }
} }
} }
@ -1671,24 +1671,24 @@ void G_SnapshotLevel ()
// //
//========================================================================== //==========================================================================
void G_UnSnapshotLevel (bool hubLoad) void FLevelLocals::UnSnapshotLevel (bool hubLoad)
{ {
if (level.info->Snapshot.mBuffer == nullptr) if (info->Snapshot.mBuffer == nullptr)
return; return;
if (level.info->isValid()) if (info->isValid())
{ {
FSerializer arc; FSerializer arc(this);
if (!arc.OpenReader(&level.info->Snapshot)) if (!arc.OpenReader(&info->Snapshot))
{ {
I_Error("Failed to load savegame"); I_Error("Failed to load savegame");
return; return;
} }
level.Serialize (arc, hubLoad); Serialize (arc, hubLoad);
level.FromSnapshot = true; FromSnapshot = true;
TThinkerIterator<AActor> it(NAME_PlayerPawn); auto it = GetThinkerIterator<AActor>(NAME_PlayerPawn);
AActor *pawn, *next; AActor *pawn, *next;
next = it.Next(); next = it.Next();
@ -1716,11 +1716,11 @@ void G_UnSnapshotLevel (bool hubLoad)
arc.Close(); arc.Close();
} }
// No reason to keep the snapshot around once the level's been entered. // No reason to keep the snapshot around once the level's been entered.
level.info->Snapshot.Clean(); info->Snapshot.Clean();
if (hubLoad) if (hubLoad)
{ {
// Unlock ACS global strings that were locked when the snapshot was made. // Unlock ACS global strings that were locked when the snapshot was made.
level.Behaviors.UnlockLevelVarStrings(level.levelnum); Behaviors.UnlockLevelVarStrings(level.levelnum);
} }
} }

View file

@ -490,8 +490,6 @@ void G_ParseMapInfo (FString basemapinfo);
void G_ClearSnapshots (void); void G_ClearSnapshots (void);
void P_RemoveDefereds (); void P_RemoveDefereds ();
void G_SnapshotLevel (void);
void G_UnSnapshotLevel (bool keepPlayers);
void G_ReadSnapshots (FResourceFile *); void G_ReadSnapshots (FResourceFile *);
void G_WriteSnapshots (TArray<FString> &, TArray<FCompressedBuffer> &); void G_WriteSnapshots (TArray<FString> &, TArray<FCompressedBuffer> &);
void G_WriteVisited(FSerializer &arc); void G_WriteVisited(FSerializer &arc);

View file

@ -186,6 +186,9 @@ private:
void AddDisplacementForPortal(FLinePortal *portal); void AddDisplacementForPortal(FLinePortal *portal);
bool ConnectPortalGroups(); bool ConnectPortalGroups();
public: public:
void SnapshotLevel();
void UnSnapshotLevel(bool hubLoad);
void FinalizePortals(); void FinalizePortals();
bool ChangePortal(line_t *ln, int thisid, int destid); bool ChangePortal(line_t *ln, int thisid, int destid);
unsigned GetSkyboxPortal(AActor *actor); unsigned GetSkyboxPortal(AActor *actor);
@ -198,6 +201,7 @@ public:
bool CreateCeiling(sector_t *sec, DCeiling::ECeiling type, line_t *line, int tag, double speed, double speed2, double height, int crush, int silent, int change, DCeiling::ECrushMode hexencrush); bool CreateCeiling(sector_t *sec, DCeiling::ECeiling type, line_t *line, int tag, double speed, double speed2, double height, int crush, int silent, int change, DCeiling::ECrushMode hexencrush);
void ActivateInStasisCeiling(int tag); void ActivateInStasisCeiling(int tag);
bool CreateFloor(sector_t *sec, DFloor::EFloor floortype, line_t *line, double speed, double height, int crush, int change, bool hexencrush, bool hereticlower); bool CreateFloor(sector_t *sec, DFloor::EFloor floortype, line_t *line, double speed, double height, int crush, int change, bool hexencrush, bool hereticlower);
void DoDeferedScripts();
bool EV_DoPlat(int tag, line_t *line, DPlat::EPlatType type, double height, double speed, int delay, int lip, int change); bool EV_DoPlat(int tag, line_t *line, DPlat::EPlatType type, double height, double speed, int delay, int lip, int change);
void EV_StopPlat(int tag, bool remove); void EV_StopPlat(int tag, bool remove);

View file

@ -180,7 +180,7 @@ void FSavegameManager::ReadSaveStrings()
continue; continue;
} }
void *data = info->CacheLump(); void *data = info->CacheLump();
FSerializer arc; FSerializer arc(nullptr);
if (arc.OpenReader((const char *)data, info->LumpSize)) if (arc.OpenReader((const char *)data, info->LumpSize))
{ {
int savever = 0; int savever = 0;
@ -469,7 +469,7 @@ unsigned FSavegameManager::ExtractSaveData(int index)
return index; return index;
} }
void *data = info->CacheLump(); void *data = info->CacheLump();
FSerializer arc; FSerializer arc(nullptr);
if (arc.OpenReader((const char *)data, info->LumpSize)) if (arc.OpenReader((const char *)data, info->LumpSize))
{ {
FString time, pcomment, comment; FString time, pcomment, comment;

View file

@ -671,9 +671,8 @@ public:
SCRIPT_ModulusBy0, SCRIPT_ModulusBy0,
}; };
DLevelScript(AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module, DLevelScript(FLevelLocals *l, AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module,
const int *args, int argcount, int flags); const int *args, int argcount, int flags);
~DLevelScript();
void Serialize(FSerializer &arc); void Serialize(FSerializer &arc);
int RunScript(); int RunScript();
@ -780,7 +779,7 @@ private:
friend class DACSThinker; friend class DACSThinker;
}; };
static DLevelScript *P_GetScriptGoing (AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module, static DLevelScript *P_GetScriptGoing (FLevelLocals *Level, AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module,
const int *args, int argcount, int flags); const int *args, int argcount, int flags);
@ -3264,11 +3263,10 @@ void FBehavior::StartTypedScripts (uint16_t type, AActor *activator, bool always
ptr = &Scripts[i]; ptr = &Scripts[i];
if (ptr->Type == type) if (ptr->Type == type)
{ {
DLevelScript *runningScript = P_GetScriptGoing (activator, NULL, ptr->Number, DLevelScript *runningScript = P_GetScriptGoing (&level, activator, NULL, ptr->Number,
ptr, this, &arg1, 1, always ? ACS_ALWAYS : 0); ptr, this, &arg1, 1, always ? ACS_ALWAYS : 0);
if (nullptr != runningScript && runNow) if (nullptr != runningScript && runNow)
{ {
runningScript->Level = &level;
runningScript->RunScript(); runningScript->RunScript();
} }
} }
@ -3389,7 +3387,6 @@ void DACSThinker::Tick ()
while (script) while (script)
{ {
DLevelScript *next = script->next; DLevelScript *next = script->next;
script->Level = &level;
script->RunScript(); script->RunScript();
script = next; script = next;
} }
@ -3460,11 +3457,12 @@ void DLevelScript::Serialize(FSerializer &arc)
("cliprectwidth", ClipRectWidth) ("cliprectwidth", ClipRectWidth)
("cliprectheight", ClipRectHeight) ("cliprectheight", ClipRectHeight)
("wrapwidth", WrapWidth) ("wrapwidth", WrapWidth)
("inmodulescriptnum", InModuleScriptNumber); ("inmodulescriptnum", InModuleScriptNumber)
("level", Level);
if (arc.isReading()) if (arc.isReading())
{ {
activeBehavior = level.Behaviors.GetModule(lib); activeBehavior = Level->Behaviors.GetModule(lib);
if (nullptr == activeBehavior) if (nullptr == activeBehavior)
{ {
@ -3477,19 +3475,12 @@ void DLevelScript::Serialize(FSerializer &arc)
DLevelScript::DLevelScript () DLevelScript::DLevelScript ()
{ {
next = prev = nullptr;
if (level.ACSThinker == nullptr)
level.ACSThinker = Create<DACSThinker>();
activefont = SmallFont; activefont = SmallFont;
} }
DLevelScript::~DLevelScript ()
{
}
void DLevelScript::Unlink () void DLevelScript::Unlink ()
{ {
DACSThinker *controller = level.ACSThinker; DACSThinker *controller = Level->ACSThinker;
if (controller->LastScript == this) if (controller->LastScript == this)
{ {
@ -3515,7 +3506,7 @@ void DLevelScript::Unlink ()
void DLevelScript::Link () void DLevelScript::Link ()
{ {
DACSThinker *controller = level.ACSThinker; DACSThinker *controller = Level->ACSThinker;
next = controller->Scripts; next = controller->Scripts;
GC::WriteBarrier(this, next); GC::WriteBarrier(this, next);
@ -3535,7 +3526,7 @@ void DLevelScript::Link ()
void DLevelScript::PutLast () void DLevelScript::PutLast ()
{ {
DACSThinker *controller = level.ACSThinker; DACSThinker *controller = Level->ACSThinker;
if (controller->LastScript == this) if (controller->LastScript == this)
return; return;
@ -3557,7 +3548,7 @@ void DLevelScript::PutLast ()
void DLevelScript::PutFirst () void DLevelScript::PutFirst ()
{ {
DACSThinker *controller = level.ACSThinker; DACSThinker *controller = Level->ACSThinker;
if (controller->Scripts == this) if (controller->Scripts == this)
return; return;
@ -10218,10 +10209,10 @@ scriptwait:
#undef PushtoStack #undef PushtoStack
static DLevelScript *P_GetScriptGoing (AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module, static DLevelScript *P_GetScriptGoing (FLevelLocals *l, AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module,
const int *args, int argcount, int flags) const int *args, int argcount, int flags)
{ {
DACSThinker *controller = level.ACSThinker; DACSThinker *controller = l->ACSThinker;
DLevelScript **running; DLevelScript **running;
if (controller && !(flags & ACS_ALWAYS) && (running = controller->RunningScripts.CheckKey(num)) != NULL) if (controller && !(flags & ACS_ALWAYS) && (running = controller->RunningScripts.CheckKey(num)) != NULL)
@ -10234,15 +10225,16 @@ static DLevelScript *P_GetScriptGoing (AActor *who, line_t *where, int num, cons
return NULL; return NULL;
} }
return Create<DLevelScript> (who, where, num, code, module, args, argcount, flags); return Create<DLevelScript> (l, who, where, num, code, module, args, argcount, flags);
} }
DLevelScript::DLevelScript (AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module, DLevelScript::DLevelScript (FLevelLocals *l, AActor *who, line_t *where, int num, const ScriptPtr *code, FBehavior *module,
const int *args, int argcount, int flags) const int *args, int argcount, int flags)
: activeBehavior (module) : activeBehavior (module)
{ {
if (level.ACSThinker == nullptr) Level = l;
level.ACSThinker = Create<DACSThinker>(); if (Level->ACSThinker == nullptr)
Level->ACSThinker = Create<DACSThinker>();
script = num; script = num;
assert(code->VarCount >= code->ArgCount); assert(code->VarCount >= code->ArgCount);
@ -10270,11 +10262,11 @@ DLevelScript::DLevelScript (AActor *who, line_t *where, int num, const ScriptPtr
// goes by while they're in their default state. // goes by while they're in their default state.
if (!(flags & ACS_ALWAYS)) if (!(flags & ACS_ALWAYS))
level.ACSThinker->RunningScripts[num] = this; Level->ACSThinker->RunningScripts[num] = this;
Link(); Link();
if (level.flags2 & LEVEL2_HEXENHACK) if (Level->flags2 & LEVEL2_HEXENHACK)
{ {
PutLast(); PutLast();
} }
@ -10282,9 +10274,8 @@ DLevelScript::DLevelScript (AActor *who, line_t *where, int num, const ScriptPtr
DPrintf(DMSG_SPAMMY, "%s started.\n", ScriptPresentation(num).GetChars()); DPrintf(DMSG_SPAMMY, "%s started.\n", ScriptPresentation(num).GetChars());
} }
static void SetScriptState (int script, DLevelScript::EScriptState state) void SetScriptState (DACSThinker *controller, int script, DLevelScript::EScriptState state)
{ {
DACSThinker *controller = level.ACSThinker;
DLevelScript **running; DLevelScript **running;
if (controller != NULL && (running = controller->RunningScripts.CheckKey(script)) != NULL) if (controller != NULL && (running = controller->RunningScripts.CheckKey(script)) != NULL)
@ -10293,23 +10284,23 @@ static void SetScriptState (int script, DLevelScript::EScriptState state)
} }
} }
void P_DoDeferedScripts () void FLevelLocals::DoDeferedScripts ()
{ {
const ScriptPtr *scriptdata; const ScriptPtr *scriptdata;
FBehavior *module; FBehavior *module;
// Handle defered scripts in this step, too // Handle defered scripts in this step, too
for(int i = level.info->deferred.Size()-1; i>=0; i--) for(int i = info->deferred.Size()-1; i>=0; i--)
{ {
acsdefered_t *def = &level.info->deferred[i]; acsdefered_t *def = &info->deferred[i];
switch (def->type) switch (def->type)
{ {
case acsdefered_t::defexecute: case acsdefered_t::defexecute:
case acsdefered_t::defexealways: case acsdefered_t::defexealways:
scriptdata = level.Behaviors.FindScript (def->script, module); scriptdata = Behaviors.FindScript (def->script, module);
if (scriptdata) if (scriptdata)
{ {
P_GetScriptGoing ((unsigned)def->playernum < MAXPLAYERS && P_GetScriptGoing (this, (unsigned)def->playernum < MAXPLAYERS &&
playeringame[def->playernum] ? players[def->playernum].mo : NULL, playeringame[def->playernum] ? players[def->playernum].mo : NULL,
NULL, def->script, NULL, def->script,
scriptdata, module, scriptdata, module,
@ -10323,17 +10314,17 @@ void P_DoDeferedScripts ()
break; break;
case acsdefered_t::defsuspend: case acsdefered_t::defsuspend:
SetScriptState (def->script, DLevelScript::SCRIPT_Suspended); SetScriptState (ACSThinker, def->script, DLevelScript::SCRIPT_Suspended);
DPrintf (DMSG_SPAMMY, "Deferred suspend of %s\n", ScriptPresentation(def->script).GetChars()); DPrintf (DMSG_SPAMMY, "Deferred suspend of %s\n", ScriptPresentation(def->script).GetChars());
break; break;
case acsdefered_t::defterminate: case acsdefered_t::defterminate:
SetScriptState (def->script, DLevelScript::SCRIPT_PleaseRemove); SetScriptState (ACSThinker, def->script, DLevelScript::SCRIPT_PleaseRemove);
DPrintf (DMSG_SPAMMY, "Deferred terminate of %s\n", ScriptPresentation(def->script).GetChars()); DPrintf (DMSG_SPAMMY, "Deferred terminate of %s\n", ScriptPresentation(def->script).GetChars());
break; break;
} }
} }
level.info->deferred.Clear(); info->deferred.Clear();
} }
static void addDefered (level_info_t *i, acsdefered_t::EType type, int script, const int *args, int argcount, AActor *who) static void addDefered (level_info_t *i, acsdefered_t::EType type, int script, const int *args, int argcount, AActor *who)
@ -10392,13 +10383,12 @@ int P_StartScript (AActor *who, line_t *where, int script, const char *map, cons
return false; return false;
} }
} }
DLevelScript *runningScript = P_GetScriptGoing (who, where, script, DLevelScript *runningScript = P_GetScriptGoing (&level, who, where, script,
scriptdata, module, args, argcount, flags); scriptdata, module, args, argcount, flags);
if (runningScript != NULL) if (runningScript != NULL)
{ {
if (flags & ACS_WANTRESULT) if (flags & ACS_WANTRESULT)
{ {
runningScript->Level = &level;
return runningScript->RunScript(); return runningScript->RunScript();
} }
return true; return true;
@ -10428,7 +10418,7 @@ void P_SuspendScript (int script, const char *map)
if (strnicmp (level.MapName, map, 8)) if (strnicmp (level.MapName, map, 8))
addDefered (FindLevelInfo (map), acsdefered_t::defsuspend, script, NULL, 0, NULL); addDefered (FindLevelInfo (map), acsdefered_t::defsuspend, script, NULL, 0, NULL);
else else
SetScriptState (script, DLevelScript::SCRIPT_Suspended); SetScriptState (level.ACSThinker, script, DLevelScript::SCRIPT_Suspended);
} }
void P_TerminateScript (int script, const char *map) void P_TerminateScript (int script, const char *map)
@ -10436,7 +10426,7 @@ void P_TerminateScript (int script, const char *map)
if (strnicmp (level.MapName, map, 8)) if (strnicmp (level.MapName, map, 8))
addDefered (FindLevelInfo (map), acsdefered_t::defterminate, script, NULL, 0, NULL); addDefered (FindLevelInfo (map), acsdefered_t::defterminate, script, NULL, 0, NULL);
else else
SetScriptState (script, DLevelScript::SCRIPT_PleaseRemove); SetScriptState (level.ACSThinker, script, DLevelScript::SCRIPT_PleaseRemove);
} }
FSerializer &Serialize(FSerializer &arc, const char *key, acsdefered_t &defer, acsdefered_t *def) FSerializer &Serialize(FSerializer &arc, const char *key, acsdefered_t &defer, acsdefered_t *def)
@ -10515,11 +10505,11 @@ void ACSProfileInfo::AddRun(unsigned int num_instr)
} }
} }
void ArrangeScriptProfiles(TArray<ProfileCollector> &profiles) void FBehaviorContainer::ArrangeScriptProfiles(TArray<ProfileCollector> &profiles)
{ {
for (unsigned int mod_num = 0; mod_num < level.Behaviors.StaticModules.Size(); ++mod_num) for (unsigned int mod_num = 0; mod_num < StaticModules.Size(); ++mod_num)
{ {
FBehavior *module = level.Behaviors.StaticModules[mod_num]; FBehavior *module = StaticModules[mod_num];
ProfileCollector prof; ProfileCollector prof;
prof.Module = module; prof.Module = module;
for (int i = 0; i < module->NumScripts; ++i) for (int i = 0; i < module->NumScripts; ++i)
@ -10531,11 +10521,11 @@ void ArrangeScriptProfiles(TArray<ProfileCollector> &profiles)
} }
} }
void ArrangeFunctionProfiles(TArray<ProfileCollector> &profiles) void FBehaviorContainer::ArrangeFunctionProfiles(TArray<ProfileCollector> &profiles)
{ {
for (unsigned int mod_num = 0; mod_num < level.Behaviors.StaticModules.Size(); ++mod_num) for (unsigned int mod_num = 0; mod_num < StaticModules.Size(); ++mod_num)
{ {
FBehavior *module = level.Behaviors.StaticModules[mod_num]; FBehavior *module = StaticModules[mod_num];
ProfileCollector prof; ProfileCollector prof;
prof.Module = module; prof.Module = module;
for (int i = 0; i < module->NumFunctions; ++i) for (int i = 0; i < module->NumFunctions; ++i)
@ -10692,8 +10682,8 @@ CCMD(acsprofile)
assert(countof(sort_names) == countof(sort_match_len)); assert(countof(sort_names) == countof(sort_match_len));
ArrangeScriptProfiles(ScriptProfiles); level.Behaviors.ArrangeScriptProfiles(ScriptProfiles);
ArrangeFunctionProfiles(FuncProfiles); level.Behaviors.ArrangeFunctionProfiles(FuncProfiles);
if (argv.argc() > 1) if (argv.argc() > 1)
{ {

View file

@ -420,8 +420,6 @@ private:
void MarkMapVarStrings() const; void MarkMapVarStrings() const;
void LockMapVarStrings(int levelnum) const; void LockMapVarStrings(int levelnum) const;
friend void ArrangeScriptProfiles(TArray<ProfileCollector> &profiles);
friend void ArrangeFunctionProfiles(TArray<ProfileCollector> &profiles);
friend struct FBehaviorContainer; friend struct FBehaviorContainer;
}; };
@ -443,6 +441,8 @@ struct FBehaviorContainer
const char *LookupString(uint32_t index); const char *LookupString(uint32_t index);
void StartTypedScripts(uint16_t type, AActor *activator, bool always, int arg1 = 0, bool runNow = false); void StartTypedScripts(uint16_t type, AActor *activator, bool always, int arg1 = 0, bool runNow = false);
void StopMyScripts(AActor *actor); void StopMyScripts(AActor *actor);
void ArrangeScriptProfiles(TArray<ProfileCollector> &profiles);
void ArrangeFunctionProfiles(TArray<ProfileCollector> &profiles);
}; };

View file

@ -1023,7 +1023,6 @@ void FLevelLocals::Serialize(FSerializer &arc, bool hubload)
AActor::RecreateAllAttachedLights(); AActor::RecreateAllAttachedLights();
InitPortalGroups(this); InitPortalGroups(this);
automap->Level = this; // Temporary workaround. At the moment this cannot be deserialized yet.
automap->UpdateShowAllLines(); automap->UpdateShowAllLines();
} }

View file

@ -630,7 +630,6 @@ bool EV_TeleportSector (int tag, int source_tid, int dest_tid, bool fog, int gro
int P_StartScript (AActor *who, line_t *where, int script, const char *map, const int *args, int argcount, int flags); int P_StartScript (AActor *who, line_t *where, int script, const char *map, const int *args, int argcount, int flags);
void P_SuspendScript (int script, const char *map); void P_SuspendScript (int script, const char *map);
void P_TerminateScript (int script, const char *map); void P_TerminateScript (int script, const char *map);
void P_DoDeferedScripts (void);
// //
// [RH] p_quake.c // [RH] p_quake.c

View file

@ -1450,27 +1450,32 @@ FSerializer &SerializePointer(FSerializer &arc, const char *key, T *&value, T **
template<> FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&value, FPolyObj **defval) template<> FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj *&value, FPolyObj **defval)
{ {
return SerializePointer(arc, key, value, defval, level.Polyobjects.Data()); if (arc.Level == nullptr) I_Error("Trying to serialize polyobject without a valid level");
return SerializePointer(arc, key, value, defval, arc.Level->Polyobjects.Data());
} }
template<> FSerializer &Serialize(FSerializer &arc, const char *key, const FPolyObj *&value, const FPolyObj **defval) template<> FSerializer &Serialize(FSerializer &arc, const char *key, const FPolyObj *&value, const FPolyObj **defval)
{ {
return SerializePointer<const FPolyObj>(arc, key, value, defval, level.Polyobjects.Data()); if (arc.Level == nullptr) I_Error("Trying to serialize polyobject without a valid level");
return SerializePointer<const FPolyObj>(arc, key, value, defval, arc.Level->Polyobjects.Data());
} }
template<> FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value, side_t **defval) template<> FSerializer &Serialize(FSerializer &arc, const char *key, side_t *&value, side_t **defval)
{ {
return SerializePointer(arc, key, value, defval, &level.sides[0]); if (arc.Level == nullptr) I_Error("Trying to serialize SIDEDEF without a valid level");
return SerializePointer(arc, key, value, defval, &arc.Level->sides[0]);
} }
template<> FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval) template<> FSerializer &Serialize(FSerializer &arc, const char *key, sector_t *&value, sector_t **defval)
{ {
return SerializePointer(arc, key, value, defval, &level.sectors[0]); if (arc.Level == nullptr) I_Error("Trying to serialize sector without a valid level");
return SerializePointer(arc, key, value, defval, &arc.Level->sectors[0]);
} }
template<> FSerializer &Serialize(FSerializer &arc, const char *key, const sector_t *&value, const sector_t **defval) template<> FSerializer &Serialize(FSerializer &arc, const char *key, const sector_t *&value, const sector_t **defval)
{ {
return SerializePointer<const sector_t>(arc, key, value, defval, &level.sectors[0]); if (arc.Level == nullptr) I_Error("Trying to serialize sector without a valid level");
return SerializePointer<const sector_t>(arc, key, value, defval, &arc.Level->sectors[0]);
} }
template<> FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value, player_t **defval) template<> FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&value, player_t **defval)
@ -1480,12 +1485,14 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, player_t *&
template<> FSerializer &Serialize(FSerializer &arc, const char *key, line_t *&value, line_t **defval) template<> FSerializer &Serialize(FSerializer &arc, const char *key, line_t *&value, line_t **defval)
{ {
return SerializePointer(arc, key, value, defval, &level.lines[0]); if (arc.Level == nullptr) I_Error("Trying to serialize linedef without a valid level");
return SerializePointer(arc, key, value, defval, &arc.Level->lines[0]);
} }
template<> FSerializer &Serialize(FSerializer &arc, const char *key, vertex_t *&value, vertex_t **defval) template<> FSerializer &Serialize(FSerializer &arc, const char *key, vertex_t *&value, vertex_t **defval)
{ {
return SerializePointer(arc, key, value, defval, &level.vertexes[0]); if (arc.Level == nullptr) I_Error("Trying to serialize verte without a valid level");
return SerializePointer(arc, key, value, defval, &arc.Level->vertexes[0]);
} }
//========================================================================== //==========================================================================
@ -1968,13 +1975,13 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FStrifeDial
} }
else if (val->IsUint()) else if (val->IsUint())
{ {
if (val->GetUint() >= level.StrifeDialogues.Size()) if (val->GetUint() >= arc.Level->StrifeDialogues.Size())
{ {
node = nullptr; node = nullptr;
} }
else else
{ {
node = level.StrifeDialogues[val->GetUint()]; node = arc.Level->StrifeDialogues[val->GetUint()];
} }
} }
else else
@ -2128,6 +2135,65 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, char *&pstr
return arc; return arc;
} }
//==========================================================================
//
// This is a bit of a cheat because it never actually writes out the pointer.
// The rules for levels are that they must be self-contained.
// No level and no pbject that is part of a level may reference a different level.
//
// When writing, this merely checks if the rules are obeyed and if not errors out.
// When reading, it assumes that the object was properly written and restores
// the reference from the owning level.
//
// The only exception are null pointers which are allowed
//
//==========================================================================
template<> FSerializer &Serialize(FSerializer &arc, const char *key, FLevelLocals *&lev, FLevelLocals **def)
{
if (arc.isWriting())
{
if (!arc.w->inObject() || lev == nullptr)
{
arc.WriteKey(key);
if (lev == nullptr)
{
arc.w->Null();
}
else
{
// This MUST be the currently serialized level. Anything else will error out here as a sanity check.
if (arc.Level == nullptr || lev != arc.Level)
{
I_Error("Attempt to serialize invalid level reference");
}
if (!arc.w->inObject())
{
arc.w->Bool(true); // In the unlikely case this is used in an array, write a filler.
}
}
}
}
else
{
auto val = arc.r->FindKey(key);
if (val != nullptr)
{
assert(val->IsNull() || val->IsBool());
if (val->IsNull())
{
lev = nullptr;
}
else
{
lev = arc.Level;
}
}
else lev = arc.Level;
}
return arc;
}
//========================================================================== //==========================================================================
// //
// //

View file

@ -65,6 +65,7 @@ class FSerializer
public: public:
FWriter *w = nullptr; FWriter *w = nullptr;
FReader *r = nullptr; FReader *r = nullptr;
FLevelLocals *Level;
unsigned ArraySize(); unsigned ArraySize();
void WriteKey(const char *key); void WriteKey(const char *key);
@ -72,6 +73,9 @@ public:
public: public:
FSerializer(FLevelLocals *l) : Level(l)
{}
~FSerializer() ~FSerializer()
{ {
mErrors = 0; // The destructor may not throw an exception so silence the error checker. mErrors = 0; // The destructor may not throw an exception so silence the error checker.
@ -262,6 +266,7 @@ template<> FSerializer &Serialize(FSerializer &arc, const char *key, FString *&p
template<> FSerializer &Serialize(FSerializer &arc, const char *key, FDoorAnimation *&pstr, FDoorAnimation **def); template<> FSerializer &Serialize(FSerializer &arc, const char *key, FDoorAnimation *&pstr, FDoorAnimation **def);
template<> FSerializer &Serialize(FSerializer &arc, const char *key, char *&pstr, char **def); template<> FSerializer &Serialize(FSerializer &arc, const char *key, char *&pstr, char **def);
template<> FSerializer &Serialize(FSerializer &arc, const char *key, FFont *&font, FFont **def); template<> FSerializer &Serialize(FSerializer &arc, const char *key, FFont *&font, FFont **def);
template<> FSerializer &Serialize(FSerializer &arc, const char *key, FLevelLocals *&font, FLevelLocals **def);
FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def, bool *retcode); FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def, bool *retcode);
template<> inline FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def) template<> inline FSerializer &Serialize(FSerializer &arc, const char *key, FState *&state, FState **def)