- 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 "p_setup.h"
#include "c_bind.h"
#include "farchive.h"
#include "serializer.h"
#include "r_renderer.h"
#include "r_sky.h"
#include "sbar.h"
@ -3106,13 +3106,14 @@ void AM_Drawer ()
//
//=============================================================================
void AM_SerializeMarkers(FArchive &arc)
void AM_SerializeMarkers(FSerializer &arc)
{
arc << markpointnum;
for (int i=0; i<AM_NUMMARKPOINTS; i++)
if (arc.BeginObject("automarkers"))
{
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__
struct event_t;
class FArchive;
class FSerializer;
void AM_StaticInit();
@ -45,7 +45,7 @@ void AM_Stop (void);
void AM_NewResolution ();
void AM_ToggleMap ();
void AM_LevelInit ();
void AM_SerializeMarkers(FArchive &arc);
void AM_SerializeMarkers(FSerializer &arc);
#endif

View file

@ -1815,9 +1815,9 @@ void P_ReadACSDefereds (PNGHandle *png)
void FLevelLocals::Tick ()
{
// 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;
}
if (Scrolls == NULL)
if (Scrolls.Size() == 0)
{
Scrolls = new FSectorScrollValues[numsectors];
memset (Scrolls, 0, sizeof(*Scrolls)*numsectors);
Scrolls.Resize(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
{
void Tick ();
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 maptime; // time in the map
int totaltime; // time in the game
@ -436,7 +431,7 @@ struct FLevelLocals
int airsupply;
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 WallHorizLight;

View file

@ -5,6 +5,28 @@
#include "i_system.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))
{
assert(node->NextThinker != NULL && !(node->NextThinker->ObjectFlags & OF_EuthanizeMe));
//node->SerializeUserVars(arc);
::Serialize<DThinker>(arc, nullptr, node, nullptr);
node->CheckIfSerialized();
node = node->NextThinker;
}
}
}
void DThinker::SerializeThinkers(FSerializer &arc, bool hubLoad)
{
//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)
{
@ -81,7 +87,7 @@ CCMD(writejson)
arc.OpenWriter();
arc.BeginObject(nullptr);
DThinker::SerializeThinkers(arc, false);
SerializeWorld(arc);
G_SerializeLevel(arc, false);
arc.WriteObjects();
arc.EndObject();
DWORD tt = I_MSTime();

View file

@ -3432,7 +3432,7 @@ void AActor::Tick ()
// [RH] Consider carrying sectors here
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
const msecnode_t *node;
@ -3453,10 +3453,9 @@ void AActor::Tick ()
sector_t *sec = node->m_sector;
DVector2 scrollv;
if (level.Scrolls != NULL)
if (level.Scrolls.Size() > (sec-sectors))
{
const FSectorScrollValues *scroll = &level.Scrolls[sec - sectors];
scrollv = scroll->Scroll;
scrollv = level.Scrolls[sec - sectors];
}
else
{

View file

@ -732,6 +732,36 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FSectorPortal &port, F
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
arc.Array("linedefs", lines, &loadlines[0], numlines)
.Array("sidedefs", sides, &loadsides[0], numsides)
.Array("sectors", sectors, &loadsectors[0], numsectors)
("subsectors", subsectors)
("zones", Zones)
("lineportals", linePortals)
("sectorportals", sectorPortals);
AM_SerializeMarkers(arc);
//FBehavior::StaticSerializeModuleStates(arc);
arc.Array("linedefs", lines, &loadlines[0], numlines);
arc.Array("sidedefs", sides, &loadsides[0], numsides);
arc.Array("sectors", sectors, &loadsectors[0], numsectors);
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();
}
//==========================================================================
//
// ArchiveSounds
@ -830,33 +928,8 @@ void P_SerializePolyobjs (FArchive &arc)
DAngle angle;
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++)
{
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)
{
int i = level.totaltime;
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;
#if 0
// Does this level have custom translations?
FRemapTable *trans;
WORD w;
@ -969,17 +976,17 @@ void G_SerializeLevel(FArchive &arc, bool hubLoad)
// This must be saved, too, of course!
FCanvasTextureInfo::Serialize(arc);
AM_SerializeMarkers(arc);
//AM_SerializeMarkers(arc);
P_SerializePlayers(arc, hubLoad);
P_SerializeSounds(arc);
if (arc.IsLoading())
{
for (i = 0; i < numsectors; i++)
for (int i = 0; i < numsectors; 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)
{
@ -988,7 +995,6 @@ void G_SerializeLevel(FArchive &arc, bool hubLoad)
}
}
Renderer->EndSerialize(arc);
unsigned tt = I_MSTime();
Printf("Serialization took %d ms\n", tt - tm);
#endif
}

View file

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

View file

@ -3552,11 +3552,7 @@ void P_FreeLevelData ()
po_NumPolyobjs = 0;
Zones.Clear();
P_FreeStrifeConversations ();
if (level.Scrolls != NULL)
{
delete[] level.Scrolls;
level.Scrolls = NULL;
}
level.Scrolls.Clear();
P_ClearUDMFKeys();
}
@ -3668,6 +3664,8 @@ void P_SetupLevel (const char *lumpname, int position)
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
level.lumpnum = map->lumpnum;
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

View file

@ -534,6 +534,21 @@ FSerializer &FSerializer::StringPtr(const char *key, const char *&charptr)
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

View file

@ -40,6 +40,7 @@ public:
bool BeginArray(const char *name);
void EndArray();
void WriteObjects();
unsigned GetSize(const char *group);
const char *GetOutput(unsigned *len = nullptr);
FSerializer &Args(const char *key, int *args, int *defargs, int special);
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;
}