- removed the sequential processing of JSON objects because the benefit is too small.
After testing with a savegame on ZDCMP2 which is probably the largest map in existence, timing both methods resulted in a speed difference of less than 40 ms (70 vs 110 ms for reading all sectory, linedefs, sidedefs and objects). This compares to an overall restoration time, including reloading the level, precaching all textures and setting everything up, of approx. 1.2 s, meaning an increase of 3% of the entire reloading time. That's simply not worth all the negative side effects that may happen with a method that highly depends on proper code construction. On the other hand, using random access means that a savegame version change is only needed now when the semantics of a field change, but not if some get added or deleted. - do not I_Error out in the serializer unless caused by a programming error. It is better to let the serializer finish, collect all the errors and I_Error out when the game is known to be in a stable enough state to allow unwinding.
This commit is contained in:
parent
5a3f1dcdb6
commit
86e9282193
11 changed files with 346 additions and 254 deletions
281
src/g_game.cpp
281
src/g_game.cpp
|
|
@ -27,6 +27,7 @@
|
|||
#include <stdio.h>
|
||||
#include <stddef.h>
|
||||
#include <time.h>
|
||||
#include <memory>
|
||||
#ifdef __APPLE__
|
||||
#include <CoreServices/CoreServices.h>
|
||||
#endif
|
||||
|
|
@ -1844,154 +1845,162 @@ void G_DoLoadGame ()
|
|||
Printf ("Could not read savegame '%s'\n", savename.GetChars());
|
||||
return;
|
||||
}
|
||||
|
||||
FResourceLump *info = resfile->FindLump("info.json");
|
||||
if (info == nullptr)
|
||||
try
|
||||
{
|
||||
delete resfile;
|
||||
Printf ("'%s' is not a valid savegame: Missing 'info.json'.\n", savename.GetChars());
|
||||
return;
|
||||
}
|
||||
|
||||
SaveVersion = 0;
|
||||
|
||||
void *data = info->CacheLump();
|
||||
FSerializer arc;
|
||||
if (!arc.OpenReader((const char *)data, info->LumpSize))
|
||||
{
|
||||
Printf("Failed to access savegame info\n");
|
||||
delete resfile;
|
||||
return;
|
||||
}
|
||||
|
||||
// Check whether this savegame actually has been created by a compatible engine.
|
||||
// Since there are ZDoom derivates using the exact same savegame format but
|
||||
// with mutual incompatibilities this check simplifies things significantly.
|
||||
FString savever, engine, map;
|
||||
arc("Save Version", SaveVersion);
|
||||
arc("Engine", engine);
|
||||
arc("Current Map", map);
|
||||
|
||||
if (engine.CompareNoCase(GAMESIG) != 0)
|
||||
{
|
||||
// Make a special case for the message printed for old savegames that don't
|
||||
// have this information.
|
||||
if (engine.IsEmpty())
|
||||
FResourceLump *info = resfile->FindLump("info.json");
|
||||
if (info == nullptr)
|
||||
{
|
||||
Printf ("Savegame is from an incompatible version\n");
|
||||
delete resfile;
|
||||
Printf("'%s' is not a valid savegame: Missing 'info.json'.\n", savename.GetChars());
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
SaveVersion = 0;
|
||||
|
||||
void *data = info->CacheLump();
|
||||
FSerializer arc;
|
||||
if (!arc.OpenReader((const char *)data, info->LumpSize))
|
||||
{
|
||||
Printf ("Savegame is from another ZDoom-based engine: %s\n", engine);
|
||||
Printf("Failed to access savegame info\n");
|
||||
delete resfile;
|
||||
return;
|
||||
}
|
||||
delete resfile;
|
||||
return;
|
||||
}
|
||||
|
||||
if (SaveVersion < MINSAVEVER || SaveVersion > SAVEVER)
|
||||
{
|
||||
delete resfile;
|
||||
Printf ("Savegame is from an incompatible version");
|
||||
if (SaveVersion < MINSAVEVER)
|
||||
// Check whether this savegame actually has been created by a compatible engine.
|
||||
// Since there are ZDoom derivates using the exact same savegame format but
|
||||
// with mutual incompatibilities this check simplifies things significantly.
|
||||
FString savever, engine, map;
|
||||
arc("Save Version", SaveVersion);
|
||||
arc("Engine", engine);
|
||||
arc("Current Map", map);
|
||||
|
||||
if (engine.CompareNoCase(GAMESIG) != 0)
|
||||
{
|
||||
Printf(": %d (%d is the oldest supported)", SaveVersion, MINSAVEVER);
|
||||
// Make a special case for the message printed for old savegames that don't
|
||||
// have this information.
|
||||
if (engine.IsEmpty())
|
||||
{
|
||||
Printf("Savegame is from an incompatible version\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf("Savegame is from another ZDoom-based engine: %s\n", engine);
|
||||
}
|
||||
delete resfile;
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
if (SaveVersion < MINSAVEVER || SaveVersion > SAVEVER)
|
||||
{
|
||||
Printf(": %d (%d is the highest supported)", SaveVersion, SAVEVER);
|
||||
delete resfile;
|
||||
Printf("Savegame is from an incompatible version");
|
||||
if (SaveVersion < MINSAVEVER)
|
||||
{
|
||||
Printf(": %d (%d is the oldest supported)", SaveVersion, MINSAVEVER);
|
||||
}
|
||||
else
|
||||
{
|
||||
Printf(": %d (%d is the highest supported)", SaveVersion, SAVEVER);
|
||||
}
|
||||
Printf("\n");
|
||||
return;
|
||||
}
|
||||
Printf("\n");
|
||||
|
||||
if (!G_CheckSaveGameWads(arc, true))
|
||||
{
|
||||
delete resfile;
|
||||
return;
|
||||
}
|
||||
|
||||
if (map.IsEmpty())
|
||||
{
|
||||
Printf("Savegame is missing the current map\n");
|
||||
delete resfile;
|
||||
return;
|
||||
}
|
||||
|
||||
// Now that it looks like we can load this save, hide the fullscreen console if it was up
|
||||
// when the game was selected from the menu.
|
||||
if (hidecon && gamestate == GS_FULLCONSOLE)
|
||||
{
|
||||
gamestate = GS_HIDECONSOLE;
|
||||
}
|
||||
// we are done with info.json.
|
||||
arc.Close();
|
||||
|
||||
info = resfile->FindLump("globals.json");
|
||||
if (info == nullptr)
|
||||
{
|
||||
delete resfile;
|
||||
Printf("'%s' is not a valid savegame: Missing 'globals.json'.\n", savename.GetChars());
|
||||
return;
|
||||
}
|
||||
|
||||
data = info->CacheLump();
|
||||
if (!arc.OpenReader((const char *)data, info->LumpSize))
|
||||
{
|
||||
Printf("Failed to access savegame info\n");
|
||||
delete resfile;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Read intermission data for hubs
|
||||
G_SerializeHub(arc);
|
||||
|
||||
bglobal.RemoveAllBots(true);
|
||||
|
||||
FString cvar;
|
||||
arc("importantcvars", cvar);
|
||||
if (!cvar.IsEmpty())
|
||||
{
|
||||
BYTE *vars_p = (BYTE *)cvar.GetChars();
|
||||
C_ReadCVars(&vars_p);
|
||||
}
|
||||
|
||||
DWORD time[2] = { 1,0 };
|
||||
|
||||
arc("ticrate", time[0])
|
||||
("leveltime", time[1]);
|
||||
// dearchive all the modifications
|
||||
level.time = Scale(time[1], TICRATE, time[0]);
|
||||
|
||||
G_ReadSnapshots(resfile);
|
||||
delete resfile; // we no longer need the resource file below this point
|
||||
resfile = nullptr;
|
||||
G_ReadVisited(arc);
|
||||
|
||||
// load a base level
|
||||
savegamerestore = true; // Use the player actors in the savegame
|
||||
bool demoplaybacksave = demoplayback;
|
||||
G_InitNew(map, false);
|
||||
demoplayback = demoplaybacksave;
|
||||
savegamerestore = false;
|
||||
|
||||
STAT_Serialize(arc);
|
||||
FRandom::StaticReadRNGState(arc);
|
||||
P_ReadACSDefereds(arc);
|
||||
P_ReadACSVars(arc);
|
||||
|
||||
NextSkill = -1;
|
||||
arc("nextskill", NextSkill);
|
||||
|
||||
if (level.info != nullptr)
|
||||
level.info->Snapshot.Clean();
|
||||
|
||||
BackupSaveName = savename;
|
||||
|
||||
// At this point, the GC threshold is likely a lot higher than the
|
||||
// amount of memory in use, so bring it down now by starting a
|
||||
// collection.
|
||||
GC::StartCollection();
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
// delete the resource file if anything goes wrong in here.
|
||||
if (resfile != nullptr) delete resfile;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!G_CheckSaveGameWads (arc, true))
|
||||
{
|
||||
delete resfile;
|
||||
return;
|
||||
}
|
||||
|
||||
if (map.IsEmpty())
|
||||
{
|
||||
Printf ("Savegame is missing the current map\n");
|
||||
delete resfile;
|
||||
return;
|
||||
}
|
||||
|
||||
// Now that it looks like we can load this save, hide the fullscreen console if it was up
|
||||
// when the game was selected from the menu.
|
||||
if (hidecon && gamestate == GS_FULLCONSOLE)
|
||||
{
|
||||
gamestate = GS_HIDECONSOLE;
|
||||
}
|
||||
// we are done with info.json.
|
||||
arc.Close();
|
||||
|
||||
info = resfile->FindLump("globals.json");
|
||||
if (info == nullptr)
|
||||
{
|
||||
delete resfile;
|
||||
Printf("'%s' is not a valid savegame: Missing 'globals.json'.\n", savename.GetChars());
|
||||
return;
|
||||
}
|
||||
|
||||
data = info->CacheLump();
|
||||
if (!arc.OpenReader((const char *)data, info->LumpSize))
|
||||
{
|
||||
Printf("Failed to access savegame info\n");
|
||||
delete resfile;
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// Read intermission data for hubs
|
||||
G_SerializeHub(arc);
|
||||
|
||||
bglobal.RemoveAllBots (true);
|
||||
|
||||
FString cvar;
|
||||
arc("importantcvars",cvar);
|
||||
if (!cvar.IsEmpty())
|
||||
{
|
||||
BYTE *vars_p = (BYTE *)cvar.GetChars();
|
||||
C_ReadCVars (&vars_p);
|
||||
}
|
||||
|
||||
DWORD time[2] = { 1,0 };
|
||||
|
||||
arc("ticrate", time[0])
|
||||
("leveltime", time[1]);
|
||||
// dearchive all the modifications
|
||||
level.time = Scale (time[1], TICRATE, time[0]);
|
||||
|
||||
G_ReadSnapshots(resfile);
|
||||
G_ReadVisited(arc);
|
||||
|
||||
// load a base level
|
||||
savegamerestore = true; // Use the player actors in the savegame
|
||||
bool demoplaybacksave = demoplayback;
|
||||
G_InitNew (map, false);
|
||||
demoplayback = demoplaybacksave;
|
||||
savegamerestore = false;
|
||||
|
||||
STAT_Serialize(arc);
|
||||
FRandom::StaticReadRNGState(arc);
|
||||
P_ReadACSDefereds(arc);
|
||||
P_ReadACSVars(arc);
|
||||
|
||||
NextSkill = -1;
|
||||
arc("nextskill", NextSkill);
|
||||
|
||||
if (level.info != nullptr)
|
||||
level.info->Snapshot.Clean();
|
||||
|
||||
BackupSaveName = savename;
|
||||
|
||||
delete resfile;
|
||||
|
||||
// At this point, the GC threshold is likely a lot higher than the
|
||||
// amount of memory in use, so bring it down now by starting a
|
||||
// collection.
|
||||
GC::StartCollection();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue