- added polyobject serializer.

- added sanity checks to prevent a savegame from being loaded with an incompatible map
- refactored a few things to simplify serialization.
- started work on main level serializer function.
This commit is contained in:
Christoph Oelckers 2016-09-20 09:11:13 +02:00
parent ab43e0c8cb
commit daf43f9d35
13 changed files with 221 additions and 200 deletions

View file

@ -38,7 +38,7 @@
#include "gi.h" #include "gi.h"
#include "p_setup.h" #include "p_setup.h"
#include "c_bind.h" #include "c_bind.h"
#include "farchive.h" #include "serializer.h"
#include "r_renderer.h" #include "r_renderer.h"
#include "r_sky.h" #include "r_sky.h"
#include "sbar.h" #include "sbar.h"
@ -3106,13 +3106,14 @@ void AM_Drawer ()
// //
//============================================================================= //=============================================================================
void AM_SerializeMarkers(FArchive &arc) void AM_SerializeMarkers(FSerializer &arc)
{ {
arc << markpointnum; if (arc.BeginObject("automarkers"))
for (int i=0; i<AM_NUMMARKPOINTS; i++)
{ {
arc << markpoints[i].x << markpoints[i].y; arc("markpointnum", markpointnum)
.Array("markpoints", &markpoints[0].x, AM_NUMMARKPOINTS*2) // write as a double array.
("scale_mtof", scale_mtof)
("scale_ftom", scale_ftom)
.EndObject();
} }
arc << scale_mtof;
arc << scale_ftom;
} }

View file

@ -23,7 +23,7 @@
#define __AMMAP_H__ #define __AMMAP_H__
struct event_t; struct event_t;
class FArchive; class FSerializer;
void AM_StaticInit(); void AM_StaticInit();
@ -45,7 +45,7 @@ void AM_Stop (void);
void AM_NewResolution (); void AM_NewResolution ();
void AM_ToggleMap (); void AM_ToggleMap ();
void AM_LevelInit (); void AM_LevelInit ();
void AM_SerializeMarkers(FArchive &arc); void AM_SerializeMarkers(FSerializer &arc);
#endif #endif

View file

@ -1815,9 +1815,9 @@ void P_ReadACSDefereds (PNGHandle *png)
void FLevelLocals::Tick () void FLevelLocals::Tick ()
{ {
// Reset carry sectors // Reset carry sectors
if (Scrolls != NULL) if (Scrolls.Size() > 0)
{ {
memset (Scrolls, 0, sizeof(*Scrolls)*numsectors); memset (&Scrolls[0], 0, sizeof(Scrolls[0])*Scrolls.Size());
} }
} }
@ -1832,10 +1832,10 @@ void FLevelLocals::AddScroller (int secnum)
{ {
return; return;
} }
if (Scrolls == NULL) if (Scrolls.Size() == 0)
{ {
Scrolls = new FSectorScrollValues[numsectors]; Scrolls.Resize(numsectors);
memset (Scrolls, 0, sizeof(*Scrolls)*numsectors); memset (&Scrolls[0], 0, sizeof(Scrolls[0])*numsectors);
} }
} }

View file

@ -375,17 +375,12 @@ struct level_info_t
} }
}; };
// [RH] These get zeroed every tic and are updated by thinkers.
struct FSectorScrollValues
{
DVector2 Scroll;
};
struct FLevelLocals struct FLevelLocals
{ {
void Tick (); void Tick ();
void AddScroller (int secnum); void AddScroller (int secnum);
BYTE md5[16]; // for savegame validation. If the MD5 does not match the savegame won't be loaded.
int time; // time in the hub int time; // time in the hub
int maptime; // time in the map int maptime; // time in the map
int totaltime; // time in the game int totaltime; // time in the game
@ -436,7 +431,7 @@ struct FLevelLocals
int airsupply; int airsupply;
int DefaultEnvironment; // Default sound environment. int DefaultEnvironment; // Default sound environment.
FSectorScrollValues *Scrolls; // NULL if no DScrollers in this level TArray<DVector2> Scrolls; // NULL if no DScrollers in this level
SBYTE WallVertLight; // Light diffs for vert/horiz walls SBYTE WallVertLight; // Light diffs for vert/horiz walls
SBYTE WallHorizLight; SBYTE WallHorizLight;

View file

@ -5,6 +5,28 @@
#include "i_system.h" #include "i_system.h"
#include "a_sharedglobal.h" #include "a_sharedglobal.h"
void DObject::SerializeUserVars(FSerializer &arc)
{
PSymbolTable *symt;
FName varname;
//DWORD count, j;
int *varloc = NULL;
symt = &GetClass()->Symbols;
if (arc.isWriting())
{
// Write all fields that aren't serialized by native code.
//GetClass()->WriteValue(arc, this);
}
else
{
//GetClass()->ReadValue(arc, this);
}
}
//========================================================================== //==========================================================================
// //
// //
@ -18,12 +40,15 @@ void DThinker::SaveList(FSerializer &arc, DThinker *node)
while (!(node->ObjectFlags & OF_Sentinel)) while (!(node->ObjectFlags & OF_Sentinel))
{ {
assert(node->NextThinker != NULL && !(node->NextThinker->ObjectFlags & OF_EuthanizeMe)); assert(node->NextThinker != NULL && !(node->NextThinker->ObjectFlags & OF_EuthanizeMe));
//node->SerializeUserVars(arc);
::Serialize<DThinker>(arc, nullptr, node, nullptr); ::Serialize<DThinker>(arc, nullptr, node, nullptr);
node->CheckIfSerialized();
node = node->NextThinker; node = node->NextThinker;
} }
} }
} }
void DThinker::SerializeThinkers(FSerializer &arc, bool hubLoad) void DThinker::SerializeThinkers(FSerializer &arc, bool hubLoad)
{ {
//DThinker *thinker; //DThinker *thinker;
@ -50,29 +75,10 @@ void DThinker::SerializeThinkers(FSerializer &arc, bool hubLoad)
void DObject::SerializeUserVars(FSerializer &arc)
{
PSymbolTable *symt;
FName varname;
//DWORD count, j;
int *varloc = NULL;
symt = &GetClass()->Symbols;
if (arc.isWriting())
{
// Write all fields that aren't serialized by native code.
//GetClass()->WriteValue(arc, this);
}
else
{
//GetClass()->ReadValue(arc, this);
}
}
void SerializeWorld(FSerializer &arc); void G_SerializeLevel(FSerializer &arc, bool);
CCMD(writejson) CCMD(writejson)
{ {
@ -81,7 +87,7 @@ CCMD(writejson)
arc.OpenWriter(); arc.OpenWriter();
arc.BeginObject(nullptr); arc.BeginObject(nullptr);
DThinker::SerializeThinkers(arc, false); DThinker::SerializeThinkers(arc, false);
SerializeWorld(arc); G_SerializeLevel(arc, false);
arc.WriteObjects(); arc.WriteObjects();
arc.EndObject(); arc.EndObject();
DWORD tt = I_MSTime(); DWORD tt = I_MSTime();

View file

@ -3432,7 +3432,7 @@ void AActor::Tick ()
// [RH] Consider carrying sectors here // [RH] Consider carrying sectors here
DVector2 cumm(0, 0); DVector2 cumm(0, 0);
if ((level.Scrolls != NULL || player != NULL) && !(flags & MF_NOCLIP) && !(flags & MF_NOSECTOR)) if ((level.Scrolls.Size() != 0 || player != NULL) && !(flags & MF_NOCLIP) && !(flags & MF_NOSECTOR))
{ {
double height, waterheight; // killough 4/4/98: add waterheight double height, waterheight; // killough 4/4/98: add waterheight
const msecnode_t *node; const msecnode_t *node;
@ -3453,10 +3453,9 @@ void AActor::Tick ()
sector_t *sec = node->m_sector; sector_t *sec = node->m_sector;
DVector2 scrollv; DVector2 scrollv;
if (level.Scrolls != NULL) if (level.Scrolls.Size() > (sec-sectors))
{ {
const FSectorScrollValues *scroll = &level.Scrolls[sec - sectors]; scrollv = level.Scrolls[sec - sectors];
scrollv = scroll->Scroll;
} }
else else
{ {

View file

@ -732,6 +732,36 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSectorPortal &port, F
return arc; return arc;
} }
//============================================================================
//
// one polyobject.
//
//============================================================================
FSerializer &Serialize(FSerializer &arc, const char *key, FPolyObj &poly, FPolyObj *def)
{
if (arc.BeginObject(key))
{
DAngle angle = poly.Angle;
DVector2 delta = poly.StartSpot.pos;
arc("angle", angle)
("pos", delta)
("interpolation", poly.interpolation)
("blocked", poly.bBlocked)
("hasportals", poly.bHasPortals)
("specialdata", poly.specialdata)
.EndObject();
if (arc.isReading())
{
poly.RotatePolyobj(angle, true);
delta -= poly.StartSpot.pos;
poly.MovePolyobj(delta, true);
}
}
return arc;
}
//========================================================================== //==========================================================================
// //
// //
@ -760,21 +790,89 @@ FSerializer &Serialize(FSerializer &arc, const char *key, zone_t &z, zone_t *def
// //
//============================================================================ //============================================================================
void SerializeWorld(FSerializer &arc) void G_SerializeLevel(FSerializer &arc, bool hubload)
{ {
int i = level.totaltime;
if (arc.isWriting())
{
arc.Array("checksum", level.md5, 16);
}
else
{
// prevent bad things from happening by doing a check on the size of level arrays and the map's entire checksum.
// The old code happily tried to load savegames with any mismatch here, often causing meaningless errors
// deep down in the deserializer or just a crash if the few insufficient safeguards were not triggered.
BYTE chk[16] = { 0 };
arc.Array("checksum", chk, 16);
if (arc.GetSize("linedefs") != numlines ||
arc.GetSize("sidedefs") != numsides ||
arc.GetSize("sectors") != numsectors ||
arc.GetSize("polyobjs") != po_NumPolyobjs ||
memcmp(chk, level.md5, 16))
{
I_Error("Savegame is from a different level");
}
}
//Renderer->StartSerialize(arc);
if (arc.isReading())
{
P_DestroyThinkers(hubload);
// ReadObjects
}
arc("level.flags", level.flags)
("level.flags2", level.flags2)
("level.fadeto", level.fadeto)
("level.found_secrets", level.found_secrets)
("level.found_items", level.found_items)
("level.killed_monsters", level.killed_monsters)
("level.total_secrets", level.total_secrets)
("level.total_items", level.total_items)
("level.total_monsters", level.total_monsters)
("level.gravity", level.gravity)
("level.aircontrol", level.aircontrol)
("level.teamdamage", level.teamdamage)
("level.maptime", level.maptime)
("level.totaltime", i)
("level.skytexture1", level.skytexture1)
("level.skytexture2", level.skytexture2)
("level.scrolls", level.Scrolls);
// Hub transitions must keep the current total time
if (!hubload)
level.totaltime = i;
if (arc.isReading())
{
sky1texture = level.skytexture1;
sky2texture = level.skytexture2;
R_InitSkyMap();
interpolator.ClearInterpolations();
}
G_AirControlChanged();
// fixme: This needs to ensure it reads from the correct place. Should be one once there's enough of this code converted to JSON // fixme: This needs to ensure it reads from the correct place. Should be one once there's enough of this code converted to JSON
arc.Array("linedefs", lines, &loadlines[0], numlines) AM_SerializeMarkers(arc);
.Array("sidedefs", sides, &loadsides[0], numsides)
.Array("sectors", sectors, &loadsectors[0], numsectors) //FBehavior::StaticSerializeModuleStates(arc);
("subsectors", subsectors) arc.Array("linedefs", lines, &loadlines[0], numlines);
("zones", Zones) arc.Array("sidedefs", sides, &loadsides[0], numsides);
("lineportals", linePortals) arc.Array("sectors", sectors, &loadsectors[0], numsectors);
("sectorportals", sectorPortals); arc.Array("polyobjs", polyobjs, po_NumPolyobjs);
arc("subsectors", subsectors);
//StatusBar->Serialize(arc);
arc("zones", Zones);
arc("lineportals", linePortals);
arc("sectorportals", sectorPortals);
if (arc.isReading()) P_CollectLinkedPortals(); if (arc.isReading()) P_CollectLinkedPortals();
} }
//========================================================================== //==========================================================================
// //
// ArchiveSounds // ArchiveSounds
@ -830,33 +928,8 @@ void P_SerializePolyobjs (FArchive &arc)
DAngle angle; DAngle angle;
DVector2 delta; DVector2 delta;
arc << data;
if (data != ASEG_POLYOBJS)
I_Error ("Polyobject marker missing");
arc << data;
if (data != po_NumPolyobjs)
{
I_Error ("UnarchivePolyobjs: Bad polyobj count");
}
for (i = 0, po = polyobjs; i < po_NumPolyobjs; i++, po++) for (i = 0, po = polyobjs; i < po_NumPolyobjs; i++, po++)
{ {
arc << data;
if (data != po->tag)
{
I_Error ("UnarchivePolyobjs: Invalid polyobj tag");
}
arc << angle << delta << po->interpolation;
arc << po->bBlocked;
arc << po->bHasPortals;
if (SaveVersion >= 4548)
{
arc << po->specialdata;
}
po->RotatePolyobj (angle, true);
delta -= po->StartSpot.pos;
po->MovePolyobj (delta, true);
} }
} }
} }
@ -868,73 +941,7 @@ void P_SerializePolyobjs (FArchive &arc)
void G_SerializeLevel(FArchive &arc, bool hubLoad) void G_SerializeLevel(FArchive &arc, bool hubLoad)
{ {
int i = level.totaltime; #if 0
unsigned tm = I_MSTime();
Renderer->StartSerialize(arc);
if (arc.IsLoading()) P_DestroyThinkers(hubLoad);
arc << level.flags
<< level.flags2
<< level.fadeto
<< level.found_secrets
<< level.found_items
<< level.killed_monsters
<< level.gravity
<< level.aircontrol
<< level.teamdamage
<< level.maptime
<< i;
// Hub transitions must keep the current total time
if (!hubLoad)
level.totaltime = i;
arc << level.skytexture1 << level.skytexture2;
if (arc.IsLoading())
{
sky1texture = level.skytexture1;
sky2texture = level.skytexture2;
R_InitSkyMap();
}
G_AirControlChanged();
BYTE t;
// Does this level have scrollers?
if (arc.IsStoring())
{
t = level.Scrolls ? 1 : 0;
arc << t;
}
else
{
arc << t;
if (level.Scrolls)
{
delete[] level.Scrolls;
level.Scrolls = NULL;
}
if (t)
{
level.Scrolls = new FSectorScrollValues[numsectors];
memset(level.Scrolls, 0, sizeof(level.Scrolls)*numsectors);
}
}
FBehavior::StaticSerializeModuleStates(arc);
if (arc.IsLoading()) interpolator.ClearInterpolations();
P_SerializeWorld(arc);
P_SerializeThinkers(arc, hubLoad);
P_SerializeWorldActors(arc); // serializing actor pointers in the world data must be done after SerializeWorld has restored the entire sector state, otherwise LinkToWorld may fail.
P_SerializePolyobjs(arc);
P_SerializeSubsectors(arc);
StatusBar->Serialize(arc);
arc << level.total_monsters << level.total_items << level.total_secrets;
// Does this level have custom translations? // Does this level have custom translations?
FRemapTable *trans; FRemapTable *trans;
WORD w; WORD w;
@ -969,17 +976,17 @@ void G_SerializeLevel(FArchive &arc, bool hubLoad)
// This must be saved, too, of course! // This must be saved, too, of course!
FCanvasTextureInfo::Serialize(arc); FCanvasTextureInfo::Serialize(arc);
AM_SerializeMarkers(arc); //AM_SerializeMarkers(arc);
P_SerializePlayers(arc, hubLoad); P_SerializePlayers(arc, hubLoad);
P_SerializeSounds(arc); P_SerializeSounds(arc);
if (arc.IsLoading()) if (arc.IsLoading())
{ {
for (i = 0; i < numsectors; i++) for (int i = 0; i < numsectors; i++)
{ {
P_Recalculate3DFloors(&sectors[i]); P_Recalculate3DFloors(&sectors[i]);
} }
for (i = 0; i < MAXPLAYERS; ++i) for (int i = 0; i < MAXPLAYERS; ++i)
{ {
if (playeringame[i] && players[i].mo != NULL) if (playeringame[i] && players[i].mo != NULL)
{ {
@ -988,7 +995,6 @@ void G_SerializeLevel(FArchive &arc, bool hubLoad)
} }
} }
Renderer->EndSerialize(arc); Renderer->EndSerialize(arc);
unsigned tt = I_MSTime(); #endif
Printf("Serialization took %d ms\n", tt - tm);
} }

View file

@ -220,8 +220,8 @@ void DScroller::Tick ()
// [RH] Don't actually carry anything here. That happens later. // [RH] Don't actually carry anything here. That happens later.
case EScroll::sc_carry: case EScroll::sc_carry:
level.Scrolls[m_Affectee].Scroll.X += dx; level.Scrolls[m_Affectee].X += dx;
level.Scrolls[m_Affectee].Scroll.Y += dy; level.Scrolls[m_Affectee].Y += dy;
break; break;
case EScroll::sc_carry_ceiling: // to be added later case EScroll::sc_carry_ceiling: // to be added later

View file

@ -3552,11 +3552,7 @@ void P_FreeLevelData ()
po_NumPolyobjs = 0; po_NumPolyobjs = 0;
Zones.Clear(); Zones.Clear();
P_FreeStrifeConversations (); P_FreeStrifeConversations ();
if (level.Scrolls != NULL) level.Scrolls.Clear();
{
delete[] level.Scrolls;
level.Scrolls = NULL;
}
P_ClearUDMFKeys(); P_ClearUDMFKeys();
} }
@ -3668,6 +3664,8 @@ void P_SetupLevel (const char *lumpname, int position)
I_Error("Unable to open map '%s'\n", lumpname); I_Error("Unable to open map '%s'\n", lumpname);
} }
// generate a checksum for the level, to be included and checked with savegames.
map->GetChecksum(level.md5);
// find map num // find map num
level.lumpnum = map->lumpnum; level.lumpnum = map->lumpnum;
hasglnodes = false; hasglnodes = false;

View file

@ -197,48 +197,6 @@ void FLinePortalTraverse::AddLineIntercepts(int bx, int by)
} }
} }
//============================================================================
//
// Save a line portal for savegames.
//
//============================================================================
FArchive &operator<< (FArchive &arc, FLinePortal &port)
{
arc << port.mOrigin
<< port.mDestination
<< port.mDisplacement
<< port.mType
<< port.mFlags
<< port.mDefFlags
<< port.mAlign;
return arc;
}
//============================================================================
//
// Save a sector portal for savegames.
//
//============================================================================
FArchive &operator<< (FArchive &arc, FSectorPortal &port)
{
arc << port.mType
<< port.mFlags
<< port.mPartner
<< port.mPlane
<< port.mOrigin
<< port.mDestination
<< port.mDisplacement
<< port.mPlaneZ;
if (arc.IsLoading())
{
port.mSkybox = nullptr;
}
return arc;
}
//============================================================================ //============================================================================
// //
// finds the destination for a line portal for spawning // finds the destination for a line portal for spawning

View file

@ -534,6 +534,21 @@ FSerializer &FSerializer::StringPtr(const char *key, const char *&charptr)
return *this; return *this;
} }
//==========================================================================
//
//
//
//==========================================================================
unsigned FSerializer::GetSize(const char *group)
{
if (isWriting()) return -1; // we do not know this when writing.
const rapidjson::Value &val = r->mDocObj[group];
if (!val.IsArray()) return -1;
return val.Size();
}
//========================================================================== //==========================================================================
// //
// Writes out all collected objects // Writes out all collected objects

View file

@ -40,6 +40,7 @@ public:
bool BeginArray(const char *name); bool BeginArray(const char *name);
void EndArray(); void EndArray();
void WriteObjects(); void WriteObjects();
unsigned GetSize(const char *group);
const char *GetOutput(unsigned *len = nullptr); const char *GetOutput(unsigned *len = nullptr);
FSerializer &Args(const char *key, int *args, int *defargs, int special); FSerializer &Args(const char *key, int *args, int *defargs, int special);
FSerializer &Terrain(const char *key, int &terrain, int *def = nullptr); FSerializer &Terrain(const char *key, int &terrain, int *def = nullptr);

View file

@ -599,3 +599,45 @@ void P_SerializeACSScriptNumber(FArchive &arc, int &scriptnum, bool was2byte)
} }
} }
//============================================================================
//
// Save a line portal for savegames.
//
//============================================================================
FArchive &operator<< (FArchive &arc, FLinePortal &port)
{
arc << port.mOrigin
<< port.mDestination
<< port.mDisplacement
<< port.mType
<< port.mFlags
<< port.mDefFlags
<< port.mAlign;
return arc;
}
//============================================================================
//
// Save a sector portal for savegames.
//
//============================================================================
FArchive &operator<< (FArchive &arc, FSectorPortal &port)
{
arc << port.mType
<< port.mFlags
<< port.mPartner
<< port.mPlane
<< port.mOrigin
<< port.mDestination
<< port.mDisplacement
<< port.mPlaneZ;
if (arc.IsLoading())
{
port.mSkybox = nullptr;
}
return arc;
}