mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-11-11 15:21:51 +00:00
- added object deserialization. It seems to work, at least the stuff I sampled looked like it was properly reatored and it triggers no error condition.
- always make the top level object randomaccess when opening a JSON file for reading. Some things won't work right if this is opened for sequential access.
This commit is contained in:
parent
604b2b316b
commit
c17da32dbd
8 changed files with 149 additions and 28 deletions
|
@ -209,6 +209,7 @@ enum EObjectFlags
|
||||||
OF_JustSpawned = 1 << 8, // Thinker was spawned this tic
|
OF_JustSpawned = 1 << 8, // Thinker was spawned this tic
|
||||||
OF_SerialSuccess = 1 << 9, // For debugging Serialize() calls
|
OF_SerialSuccess = 1 << 9, // For debugging Serialize() calls
|
||||||
OF_Sentinel = 1 << 10, // Object is serving as the sentinel in a ring list
|
OF_Sentinel = 1 << 10, // Object is serving as the sentinel in a ring list
|
||||||
|
OF_LoadedPlayer = 1 << 11, // this gets flagged during deserialization so that the player checks in there can be simplified.
|
||||||
};
|
};
|
||||||
|
|
||||||
template<class T> class TObjPtr;
|
template<class T> class TObjPtr;
|
||||||
|
|
|
@ -88,6 +88,7 @@ public:
|
||||||
static void MarkRoots();
|
static void MarkRoots();
|
||||||
|
|
||||||
static DThinker *FirstThinker (int statnum);
|
static DThinker *FirstThinker (int statnum);
|
||||||
|
static bool bSerialOverride;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum no_link_type { NO_LINK };
|
enum no_link_type { NO_LINK };
|
||||||
|
@ -100,7 +101,6 @@ private:
|
||||||
|
|
||||||
static FThinkerList Thinkers[MAX_STATNUM+2]; // Current thinkers
|
static FThinkerList Thinkers[MAX_STATNUM+2]; // Current thinkers
|
||||||
static FThinkerList FreshThinkers[MAX_STATNUM+1]; // Newly created thinkers
|
static FThinkerList FreshThinkers[MAX_STATNUM+1]; // Newly created thinkers
|
||||||
static bool bSerialOverride;
|
|
||||||
|
|
||||||
friend struct FThinkerList;
|
friend struct FThinkerList;
|
||||||
friend class FThinkerIterator;
|
friend class FThinkerIterator;
|
||||||
|
|
|
@ -1857,7 +1857,7 @@ void G_DoLoadGame ()
|
||||||
|
|
||||||
void *data = info->CacheLump();
|
void *data = info->CacheLump();
|
||||||
FSerializer arc;
|
FSerializer arc;
|
||||||
if (!arc.OpenReader((const char *)data, info->LumpSize, true))
|
if (!arc.OpenReader((const char *)data, info->LumpSize))
|
||||||
{
|
{
|
||||||
Printf("Failed to access savegame info\n");
|
Printf("Failed to access savegame info\n");
|
||||||
delete resfile;
|
delete resfile;
|
||||||
|
@ -1935,7 +1935,7 @@ void G_DoLoadGame ()
|
||||||
}
|
}
|
||||||
|
|
||||||
data = info->CacheLump();
|
data = info->CacheLump();
|
||||||
if (!arc.OpenReader((const char *)data, info->LumpSize, true))
|
if (!arc.OpenReader((const char *)data, info->LumpSize))
|
||||||
{
|
{
|
||||||
Printf("Failed to access savegame info\n");
|
Printf("Failed to access savegame info\n");
|
||||||
delete resfile;
|
delete resfile;
|
||||||
|
|
|
@ -1517,8 +1517,6 @@ void G_UnSnapshotLevel (bool hubLoad)
|
||||||
FSerializer arc;
|
FSerializer arc;
|
||||||
if (!arc.OpenReader(&level.info->Snapshot)) return;
|
if (!arc.OpenReader(&level.info->Snapshot)) return;
|
||||||
|
|
||||||
//if (hubLoad) arc.SetHubTravel (); // no idea if this is still needed.
|
|
||||||
|
|
||||||
G_SerializeLevel (arc, hubLoad);
|
G_SerializeLevel (arc, hubLoad);
|
||||||
level.FromSnapshot = true;
|
level.FromSnapshot = true;
|
||||||
|
|
||||||
|
|
|
@ -242,7 +242,7 @@ void DLoadSaveMenu::ReadSaveStrings ()
|
||||||
}
|
}
|
||||||
void *data = info->CacheLump();
|
void *data = info->CacheLump();
|
||||||
FSerializer arc;
|
FSerializer arc;
|
||||||
if (arc.OpenReader((const char *)data, info->LumpSize, true))
|
if (arc.OpenReader((const char *)data, info->LumpSize))
|
||||||
{
|
{
|
||||||
int savever = 0;
|
int savever = 0;
|
||||||
FString engine;
|
FString engine;
|
||||||
|
@ -537,7 +537,7 @@ void DLoadSaveMenu::ExtractSaveData (int index)
|
||||||
}
|
}
|
||||||
void *data = info->CacheLump();
|
void *data = info->CacheLump();
|
||||||
FSerializer arc;
|
FSerializer arc;
|
||||||
if (arc.OpenReader((const char *)data, info->LumpSize, true))
|
if (arc.OpenReader((const char *)data, info->LumpSize))
|
||||||
{
|
{
|
||||||
FString time, pcomment, comment;
|
FString time, pcomment, comment;
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "po_man.h"
|
#include "po_man.h"
|
||||||
#include "v_font.h"
|
#include "v_font.h"
|
||||||
#include "w_zip.h"
|
#include "w_zip.h"
|
||||||
|
#include "doomerrors.h"
|
||||||
|
|
||||||
char nulspace[1024 * 1024 * 4];
|
char nulspace[1024 * 1024 * 4];
|
||||||
|
|
||||||
|
@ -196,12 +197,15 @@ struct FReader
|
||||||
{
|
{
|
||||||
TArray<FJSONObject> mObjects;
|
TArray<FJSONObject> mObjects;
|
||||||
rapidjson::Document mDoc;
|
rapidjson::Document mDoc;
|
||||||
|
TArray<DObject *> mDObjects;
|
||||||
|
int mPlayers[MAXPLAYERS];
|
||||||
|
|
||||||
FReader(const char *buffer, size_t length, bool randomaccess)
|
FReader(const char *buffer, size_t length, bool randomaccess)
|
||||||
{
|
{
|
||||||
rapidjson::Document doc;
|
rapidjson::Document doc;
|
||||||
mDoc.Parse(buffer, length);
|
mDoc.Parse(buffer, length);
|
||||||
mObjects.Push(FJSONObject(&mDoc, randomaccess));
|
mObjects.Push(FJSONObject(&mDoc, randomaccess));
|
||||||
|
memset(mPlayers, 0, sizeof(mPlayers));
|
||||||
}
|
}
|
||||||
|
|
||||||
rapidjson::Value *FindKey(const char *key)
|
rapidjson::Value *FindKey(const char *key)
|
||||||
|
@ -258,10 +262,11 @@ bool FSerializer::OpenWriter(bool pretty)
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
bool FSerializer::OpenReader(const char *buffer, size_t length, bool randomaccess)
|
bool FSerializer::OpenReader(const char *buffer, size_t length)
|
||||||
{
|
{
|
||||||
if (w != nullptr || r != nullptr) return false;
|
if (w != nullptr || r != nullptr) return false;
|
||||||
r = new FReader(buffer, length, randomaccess);
|
r = new FReader(buffer, length, true);
|
||||||
|
ReadObjects();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,23 +276,23 @@ bool FSerializer::OpenReader(const char *buffer, size_t length, bool randomacces
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
bool FSerializer::OpenReader(FCompressedBuffer *input, bool randomaccess)
|
bool FSerializer::OpenReader(FCompressedBuffer *input)
|
||||||
{
|
{
|
||||||
if (input->mSize <= 0 || input->mBuffer == nullptr) return false;
|
if (input->mSize <= 0 || input->mBuffer == nullptr) return false;
|
||||||
if (w != nullptr || r != nullptr) return false;
|
if (w != nullptr || r != nullptr) return false;
|
||||||
|
|
||||||
if (input->mMethod == METHOD_STORED)
|
if (input->mMethod == METHOD_STORED)
|
||||||
{
|
{
|
||||||
r = new FReader((char*)input->mBuffer, input->mSize, randomaccess);
|
r = new FReader((char*)input->mBuffer, input->mSize, true);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
char *unpacked = new char[input->mSize];
|
char *unpacked = new char[input->mSize];
|
||||||
input->Decompress(unpacked);
|
input->Decompress(unpacked);
|
||||||
r = new FReader(unpacked, input->mSize, randomaccess);
|
r = new FReader(unpacked, input->mSize, true);
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
ReadObjects();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
@ -400,7 +405,7 @@ bool FSerializer::BeginObject(const char *name, bool randomaccess)
|
||||||
//
|
//
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
|
|
||||||
void FSerializer::EndObject()
|
void FSerializer::EndObject(bool endwarning)
|
||||||
{
|
{
|
||||||
if (isWriting())
|
if (isWriting())
|
||||||
{
|
{
|
||||||
|
@ -416,6 +421,13 @@ void FSerializer::EndObject()
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
if (endwarning && !r->mObjects.Last().mRandomAccess)
|
||||||
|
{
|
||||||
|
if (r->mObjects.Last().mIterator != r->mObjects.Last().mObject->MemberEnd())
|
||||||
|
{
|
||||||
|
I_Error("Incomplete read of sequential object");
|
||||||
|
}
|
||||||
|
}
|
||||||
r->mObjects.Pop();
|
r->mObjects.Pop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -482,6 +494,22 @@ void FSerializer::EndArray()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Discards an entry (only needed for sequential access)
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
FSerializer &FSerializer::Discard(const char *key)
|
||||||
|
{
|
||||||
|
if (isReading())
|
||||||
|
{
|
||||||
|
// just get the key and advance the iterator, if present
|
||||||
|
if (!r->mObjects.Last().mRandomAccess) r->FindKey(key);
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// Special handler for args (because ACS specials' arg0 needs special treatment.)
|
// Special handler for args (because ACS specials' arg0 needs special treatment.)
|
||||||
|
@ -760,6 +788,94 @@ void FSerializer::WriteObjects()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// Writes out all collected objects
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void FSerializer::ReadObjects()
|
||||||
|
{
|
||||||
|
bool founderrors = false;
|
||||||
|
|
||||||
|
if (isReading() && BeginArray("objects"))
|
||||||
|
{
|
||||||
|
// Do not link any thinker that's being created here. This will be done by deserializing the thinker list later.
|
||||||
|
try
|
||||||
|
{
|
||||||
|
DThinker::bSerialOverride = true;
|
||||||
|
r->mDObjects.Resize(ArraySize());
|
||||||
|
// First create all the objects
|
||||||
|
for (unsigned i = 0; i < r->mDObjects.Size(); i++)
|
||||||
|
{
|
||||||
|
if (BeginObject(nullptr))
|
||||||
|
{
|
||||||
|
FString clsname; // do not deserialize the class type directly so that we can print appropriate errors.
|
||||||
|
|
||||||
|
Serialize(*this, "classtype", clsname, nullptr);
|
||||||
|
PClass *cls = PClass::FindClass(clsname);
|
||||||
|
if (cls == nullptr)
|
||||||
|
{
|
||||||
|
Printf("Unknown object class '%d' in savegame", clsname.GetChars());
|
||||||
|
founderrors = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
r->mDObjects[i] = cls->CreateNew();
|
||||||
|
}
|
||||||
|
EndObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Now that everything has been created and we can retrieve the pointers we can deserialize it.
|
||||||
|
|
||||||
|
if (!founderrors)
|
||||||
|
{
|
||||||
|
// Reset to start;
|
||||||
|
r->mObjects.Last().mIndex = 0;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < r->mDObjects.Size(); i++)
|
||||||
|
{
|
||||||
|
if (BeginObject(nullptr))
|
||||||
|
{
|
||||||
|
Discard("classtype");
|
||||||
|
|
||||||
|
int pindex = -1;
|
||||||
|
Serialize(*this, "playerindex", pindex, nullptr);
|
||||||
|
auto obj = r->mDObjects[i];
|
||||||
|
if (pindex >= 0 && pindex < MAXPLAYERS)
|
||||||
|
{
|
||||||
|
obj->ObjectFlags |= OF_LoadedPlayer;
|
||||||
|
r->mPlayers[pindex] = int(i);
|
||||||
|
}
|
||||||
|
obj->SerializeUserVars(*this);
|
||||||
|
obj->Serialize(*this);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
EndObject(true);
|
||||||
|
}
|
||||||
|
catch (CRecoverableError &err)
|
||||||
|
{
|
||||||
|
I_Error("%s\n while restoring %s", err.GetMessage(), obj->GetClass()->TypeName.GetChars());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EndArray();
|
||||||
|
DThinker::bSerialOverride = false;
|
||||||
|
}
|
||||||
|
catch(...)
|
||||||
|
{
|
||||||
|
// make sure this flag gets unset, even if something in here throws an error.
|
||||||
|
DThinker::bSerialOverride = false;
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (founderrors)
|
||||||
|
{
|
||||||
|
I_Error("Failed to restore all objects in savegame");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
@ -1209,7 +1325,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, FTextureID &value, FTe
|
||||||
auto val = arc.r->FindKey(key);
|
auto val = arc.r->FindKey(key);
|
||||||
if (val != nullptr)
|
if (val != nullptr)
|
||||||
{
|
{
|
||||||
if (val->IsObject())
|
if (val->IsArray())
|
||||||
{
|
{
|
||||||
const rapidjson::Value &nameval = (*val)[0];
|
const rapidjson::Value &nameval = (*val)[0];
|
||||||
const rapidjson::Value &typeval = (*val)[1];
|
const rapidjson::Value &typeval = (*val)[1];
|
||||||
|
@ -1251,7 +1367,7 @@ FSerializer &Serialize(FSerializer &arc, const char *key, DObject *&value, DObje
|
||||||
if (retcode) *retcode = true;
|
if (retcode) *retcode = true;
|
||||||
if (arc.isWriting())
|
if (arc.isWriting())
|
||||||
{
|
{
|
||||||
if (value != nullptr)
|
if (value != nullptr && !(value->ObjectFlags & OF_EuthanizeMe))
|
||||||
{
|
{
|
||||||
int ndx;
|
int ndx;
|
||||||
if (value == WP_NOCHANGE)
|
if (value == WP_NOCHANGE)
|
||||||
|
|
|
@ -16,7 +16,16 @@ struct FReader;
|
||||||
extern TArray<sector_t> loadsectors;
|
extern TArray<sector_t> loadsectors;
|
||||||
extern TArray<line_t> loadlines;
|
extern TArray<line_t> loadlines;
|
||||||
extern TArray<side_t> loadsides;
|
extern TArray<side_t> loadsides;
|
||||||
extern char nulspace[];
|
|
||||||
|
inline bool nullcmp(const void *buffer, size_t length)
|
||||||
|
{
|
||||||
|
const char *p = (const char *)buffer;
|
||||||
|
for (; length > 0; length--)
|
||||||
|
{
|
||||||
|
if (*p++ != 0) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
struct NumericValue
|
struct NumericValue
|
||||||
{
|
{
|
||||||
|
@ -51,6 +60,8 @@ public:
|
||||||
|
|
||||||
int ArraySize();
|
int ArraySize();
|
||||||
void WriteKey(const char *key);
|
void WriteKey(const char *key);
|
||||||
|
void WriteObjects();
|
||||||
|
void ReadObjects();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
@ -59,18 +70,18 @@ public:
|
||||||
Close();
|
Close();
|
||||||
}
|
}
|
||||||
bool OpenWriter(bool pretty = true);
|
bool OpenWriter(bool pretty = true);
|
||||||
bool OpenReader(const char *buffer, size_t length, bool randomaccess = false);
|
bool OpenReader(const char *buffer, size_t length);
|
||||||
bool OpenReader(FCompressedBuffer *input, bool randomaccess = false);
|
bool OpenReader(FCompressedBuffer *input);
|
||||||
void Close();
|
void Close();
|
||||||
bool BeginObject(const char *name, bool randomaccess = false);
|
bool BeginObject(const char *name, bool randomaccess = false);
|
||||||
void EndObject();
|
void EndObject(bool endwarning = false);
|
||||||
bool BeginArray(const char *name);
|
bool BeginArray(const char *name);
|
||||||
void EndArray();
|
void EndArray();
|
||||||
void WriteObjects();
|
|
||||||
unsigned GetSize(const char *group);
|
unsigned GetSize(const char *group);
|
||||||
const char *GetKey();
|
const char *GetKey();
|
||||||
const char *GetOutput(unsigned *len = nullptr);
|
const char *GetOutput(unsigned *len = nullptr);
|
||||||
FCompressedBuffer GetCompressedOutput();
|
FCompressedBuffer GetCompressedOutput();
|
||||||
|
FSerializer &Discard(const char *key);
|
||||||
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);
|
||||||
FSerializer &Sprite(const char *key, int32_t &spritenum, int32_t *def);
|
FSerializer &Sprite(const char *key, int32_t &spritenum, int32_t *def);
|
||||||
|
@ -104,7 +115,7 @@ public:
|
||||||
template<class T>
|
template<class T>
|
||||||
FSerializer &Array(const char *key, T *obj, int count, bool fullcompare = false)
|
FSerializer &Array(const char *key, T *obj, int count, bool fullcompare = false)
|
||||||
{
|
{
|
||||||
if (fullcompare && isWriting() && !memcmp(obj, nulspace, count * sizeof(T)))
|
if (fullcompare && isWriting() && nullcmp(obj, count * sizeof(T)))
|
||||||
{
|
{
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
|
@ -174,9 +174,6 @@ void DThinker::SerializeAll(FArchive &arc, bool hubLoad)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Prevent the constructor from inserting thinkers into a list.
|
|
||||||
bSerialOverride = true;
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
arc << statcount;
|
arc << statcount;
|
||||||
|
@ -213,11 +210,9 @@ void DThinker::SerializeAll(FArchive &arc, bool hubLoad)
|
||||||
}
|
}
|
||||||
catch (class CDoomError &)
|
catch (class CDoomError &)
|
||||||
{
|
{
|
||||||
bSerialOverride = false;
|
|
||||||
DestroyAllThinkers();
|
DestroyAllThinkers();
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
bSerialOverride = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
Loading…
Reference in a new issue