From daf43f9d35e81af1c4d0975cec6ca7910d254ae0 Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Tue, 20 Sep 2016 09:11:13 +0200 Subject: [PATCH] - 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. --- src/am_map.cpp | 15 ++-- src/am_map.h | 4 +- src/g_level.cpp | 10 +-- src/g_level.h | 9 +- src/json.cpp | 48 +++++----- src/p_mobj.cpp | 7 +- src/p_saveg.cpp | 216 +++++++++++++++++++++++---------------------- src/p_scroll.cpp | 4 +- src/p_setup.cpp | 8 +- src/portal.cpp | 42 --------- src/serializer.cpp | 15 ++++ src/serializer.h | 1 + src/zzz_old.cpp | 42 +++++++++ 13 files changed, 221 insertions(+), 200 deletions(-) diff --git a/src/am_map.cpp b/src/am_map.cpp index 7e346843b..9292e81d9 100644 --- a/src/am_map.cpp +++ b/src/am_map.cpp @@ -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 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); } } diff --git a/src/g_level.h b/src/g_level.h index 8360bd45e..5b994a3d2 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -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 Scrolls; // NULL if no DScrollers in this level SBYTE WallVertLight; // Light diffs for vert/horiz walls SBYTE WallHorizLight; diff --git a/src/json.cpp b/src/json.cpp index c3fb88247..0fca84d4a 100644 --- a/src/json.cpp +++ b/src/json.cpp @@ -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(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(); diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 2459f79af..70fff197a 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -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 { diff --git a/src/p_saveg.cpp b/src/p_saveg.cpp index 99c372b4d..13fac2e7e 100644 --- a/src/p_saveg.cpp +++ b/src/p_saveg.cpp @@ -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(§ors[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 } diff --git a/src/p_scroll.cpp b/src/p_scroll.cpp index 761ce00ec..2c7333a21 100644 --- a/src/p_scroll.cpp +++ b/src/p_scroll.cpp @@ -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 diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 99bb01330..aafed854b 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -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; diff --git a/src/portal.cpp b/src/portal.cpp index d7dad220e..90cff83db 100644 --- a/src/portal.cpp +++ b/src/portal.cpp @@ -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 diff --git a/src/serializer.cpp b/src/serializer.cpp index 44d95c3dd..35ce9d4e3 100644 --- a/src/serializer.cpp +++ b/src/serializer.cpp @@ -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 diff --git a/src/serializer.h b/src/serializer.h index 15b969692..efa92a550 100644 --- a/src/serializer.h +++ b/src/serializer.h @@ -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); diff --git a/src/zzz_old.cpp b/src/zzz_old.cpp index 8b9d0277f..939767c87 100644 --- a/src/zzz_old.cpp +++ b/src/zzz_old.cpp @@ -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; +} + +