diff options
Diffstat (limited to 'source')
-rw-r--r-- | source/FastNBT.cpp | 212 | ||||
-rw-r--r-- | source/FastNBT.h | 58 | ||||
-rw-r--r-- | source/WSSAnvil.cpp | 163 | ||||
-rw-r--r-- | source/WSSAnvil.h | 4 |
4 files changed, 364 insertions, 73 deletions
diff --git a/source/FastNBT.cpp b/source/FastNBT.cpp index fa3edec52..9854fe3bc 100644 --- a/source/FastNBT.cpp +++ b/source/FastNBT.cpp @@ -17,7 +17,7 @@ ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-// cFastNBTParser:
+// cParsedNBT:
#define NEEDBYTES(N) \
if (m_Length - m_Pos < N) \
@@ -318,3 +318,213 @@ int cParsedNBT::FindTagByPath(int a_Tag, const AString & a_Path) const +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cFastNBTWriter:
+
+cFastNBTWriter::cFastNBTWriter(void) :
+ m_CurrentStack(0)
+{
+ m_Stack[0].m_Type = TAG_Compound;
+ m_Result.reserve(100 * 1024);
+ m_Result.push_back(TAG_Compound);
+ WriteString("", 0);
+}
+
+
+
+
+
+void cFastNBTWriter::BeginCompound(const AString & a_Name)
+{
+ if (m_CurrentStack >= MAX_STACK)
+ {
+ ASSERT(!"Stack overflow");
+ return;
+ }
+
+ TagCommon(a_Name, TAG_Compound);
+
+ ++m_CurrentStack;
+ m_Stack[m_CurrentStack].m_Type = TAG_Compound;
+}
+
+
+
+
+
+void cFastNBTWriter::EndCompound(void)
+{
+ ASSERT(m_CurrentStack > 0);
+ ASSERT(IsStackTopCompound());
+
+ m_Result.push_back(TAG_End);
+ --m_CurrentStack;
+}
+
+
+
+
+
+void cFastNBTWriter::BeginList(const AString & a_Name, eTagType a_ChildrenType)
+{
+ if (m_CurrentStack >= MAX_STACK)
+ {
+ ASSERT(!"Stack overflow");
+ return;
+ }
+
+ TagCommon(a_Name, TAG_List);
+
+ m_Result.push_back((char)a_ChildrenType);
+ m_Result.append(4, (char)0);
+
+ ++m_CurrentStack;
+ m_Stack[m_CurrentStack].m_Type = TAG_List;
+ m_Stack[m_CurrentStack].m_Pos = m_Result.size() - 4;
+ m_Stack[m_CurrentStack].m_Count = 0;
+}
+
+
+
+
+
+void cFastNBTWriter::EndList(void)
+{
+ ASSERT(m_CurrentStack > 0);
+ ASSERT(m_Stack[m_CurrentStack].m_Type == TAG_List);
+
+ // Update the list count:
+ *((int *)(m_Result.c_str() + m_Stack[m_CurrentStack].m_Pos)) = htonl(m_Stack[m_CurrentStack].m_Count);
+
+ --m_CurrentStack;
+}
+
+
+
+
+
+void cFastNBTWriter::AddByte(const AString & a_Name, unsigned char a_Value)
+{
+ TagCommon(a_Name, TAG_Byte);
+ m_Result.push_back(a_Value);
+}
+
+
+
+
+
+void cFastNBTWriter::AddShort(const AString & a_Name, Int16 a_Value)
+{
+ TagCommon(a_Name, TAG_Short);
+ Int16 Value = htons(a_Value);
+ m_Result.append((const char *)&Value, 2);
+}
+
+
+
+
+
+void cFastNBTWriter::AddInt(const AString & a_Name, Int32 a_Value)
+{
+ TagCommon(a_Name, TAG_Int);
+ Int32 Value = htonl(a_Value);
+ m_Result.append((const char *)&Value, 4);
+}
+
+
+
+
+
+void cFastNBTWriter::AddLong(const AString & a_Name, Int64 a_Value)
+{
+ TagCommon(a_Name, TAG_Long);
+ Int64 Value = HostToNetwork8(&a_Value);
+ m_Result.append((const char *)&Value, 8);
+}
+
+
+
+
+
+void cFastNBTWriter::AddFloat(const AString & a_Name, float a_Value)
+{
+ TagCommon(a_Name, TAG_Float);
+ Int32 Value = HostToNetwork4(&a_Value);
+ m_Result.append((const char *)&Value, 4);
+}
+
+
+
+
+
+void cFastNBTWriter::AddDouble(const AString & a_Name, double a_Value)
+{
+ TagCommon(a_Name, TAG_Double);
+ Int64 Value = HostToNetwork8(&a_Value);
+ m_Result.append((const char *)&Value, 8);
+}
+
+
+
+
+
+void cFastNBTWriter::AddString(const AString & a_Name, const AString & a_Value)
+{
+ TagCommon(a_Name, TAG_String);
+ Int16 len = htons((short)(a_Value.size()));
+ m_Result.append((const char *)&len, 2);
+ m_Result.append(a_Value.c_str(), a_Value.size());
+}
+
+
+
+
+
+void cFastNBTWriter::AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements)
+{
+ TagCommon(a_Name, TAG_ByteArray);
+ Int32 len = htonl(a_NumElements);
+ m_Result.append((const char *)&len, 4);
+ m_Result.append(a_Value, a_NumElements);
+}
+
+
+
+
+
+void cFastNBTWriter::AddIntArray(const AString & a_Name, const int * a_Value, size_t a_NumElements)
+{
+ TagCommon(a_Name, TAG_IntArray);
+ Int32 len = htonl(a_NumElements);
+ m_Result.append((const char *)&len, 2);
+ int * Elements = (int *)(m_Result.data() + m_Result.size());
+ m_Result.append(a_NumElements * 4, (char)0);
+ for (size_t i = 0; i < a_NumElements; i++)
+ {
+ Elements[i] = htonl(a_Value[i]);
+ }
+}
+
+
+
+
+void cFastNBTWriter::Finish(void)
+{
+ ASSERT(m_CurrentStack == 0);
+ m_Result.push_back(TAG_End);
+}
+
+
+
+
+
+void cFastNBTWriter::WriteString(const char * a_Data, short a_Length)
+{
+ Int16 Len = htons(a_Length);
+ m_Result.append((const char *)&Len, 2);
+ m_Result.append(a_Data, a_Length);
+}
+
+
+
+
diff --git a/source/FastNBT.h b/source/FastNBT.h index fd60fc671..a0fe7a863 100644 --- a/source/FastNBT.h +++ b/source/FastNBT.h @@ -214,19 +214,59 @@ public: void EndList(void);
void AddByte (const AString & a_Name, unsigned char a_Value);
- void AddShort (const AString & a_Name, unsigned char a_Value);
- void AddInt (const AString & a_Name, unsigned char a_Value);
- void AddLong (const AString & a_Name, unsigned char a_Value);
- void AddFloat (const AString & a_Name, unsigned char a_Value);
- void AddDouble (const AString & a_Name, unsigned char a_Value);
- void AddString (const AString & a_Name, unsigned char a_Value);
- void AddByteArray(const AString & a_Name, unsigned char a_Value);
- void AddIntArray (const AString & a_Name, unsigned char a_Value);
-
+ void AddShort (const AString & a_Name, Int16 a_Value);
+ void AddInt (const AString & a_Name, Int32 a_Value);
+ void AddLong (const AString & a_Name, Int64 a_Value);
+ void AddFloat (const AString & a_Name, float a_Value);
+ void AddDouble (const AString & a_Name, double a_Value);
+ void AddString (const AString & a_Name, const AString & a_Value);
+ void AddByteArray(const AString & a_Name, const char * a_Value, size_t a_NumElements);
+ void AddIntArray (const AString & a_Name, const int * a_Value, size_t a_NumElements);
+
+ void AddByteArray(const AString & a_Name, const AString & a_Value)
+ {
+ AddByteArray(a_Name, a_Value.data(), a_Value.size());
+ }
+
const AString & GetResult(void) const {return m_Result; }
+ void Finish(void);
+
protected:
+
+ struct sParent
+ {
+ int m_Type; // TAG_Compound or TAG_List
+ int m_Pos; // for TAG_List, the position of the list count
+ int m_Count; // for TAG_List, the element count
+ } ;
+
+ static const int MAX_STACK = 50; // Highliy doubtful that an NBT would be constructed this many levels deep
+
+ // These two fields emulate a stack. A raw array is used due to speed issues - no reallocations are allowed.
+ sParent m_Stack[MAX_STACK];
+ int m_CurrentStack;
+
AString m_Result;
+
+ bool IsStackTopCompound(void) const { return (m_Stack[m_CurrentStack].m_Type == TAG_Compound); }
+
+ void WriteString(const char * a_Data, short a_Length);
+
+ inline void TagCommon(const AString & a_Name, eTagType a_Type)
+ {
+ if (IsStackTopCompound())
+ {
+ // Compound: add the type and name:
+ m_Result.push_back((char)a_Type);
+ WriteString(a_Name.c_str(), a_Name.length());
+ }
+ else
+ {
+ // List: add to the counter
+ m_Stack[m_CurrentStack].m_Count++;
+ }
+ }
} ;
diff --git a/source/WSSAnvil.cpp b/source/WSSAnvil.cpp index e5400cf4c..f8e3abac3 100644 --- a/source/WSSAnvil.cpp +++ b/source/WSSAnvil.cpp @@ -41,11 +41,23 @@ class cNBTChunkSerializer : public cChunkDataSeparateCollector
{
public:
- cNBTChunkSerializer(cNBTList * a_Entities, cNBTList * a_TileEntities) :
- m_Entities(a_Entities),
- m_TileEntities(a_TileEntities)
+ cNBTChunkSerializer(cFastNBTWriter & a_Writer) :
+ m_Writer(a_Writer),
+ m_IsTagOpen(false),
+ m_HasHadEntity(false),
+ m_HasHadBlockEntity(false)
{
}
+
+
+ /// Close NBT tags that we've opened
+ void Finish(void)
+ {
+ if (m_IsTagOpen)
+ {
+ m_Writer.EndCompound();
+ }
+ }
protected:
@@ -58,37 +70,38 @@ protected: // TODO: Biomes
- // We need to save entities and blockentities into NBT
- cNBTList * m_Entities; // Tag where entities will be saved
- cNBTList * m_TileEntities; // Tag where block-entities will be saved
+ cFastNBTWriter & m_Writer;
+
+ bool m_IsTagOpen; // True if a tag has been opened in the callbacks and not yet closed.
+ bool m_HasHadEntity; // True if any Entity has already been received and processed
+ bool m_HasHadBlockEntity; // True if any BlockEntity has already been received and processed
- cNBTCompound * AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID)
+ void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID)
{
- cNBTCompound * res = new cNBTCompound(m_TileEntities);
- res->Add(new cNBTInt (res, "x", a_Entity->GetPosX()));
- res->Add(new cNBTInt (res, "y", a_Entity->GetPosY()));
- res->Add(new cNBTInt (res, "z", a_Entity->GetPosZ()));
- res->Add(new cNBTString(res, "id", a_EntityTypeID));
- return res;
+ m_Writer.AddInt ("x", a_Entity->GetPosX());
+ m_Writer.AddInt ("y", a_Entity->GetPosY());
+ m_Writer.AddInt ("z", a_Entity->GetPosZ());
+ m_Writer.AddString("id", a_EntityTypeID);
}
- void AddItem(cNBTList * a_Items, cItem * a_Item, int a_Slot)
+ void AddItem(cItem * a_Item, int a_Slot)
{
- cNBTCompound * Tag = new cNBTCompound(a_Items);
- Tag->Add(new cNBTShort(Tag, "id", a_Item->m_ItemID));
- Tag->Add(new cNBTShort(Tag, "Damage", a_Item->m_ItemHealth));
- Tag->Add(new cNBTByte (Tag, "Count", a_Item->m_ItemCount));
- Tag->Add(new cNBTByte (Tag, "Slot", a_Slot));
+ m_Writer.BeginCompound("");
+ m_Writer.AddShort("id", a_Item->m_ItemID);
+ m_Writer.AddShort("Damage", a_Item->m_ItemHealth);
+ m_Writer.AddByte ("Count", a_Item->m_ItemCount);
+ m_Writer.AddByte ("Slot", a_Slot);
+ m_Writer.EndCompound();
}
void AddChestEntity(cChestEntity * a_Entity)
{
- cNBTCompound * Base = AddBasicTileEntity(a_Entity, "chest");
- cNBTList * Items = new cNBTList(Base, "Items", cNBTTag::TAG_Compound);
- Base->Add(Items);
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "chest");
+ m_Writer.BeginList("Items", TAG_Compound);
for (int i = 0; i < cChestEntity::c_ChestHeight * cChestEntity::c_ChestWidth; i++)
{
cItem * Item = a_Entity->GetSlot(i);
@@ -96,8 +109,10 @@ protected: {
continue;
}
- AddItem(Items, Item, i);
+ AddItem(Item, i);
}
+ m_Writer.EndList();
+ m_Writer.EndCompound();
}
@@ -109,6 +124,20 @@ protected: virtual void BlockEntity(cBlockEntity * a_Entity)
{
+ if (m_IsTagOpen)
+ {
+ if (!m_HasHadBlockEntity)
+ {
+ m_Writer.EndCompound();
+ m_Writer.BeginCompound("TileEntities");
+ }
+ }
+ else
+ {
+ m_Writer.BeginCompound("TileEntities");
+ }
+ m_IsTagOpen = true;
+
// Add tile-entity into NBT:
switch (a_Entity->GetBlockType())
{
@@ -118,6 +147,7 @@ protected: ASSERT(!"Unhandled block entity saved into Anvil");
}
}
+ m_HasHadBlockEntity = true;
}
} ; // class cNBTChunkSerializer
@@ -136,19 +166,23 @@ cWSSAnvil::cWSSAnvil(cWorld * a_World) : Printf(fnam, "%s/level.dat", a_World->GetName().c_str());
if (!cFile::Exists(fnam))
{
- std::auto_ptr<cNBTCompound> Root(new cNBTCompound(NULL));
- cNBTCompound * Data = new cNBTCompound(Root.get());
- Root->Add(Data);
- Data->Add(new cNBTInt(Data, "SpawnX", (int)(a_World->GetSpawnX())));
- Data->Add(new cNBTInt(Data, "SpawnY", (int)(a_World->GetSpawnY())));
- Data->Add(new cNBTInt(Data, "SpawnZ", (int)(a_World->GetSpawnZ())));
- AString Uncompressed;
- cNBTSerializer::Serialize(Root.get(), Uncompressed);
+ cFastNBTWriter Writer;
+ Writer.BeginCompound("");
+ Writer.AddInt("SpawnX", (int)(a_World->GetSpawnX()));
+ Writer.AddInt("SpawnY", (int)(a_World->GetSpawnY()));
+ Writer.AddInt("SpawnZ", (int)(a_World->GetSpawnZ()));
+ Writer.EndCompound();
+ Writer.Finish();
+
+ #ifdef _DEBUG
+ cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size());
+ ASSERT(TestParse.IsValid());
+ #endif // _DEBUG
gzFile gz = gzopen(fnam.c_str(), "wb");
if (gz != NULL)
{
- gzwrite(gz, Uncompressed.data(), Uncompressed.size());
+ gzwrite(gz, Writer.GetResult().data(), Writer.GetResult().size());
}
gzclose(gz);
}
@@ -199,6 +233,14 @@ bool cWSSAnvil::SaveChunk(const cChunkCoords & a_Chunk) return false;
}
+ // DEBUG: How fast is it?
+ DWORD BeginTick = GetTickCount();
+ for (int i = 0; i < 1000; i++)
+ {
+ SaveChunkToData(a_Chunk, ChunkData);
+ }
+ LOGINFO("1000* SaveChunk took %d ticks.", GetTickCount() - BeginTick);
+
// Everything successful
return true;
}
@@ -323,14 +365,19 @@ bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & bool cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data)
{
- std::auto_ptr<cNBTTree> Tree(SaveChunkToNBT(a_Chunk));
- if (Tree.get() == NULL)
+ cFastNBTWriter Writer;
+ if (!SaveChunkToNBT(a_Chunk, Writer))
{
return false;
}
- AString Uncompressed;
- cNBTSerializer::Serialize(Tree.get(), Uncompressed);
- CompressString(Uncompressed.data(), Uncompressed.size(), a_Data);
+ Writer.Finish();
+
+ #ifdef _DEBUG
+ cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size());
+ ASSERT(TestParse.IsValid());
+ #endif // _DEBUG
+
+ CompressString(Writer.GetResult().data(), Writer.GetResult().size(), a_Data);
return true;
}
@@ -474,44 +521,38 @@ void cWSSAnvil::CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString & -cNBTTag * cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk)
+bool cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer)
{
- std::auto_ptr<cNBTCompound> res(new cNBTCompound(NULL));
- cNBTCompound * Level = new cNBTCompound(res.get(), "Level");
- res->Add(Level);
- cNBTList * Entities = new cNBTList(Level, "Entities", cNBTTag::TAG_Compound);
- Level->Add(Entities);
- cNBTList * TileEntities = new cNBTList(Level, "TileEntities", cNBTTag::TAG_Compound);
- Level->Add(TileEntities);
- cNBTChunkSerializer Serializer(Entities, TileEntities);
+ a_Writer.BeginCompound("Level");
+ a_Writer.AddInt("xPos", a_Chunk.m_ChunkX);
+ a_Writer.AddInt("zPos", a_Chunk.m_ChunkZ);
+ cNBTChunkSerializer Serializer(a_Writer);
if (!m_World->GetChunkData(a_Chunk.m_ChunkX, a_Chunk.m_ChunkY, a_Chunk.m_ChunkZ, Serializer))
{
- return NULL;
+ return false;
}
-
- Level->Add(new cNBTInt(Level, "xPos", a_Chunk.m_ChunkX));
- Level->Add(new cNBTInt(Level, "zPos", a_Chunk.m_ChunkZ));
+ Serializer.Finish(); // Close NBT tags
// TODO: Save biomes:
// Level->Add(new cNBTByteArray(Level, "Biomes", AString(Serializer.m_Biomes, sizeof(Serializer.m_Biomes));
// Save blockdata:
- cNBTList * Sections = new cNBTList(Level, "Sections", cNBTTag::TAG_Compound);
- Level->Add(Sections);
+ a_Writer.BeginList("Sections", TAG_Compound);
int SliceSizeBlock = cChunkDef::Width * cChunkDef::Width * 16;
int SliceSizeNibble = SliceSizeBlock / 2;
for (int Y = 0; Y < 16; Y++)
{
- cNBTCompound * Slice = new cNBTCompound(Sections);
- Sections->Add(Slice);
- Slice->Add(new cNBTByteArray(Slice, "Blocks", AString(Serializer.m_BlockTypes + Y * SliceSizeBlock, SliceSizeBlock)));
- Slice->Add(new cNBTByteArray(Slice, "Data", AString(Serializer.m_BlockMetas + Y * SliceSizeNibble, SliceSizeNibble)));
- Slice->Add(new cNBTByteArray(Slice, "SkyLight", AString(Serializer.m_BlockSkyLight + Y * SliceSizeNibble, SliceSizeNibble)));
- Slice->Add(new cNBTByteArray(Slice, "BlockLight", AString(Serializer.m_BlockLight + Y * SliceSizeNibble, SliceSizeNibble)));
- Slice->Add(new cNBTByte(Slice, "Y", Y));
+ a_Writer.BeginCompound("");
+ a_Writer.AddByteArray("Blocks", Serializer.m_BlockTypes + Y * SliceSizeBlock, SliceSizeBlock);
+ a_Writer.AddByteArray("Data", Serializer.m_BlockMetas + Y * SliceSizeNibble, SliceSizeNibble);
+ a_Writer.AddByteArray("SkyLight", Serializer.m_BlockSkyLight + Y * SliceSizeNibble, SliceSizeNibble);
+ a_Writer.AddByteArray("BlockLight", Serializer.m_BlockLight + Y * SliceSizeNibble, SliceSizeNibble);
+ a_Writer.AddByte("Y", Y);
+ a_Writer.EndCompound();
}
-
- return res.release();
+ a_Writer.EndList(); // "Sections"
+ a_Writer.EndCompound(); // "Level"
+ return true;
}
diff --git a/source/WSSAnvil.h b/source/WSSAnvil.h index 58259f0e8..85b4fcaf1 100644 --- a/source/WSSAnvil.h +++ b/source/WSSAnvil.h @@ -102,8 +102,8 @@ protected: /// Loads the chunk from NBT data (no locking needed)
bool LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT);
- /// Saves the chunk into NBT data; returns NULL for failure
- cNBTTag * SaveChunkToNBT(const cChunkCoords & a_Chunk);
+ /// Saves the chunk into NBT data using a_Writer; returns true on success
+ bool SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer);
/// Loads the chunk's entities from NBT data (a_Tag is the Level\\Entities list tag; may be -1)
void LoadEntitiesFromNBT(cEntityList & a_Entitites, const cParsedNBT & a_NBT, int a_Tag);
|