mirror of
https://github.com/ZDoom/qzdoom-gpl.git
synced 2024-11-16 17:21:24 +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_SerialSuccess = 1 << 9, // For debugging Serialize() calls
|
||||
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;
|
||||
|
|
|
@ -88,6 +88,7 @@ public:
|
|||
static void MarkRoots();
|
||||
|
||||
static DThinker *FirstThinker (int statnum);
|
||||
static bool bSerialOverride;
|
||||
|
||||
private:
|
||||
enum no_link_type { NO_LINK };
|
||||
|
@ -100,7 +101,6 @@ private:
|
|||
|
||||
static FThinkerList Thinkers[MAX_STATNUM+2]; // Current thinkers
|
||||
static FThinkerList FreshThinkers[MAX_STATNUM+1]; // Newly created thinkers
|
||||
static bool bSerialOverride;
|
||||
|
||||
friend struct FThinkerList;
|
||||
friend class FThinkerIterator;
|
||||
|
|
|
@ -1857,7 +1857,7 @@ void G_DoLoadGame ()
|
|||
|
||||
void *data = info->CacheLump();
|
||||
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");
|
||||
delete resfile;
|
||||
|
@ -1935,7 +1935,7 @@ void G_DoLoadGame ()
|
|||
}
|
||||
|
||||
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");
|
||||
delete resfile;
|
||||
|
|
|
@ -1517,8 +1517,6 @@ void G_UnSnapshotLevel (bool hubLoad)
|
|||
FSerializer arc;
|
||||
if (!arc.OpenReader(&level.info->Snapshot)) return;
|
||||
|
||||
//if (hubLoad) arc.SetHubTravel (); // no idea if this is still needed.
|
||||
|
||||
G_SerializeLevel (arc, hubLoad);
|
||||
level.FromSnapshot = true;
|
||||
|
||||
|
|
|
@ -242,7 +242,7 @@ void DLoadSaveMenu::ReadSaveStrings ()
|
|||
}
|
||||
void *data = info->CacheLump();
|
||||
FSerializer arc;
|
||||
if (arc.OpenReader((const char *)data, info->LumpSize, true))
|
||||
if (arc.OpenReader((const char *)data, info->LumpSize))
|
||||
{
|
||||
int savever = 0;
|
||||
FString engine;
|
||||
|
@ -537,7 +537,7 @@ void DLoadSaveMenu::ExtractSaveData (int index)
|
|||
}
|
||||
void *data = info->CacheLump();
|
||||
FSerializer arc;
|
||||
if (arc.OpenReader((const char *)data, info->LumpSize, true))
|
||||
if (arc.OpenReader((const char *)data, info->LumpSize))
|
||||
{
|
||||
FString time, pcomment, comment;
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
#include "po_man.h"
|
||||
#include "v_font.h"
|
||||
#include "w_zip.h"
|
||||
#include "doomerrors.h"
|
||||
|
||||
char nulspace[1024 * 1024 * 4];
|
||||
|
||||
|
@ -196,12 +197,15 @@ struct FReader
|
|||
{
|
||||
TArray<FJSONObject> mObjects;
|
||||
rapidjson::Document mDoc;
|
||||
TArray<DObject *> mDObjects;
|
||||
int mPlayers[MAXPLAYERS];
|
||||
|
||||
FReader(const char *buffer, size_t length, bool randomaccess)
|
||||
{
|
||||
rapidjson::Document doc;
|
||||
mDoc.Parse(buffer, length);
|
||||
mObjects.Push(FJSONObject(&mDoc, randomaccess));
|
||||
memset(mPlayers, 0, sizeof(mPlayers));
|
||||
}
|
||||
|
||||
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;
|
||||
r = new FReader(buffer, length, randomaccess);
|
||||
r = new FReader(buffer, length, true);
|
||||
ReadObjects();
|
||||
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 (w != nullptr || r != nullptr) return false;
|
||||
|
||||
if (input->mMethod == METHOD_STORED)
|
||||
{
|
||||
r = new FReader((char*)input->mBuffer, input->mSize, randomaccess);
|
||||
return true;
|
||||
r = new FReader((char*)input->mBuffer, input->mSize, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
char *unpacked = new char[input->mSize];
|
||||
input->Decompress(unpacked);
|
||||
r = new FReader(unpacked, input->mSize, randomaccess);
|
||||
return true;
|
||||
r = new FReader(unpacked, input->mSize, 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())
|
||||
{
|
||||
|
@ -416,6 +421,13 @@ void FSerializer::EndObject()
|
|||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -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.)
|
||||
|
@ -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);
|
||||
if (val != nullptr)
|
||||
{
|
||||
if (val->IsObject())
|
||||
if (val->IsArray())
|
||||
{
|
||||
const rapidjson::Value &nameval = (*val)[0];
|
||||
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 (arc.isWriting())
|
||||
{
|
||||
if (value != nullptr)
|
||||
if (value != nullptr && !(value->ObjectFlags & OF_EuthanizeMe))
|
||||
{
|
||||
int ndx;
|
||||
if (value == WP_NOCHANGE)
|
||||
|
|
|
@ -16,7 +16,16 @@ struct FReader;
|
|||
extern TArray<sector_t> loadsectors;
|
||||
extern TArray<line_t> loadlines;
|
||||
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
|
||||
{
|
||||
|
@ -51,6 +60,8 @@ public:
|
|||
|
||||
int ArraySize();
|
||||
void WriteKey(const char *key);
|
||||
void WriteObjects();
|
||||
void ReadObjects();
|
||||
|
||||
public:
|
||||
|
||||
|
@ -59,18 +70,18 @@ public:
|
|||
Close();
|
||||
}
|
||||
bool OpenWriter(bool pretty = true);
|
||||
bool OpenReader(const char *buffer, size_t length, bool randomaccess = false);
|
||||
bool OpenReader(FCompressedBuffer *input, bool randomaccess = false);
|
||||
bool OpenReader(const char *buffer, size_t length);
|
||||
bool OpenReader(FCompressedBuffer *input);
|
||||
void Close();
|
||||
bool BeginObject(const char *name, bool randomaccess = false);
|
||||
void EndObject();
|
||||
void EndObject(bool endwarning = false);
|
||||
bool BeginArray(const char *name);
|
||||
void EndArray();
|
||||
void WriteObjects();
|
||||
unsigned GetSize(const char *group);
|
||||
const char *GetKey();
|
||||
const char *GetOutput(unsigned *len = nullptr);
|
||||
FCompressedBuffer GetCompressedOutput();
|
||||
FSerializer &Discard(const char *key);
|
||||
FSerializer &Args(const char *key, int *args, int *defargs, int special);
|
||||
FSerializer &Terrain(const char *key, int &terrain, int *def = nullptr);
|
||||
FSerializer &Sprite(const char *key, int32_t &spritenum, int32_t *def);
|
||||
|
@ -104,7 +115,7 @@ public:
|
|||
template<class T>
|
||||
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;
|
||||
}
|
||||
|
|
|
@ -174,9 +174,6 @@ void DThinker::SerializeAll(FArchive &arc, bool hubLoad)
|
|||
}
|
||||
else
|
||||
{
|
||||
// Prevent the constructor from inserting thinkers into a list.
|
||||
bSerialOverride = true;
|
||||
|
||||
try
|
||||
{
|
||||
arc << statcount;
|
||||
|
@ -213,11 +210,9 @@ void DThinker::SerializeAll(FArchive &arc, bool hubLoad)
|
|||
}
|
||||
catch (class CDoomError &)
|
||||
{
|
||||
bSerialOverride = false;
|
||||
DestroyAllThinkers();
|
||||
throw;
|
||||
}
|
||||
bSerialOverride = false;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
Loading…
Reference in a new issue