From 35c8bc4c5807af41a37cc901dcbbfee7ab73f7bf Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Mon, 7 May 2012 20:22:04 +0000 Subject: Fast NBT Parser (loading a chunk is now about 10 times faster) git-svn-id: http://mc-server.googlecode.com/svn/trunk@481 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- VC2008/MCServer.vcproj | 24 ++- VC2010/MCServer.vcxproj | 2 + VC2010/MCServer.vcxproj.filters | 41 ++++- source/FastNBT.cpp | 320 ++++++++++++++++++++++++++++++++++++++++ source/FastNBT.h | 234 +++++++++++++++++++++++++++++ source/Globals.h | 9 ++ source/NBT.h | 8 - source/WSSAnvil.cpp | 150 ++++++++++--------- source/WSSAnvil.h | 18 ++- 9 files changed, 702 insertions(+), 104 deletions(-) create mode 100644 source/FastNBT.cpp create mode 100644 source/FastNBT.h diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj index 4b8bcdbfb..146d735e1 100644 --- a/VC2008/MCServer.vcproj +++ b/VC2008/MCServer.vcproj @@ -572,14 +572,6 @@ RelativePath="..\source\MersenneTwister.h" > - - - - @@ -1629,6 +1621,22 @@ + + + + + + + + diff --git a/VC2010/MCServer.vcxproj b/VC2010/MCServer.vcxproj index d6ddb3b1c..ca7323f31 100644 --- a/VC2010/MCServer.vcxproj +++ b/VC2010/MCServer.vcxproj @@ -390,6 +390,7 @@ + Create Create @@ -575,6 +576,7 @@ + diff --git a/VC2010/MCServer.vcxproj.filters b/VC2010/MCServer.vcxproj.filters index 02421c803..dcbf530a9 100644 --- a/VC2010/MCServer.vcxproj.filters +++ b/VC2010/MCServer.vcxproj.filters @@ -445,6 +445,9 @@ {4b3b7b43-8e8b-4823-b112-2259fdfff7d3} + + {038cf4bd-108e-44e2-bdcb-9d48fb26676e} + @@ -887,20 +890,31 @@ - - Simulator\cSimulator\cRedstoneSimulator - - Packets + + Storage + + + Storage + + + Storage + + + Storage + + + Storage + @@ -1379,23 +1393,34 @@ - - Simulator\cSimulator\cRedstoneSimulator - - Packets + + Storage + + + Storage + + + Storage + + + Storage + + + Storage + diff --git a/source/FastNBT.cpp b/source/FastNBT.cpp new file mode 100644 index 000000000..fa3edec52 --- /dev/null +++ b/source/FastNBT.cpp @@ -0,0 +1,320 @@ + +// FastNBT.cpp + +// Implements the fast NBT parser and writer + +#include "Globals.h" +#include "FastNBT.h" + + + + + +#define RETURN_FALSE_IF_FALSE(X) do { if (!X) return false; } while (0) + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cFastNBTParser: + +#define NEEDBYTES(N) \ + if (m_Length - m_Pos < N) \ + { \ + return false; \ + } + + + + + +cParsedNBT::cParsedNBT(const char * a_Data, int a_Length) : + m_Data(a_Data), + m_Length(a_Length), + m_Pos(0) +{ + m_IsValid = Parse(); +} + + + + + +bool cParsedNBT::Parse(void) +{ + if (m_Length < 3) + { + // Data too short + return false; + } + if (m_Data[0] != TAG_Compound) + { + // The top-level tag must be a Compound + return false; + } + + m_Tags.reserve(200); + + m_Tags.push_back(cFastNBTTag(TAG_Compound, -1)); + + m_Pos = 1; + + RETURN_FALSE_IF_FALSE(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength)); + RETURN_FALSE_IF_FALSE(ReadCompound()); + + return true; +} + + + + + +bool cParsedNBT::ReadString(int & a_StringStart, int & a_StringLen) +{ + NEEDBYTES(2); + a_StringStart = m_Pos + 2; + a_StringLen = ntohs(*((short *)(m_Data + m_Pos))); + if (a_StringLen < 0) + { + // Invalid string length + return false; + } + m_Pos += 2 + a_StringLen; + return true; +} + + + + + +bool cParsedNBT::ReadCompound(void) +{ + // Reads the latest tag as a compound + int ParentIdx = m_Tags.size() - 1; + int PrevSibling = -1; + while (true) + { + NEEDBYTES(1); + eTagType TagType = (eTagType)(m_Data[m_Pos]); + m_Pos++; + if (TagType == TAG_End) + { + break; + } + m_Tags.push_back(cFastNBTTag(TagType, ParentIdx, PrevSibling)); + if (PrevSibling >= 0) + { + m_Tags[PrevSibling].m_NextSibling = m_Tags.size() - 1; + } + else + { + m_Tags[ParentIdx].m_FirstChild = m_Tags.size() - 1; + } + PrevSibling = m_Tags.size() - 1; + RETURN_FALSE_IF_FALSE(ReadString(m_Tags.back().m_NameStart, m_Tags.back().m_NameLength)); + RETURN_FALSE_IF_FALSE(ReadTag()); + } // while (true) + m_Tags[ParentIdx].m_LastChild = PrevSibling; + return true; +} + + + + + +bool cParsedNBT::ReadList(eTagType a_ChildrenType) +{ + // Reads the latest tag as a list of items of type a_ChildrenType + + // Read the count: + NEEDBYTES(4); + int Count = ntohl(*((int *)(m_Data + m_Pos))); + m_Pos += 4; + if (Count < 0) + { + return false; + } + + // Read items: + int ParentIdx = m_Tags.size() - 1; + int PrevSibling = -1; + for (int i = 0; i < Count; i++) + { + m_Tags.push_back(cFastNBTTag(a_ChildrenType, ParentIdx, PrevSibling)); + if (PrevSibling >= 0) + { + m_Tags[PrevSibling].m_NextSibling = m_Tags.size() - 1; + } + else + { + m_Tags[ParentIdx].m_FirstChild = m_Tags.size() - 1; + } + PrevSibling = m_Tags.size() - 1; + RETURN_FALSE_IF_FALSE(ReadTag()); + } // for (i) + m_Tags[ParentIdx].m_LastChild = PrevSibling; + return true; +} + + + + + +#define CASE_SIMPLE_TAG(TAGTYPE, LEN) \ + case TAG_##TAGTYPE: \ + { \ + NEEDBYTES(LEN); \ + Tag.m_DataStart = m_Pos; \ + Tag.m_DataLength = LEN; \ + m_Pos += LEN; \ + return true; \ + } + +bool cParsedNBT::ReadTag(void) +{ + cFastNBTTag & Tag = m_Tags.back(); + switch (Tag.m_Type) + { + CASE_SIMPLE_TAG(Byte, 1) + CASE_SIMPLE_TAG(Short, 2) + CASE_SIMPLE_TAG(Int, 4) + CASE_SIMPLE_TAG(Long, 8) + CASE_SIMPLE_TAG(Float, 4) + CASE_SIMPLE_TAG(Double, 8) + + case TAG_String: + { + return ReadString(Tag.m_DataStart, Tag.m_DataLength); + } + + case TAG_ByteArray: + { + NEEDBYTES(4); + int len = ntohl(*((int *)(m_Data + m_Pos))); + m_Pos += 4; + if (len < 0) + { + // Invalid length + return false; + } + NEEDBYTES(len); + Tag.m_DataLength = len; + Tag.m_DataStart = m_Pos; + m_Pos += len; + return true; + } + + case TAG_List: + { + NEEDBYTES(1); + eTagType ItemType = (eTagType)m_Data[m_Pos]; + m_Pos++; + RETURN_FALSE_IF_FALSE(ReadList(ItemType)); + return true; + } + + case TAG_Compound: + { + RETURN_FALSE_IF_FALSE(ReadCompound()); + return true; + } + + case TAG_IntArray: + { + NEEDBYTES(4); + int len = ntohl(*((int *)(m_Data + m_Pos))); + m_Pos += 4; + if (len < 0) + { + // Invalid length + return false; + } + len *= 4; + NEEDBYTES(len); + Tag.m_DataLength = len; + Tag.m_DataStart = m_Pos; + m_Pos += len; + return true; + } + + default: + { + ASSERT(!"Unhandled NBT tag type"); + return false; + } + } // switch (iType) +} + +#undef CASE_SIMPLE_TAG + + + + + +int cParsedNBT::FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength) const +{ + if (a_Tag < 0) + { + return -1; + } + if (m_Tags[a_Tag].m_Type != TAG_Compound) + { + return -1; + } + + if (a_NameLength == 0) + { + a_NameLength = strlen(a_Name); + } + for (int Child = m_Tags[a_Tag].m_FirstChild; Child != -1; Child = m_Tags[Child].m_NextSibling) + { + if ( + (m_Tags[Child].m_NameLength == a_NameLength) && + (memcmp(m_Data + m_Tags[Child].m_NameStart, a_Name, a_NameLength) == 0) + ) + { + return Child; + } + } // for Child - children of a_Tag + return -1; +} + + + + + +int cParsedNBT::FindTagByPath(int a_Tag, const AString & a_Path) const +{ + if (a_Tag < 0) + { + return -1; + } + size_t Begin = 0; + size_t Length = a_Path.length(); + int Tag = a_Tag; + for (size_t i = 0; i < Length; i++) + { + if (a_Path[i] != '\\') + { + continue; + } + Tag = FindChildByName(Tag, a_Path.c_str() + Begin, i - Begin - 1); + if (Tag < 0) + { + return -1; + } + Begin = i + 1; + } // for i - a_Path[] + + if (Begin < Length) + { + Tag = FindChildByName(Tag, a_Path.c_str() + Begin, Length - Begin); + } + return Tag; +} + + + + + diff --git a/source/FastNBT.h b/source/FastNBT.h new file mode 100644 index 000000000..fd60fc671 --- /dev/null +++ b/source/FastNBT.h @@ -0,0 +1,234 @@ + +// FastNBT.h + +// Interfaces to the fast NBT parser and writer + +/* +The fast parser parses the data into a vector of cFastNBTTag structures. These structures describe the NBT tree, +but themselves are allocated in a vector, thus minimizing reallocation. +The structures have a minimal constructor, setting all member "pointers" to "invalid". + +The fast writer doesn't need a NBT tree structure built beforehand, it is commanded to open, append and close tags +(just like XML); it keeps the internal tag stack and reports errors in usage. +It directly outputs a string containing the serialized NBT data. +*/ + + + + + +#pragma once + +#include "Endianness.h" + + + + + +enum eTagType +{ + TAG_Min = 0, // The minimum value for a tag type + TAG_End = 0, + TAG_Byte = 1, + TAG_Short = 2, + TAG_Int = 3, + TAG_Long = 4, + TAG_Float = 5, + TAG_Double = 6, + TAG_ByteArray = 7, + TAG_String = 8, + TAG_List = 9, + TAG_Compound = 10, + TAG_IntArray = 11, + TAG_Max = 11, // The maximum value for a tag type +} ; + + + + + +/** This structure is used for all NBT tags. +It contains indices to the parent array of tags, building the NBT tree this way. +Also contains indices into the data stream being parsed, used for values; +NO dynamically allocated memory is used! +Structure (all with the tree structure it describes) supports moving in memory (std::vector reallocation) +*/ +struct cFastNBTTag +{ +public: + + eTagType m_Type; + + // The following members are indices into the data stream. m_DataLength == 0 if no data available + // They must not be pointers, because the datastream may be copied into another AString object in the meantime. + int m_NameStart; + int m_NameLength; + int m_DataStart; + int m_DataLength; + + // The following members are indices into the array returned; -1 if not valid + // They must not be pointers, because pointers would not survive std::vector reallocation + int m_Parent; + int m_PrevSibling; + int m_NextSibling; + int m_FirstChild; + int m_LastChild; + + cFastNBTTag(eTagType a_Type, int a_Parent) : + m_Type(a_Type), + m_NameLength(0), + m_DataLength(0), + m_Parent(a_Parent), + m_PrevSibling(-1), + m_NextSibling(-1), + m_FirstChild(-1), + m_LastChild(-1) + { + } + + cFastNBTTag(eTagType a_Type, int a_Parent, int a_PrevSibling) : + m_Type(a_Type), + m_NameLength(0), + m_DataLength(0), + m_Parent(a_Parent), + m_PrevSibling(a_PrevSibling), + m_NextSibling(-1), + m_FirstChild(-1), + m_LastChild(-1) + { + } +} ; + + + + + +/** Parses and contains the parsed data +Also implements data accessor functions for tree traversal and value getters +The data pointer passed in the constructor is assumed to be valid throughout the object's life. Care must be taken not to initialize from a temporary. +*/ +class cParsedNBT +{ +public: + cParsedNBT(const char * a_Data, int a_Length); + + bool IsValid(void) const {return m_IsValid; } + + int GetRoot(void) const {return 0; } + int GetFirstChild (int a_Tag) const { return m_Tags[a_Tag].m_FirstChild; } + int GetLastChild (int a_Tag) const { return m_Tags[a_Tag].m_LastChild; } + int GetNextSibling(int a_Tag) const { return m_Tags[a_Tag].m_NextSibling; } + int GetPrevSibling(int a_Tag) const { return m_Tags[a_Tag].m_PrevSibling; } + int GetDataLength (int a_Tag) const { return m_Tags[a_Tag].m_DataLength; } + + const char * GetData(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type != TAG_List); + ASSERT(m_Tags[a_Tag].m_Type != TAG_Compound); + return m_Data + m_Tags[a_Tag].m_DataStart; + } + + int FindChildByName(int a_Tag, const AString & a_Name) const + { + return FindChildByName(a_Tag, a_Name.c_str(), a_Name.length()); + } + + int FindChildByName(int a_Tag, const char * a_Name, size_t a_NameLength = 0) const; + int FindTagByPath (int a_Tag, const AString & a_Path) const; + + eTagType GetType(int a_Tag) const { return m_Tags[a_Tag].m_Type; } + + /// Returns the children type for a list tag; undefined on other tags. If list empty, returns TAG_End + eTagType GetChildrenType(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_List); + return (m_Tags[a_Tag].m_FirstChild < 0) ? TAG_End : m_Tags[m_Tags[a_Tag].m_FirstChild].m_Type; + } + + inline unsigned char GetByte(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Byte); + return (unsigned char)(m_Data[m_Tags[a_Tag].m_DataStart]); + } + + inline Int16 GetShort(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Short); + return ntohs(*((Int16 *)(m_Data + m_Tags[a_Tag].m_DataStart))); + } + + inline Int32 GetInt(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Int); + return ntohl(*((Int32 *)(m_Data + m_Tags[a_Tag].m_DataStart))); + } + + inline Int64 GetLong(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Long); + return NetworkToHostLong8(m_Data + m_Tags[a_Tag].m_DataStart); + } + + inline float GetFloat(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Float); + Int32 tmp = ntohl(*((Int32 *)(m_Data + m_Tags[a_Tag].m_DataStart))); + return *((float *)&tmp); + } + + inline double GetDouble(int a_Tag) const + { + ASSERT(m_Tags[a_Tag].m_Type == TAG_Double); + return NetworkToHostDouble8(m_Data + m_Tags[a_Tag].m_DataStart); + } + +protected: + const char * m_Data; + int m_Length; + std::vector m_Tags; + bool m_IsValid; // True if parsing succeeded + + // Used while parsing: + int m_Pos; + + bool Parse(void); + bool ReadString(int & a_StringStart, int & a_StringLen); // Reads a simple string (2 bytes length + data), sets the string descriptors + bool ReadCompound(void); // Reads the latest tag as a compound + bool ReadList(eTagType a_ChildrenType); // Reads the latest tag as a list of items of type a_ChildrenType + bool ReadTag(void); // Reads the latest tag, depending on its m_Type setting +} ; + + + + + +class cFastNBTWriter +{ +public: + cFastNBTWriter(void); + + void BeginCompound(const AString & a_Name); + void EndCompound(void); + + void BeginList(const AString & a_Name, eTagType a_ChildrenType); + 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); + + const AString & GetResult(void) const {return m_Result; } + +protected: + AString m_Result; +} ; + + + + diff --git a/source/Globals.h b/source/Globals.h index 99ef55444..3a9c6e9db 100644 --- a/source/Globals.h +++ b/source/Globals.h @@ -65,6 +65,15 @@ +// Integral types with predefined sizes: +typedef long long Int64; +typedef int Int32; +typedef short Int16; + + + + + // A macro to disallow the copy constructor and operator= functions // This should be used in the private: declarations for any class that shouldn't allow copying itself #define DISALLOW_COPY_AND_ASSIGN(TypeName) \ diff --git a/source/NBT.h b/source/NBT.h index f63e1fea1..39b3c7252 100644 --- a/source/NBT.h +++ b/source/NBT.h @@ -13,14 +13,6 @@ -typedef long long Int64; -typedef int Int32; -typedef short Int16; - - - - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Representation classes: diff --git a/source/WSSAnvil.cpp b/source/WSSAnvil.cpp index 1b916a63e..e5400cf4c 100644 --- a/source/WSSAnvil.cpp +++ b/source/WSSAnvil.cpp @@ -15,6 +15,7 @@ #include "cEntity.h" #include "cBlockEntity.h" #include "cMakeDir.h" +#include "FastNBT.h" @@ -143,6 +144,7 @@ cWSSAnvil::cWSSAnvil(cWorld * a_World) : Data->Add(new cNBTInt(Data, "SpawnZ", (int)(a_World->GetSpawnZ()))); AString Uncompressed; cNBTSerializer::Serialize(Root.get(), Uncompressed); + gzFile gz = gzopen(fnam.c_str(), "wb"); if (gz != NULL) { @@ -304,14 +306,15 @@ bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const AString & } // Parse the NBT data: - std::auto_ptr Tree(cNBTParser::Parse(Uncompressed, strm.total_out)); - if (Tree.get() == NULL) + cParsedNBT NBT(Uncompressed, strm.total_out); + if (!NBT.IsValid()) { + // NBT Parsing failed return false; } // Load the data from NBT: - return LoadChunkFromNBT(a_Chunk, *Tree.get()); + return LoadChunkFromNBT(a_Chunk, NBT); } @@ -335,7 +338,7 @@ bool cWSSAnvil::SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data) -bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, cNBTTag & a_NBT) +bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT & a_NBT) { // The data arrays, in MCA-native y/z/x ordering (will be reordered for the final chunk data) BLOCKTYPE BlockData[cChunkDef::BlockDataSize]; @@ -347,49 +350,41 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, cNBTTag & a_NBT) memset(SkyLight, 0xff, cChunkDef::NumBlocks / 2); // By default, data not present in the NBT means air, which means full skylight // Load the blockdata, blocklight and skylight: - cNBTList * Sections = (cNBTList *)a_NBT.FindChildByPath("Level\\Sections"); - if ((Sections == NULL) || (Sections->GetType() != cNBTTag::TAG_List) || (Sections->GetChildrenType() != cNBTTag::TAG_Compound)) + int Level = a_NBT.FindChildByName(0, "Level"); + if (Level < 0) { return false; } - const cNBTTags & LevelSections = Sections->GetChildren(); - for (cNBTTags::const_iterator itr = LevelSections.begin(); itr != LevelSections.end(); ++itr) + int Sections = a_NBT.FindChildByName(Level, "Sections"); + if ((Sections < 0) || (a_NBT.GetType(Sections) != TAG_List) || (a_NBT.GetChildrenType(Sections) != TAG_Compound)) + { + return false; + } + for (int Child = a_NBT.GetFirstChild(Sections); Child >= 0; Child = a_NBT.GetNextSibling(Child)) { int y = 0; - cNBTByte * SectionY = (cNBTByte *)((*itr)->FindChildByName("Y")); - if ((SectionY == NULL) || (SectionY->GetType() != cNBTTag::TAG_Byte) || (SectionY->m_Value < 0) || (SectionY->m_Value > 15)) + int SectionY = a_NBT.FindChildByName(Child, "Y"); + if ((SectionY < 0) || (a_NBT.GetType(SectionY) != TAG_Byte)) { continue; } - y = SectionY->m_Value; - cNBTByteArray * baBlocks = (cNBTByteArray *)((*itr)->FindChildByName("Blocks")); - if ((baBlocks != NULL) && (baBlocks->GetType() == cNBTTag::TAG_ByteArray) && (baBlocks->m_Value.size() == 4096)) - { - memcpy(&(BlockData[y * 4096]), baBlocks->m_Value.data(), 4096); - } - cNBTByteArray * baMetaData = (cNBTByteArray *)((*itr)->FindChildByName("Data")); - if ((baMetaData != NULL) && (baMetaData->GetType() == cNBTTag::TAG_ByteArray) && (baMetaData->m_Value.size() == 2048)) - { - memcpy(&(MetaData[y * 2048]), baMetaData->m_Value.data(), 2048); - } - cNBTByteArray * baSkyLight = (cNBTByteArray *)((*itr)->FindChildByName("SkyLight")); - if ((baSkyLight != NULL) && (baSkyLight->GetType() == cNBTTag::TAG_ByteArray) && (baSkyLight->m_Value.size() == 2048)) - { - memcpy(&(SkyLight[y * 2048]), baSkyLight->m_Value.data(), 2048); - } - cNBTByteArray * baBlockLight = (cNBTByteArray *)((*itr)->FindChildByName("BlockLight")); - if ((baBlockLight != NULL) && (baBlockLight->GetType() == cNBTTag::TAG_ByteArray) && (baBlockLight->m_Value.size() == 2048)) + y = a_NBT.GetByte(SectionY); + if ((y < 0) || (y > 15)) { - memcpy(&(BlockLight[y * 2048]), baBlockLight->m_Value.data(), 2048); + continue; } + CopyNBTData(a_NBT, Child, "Blocks", &(BlockData[y * 4096]), 4096); + CopyNBTData(a_NBT, Child, "Data", &(MetaData[y * 2048]), 2048); + CopyNBTData(a_NBT, Child, "SkyLight", &(SkyLight[y * 2048]), 2048); + CopyNBTData(a_NBT, Child, "BlockLight", &(BlockLight[y * 2048]), 2048); } // for itr - LevelSections[] cEntityList Entities; cBlockEntityList BlockEntities; // Load the entities from NBT: - LoadEntitiesFromNBT (Entities, (cNBTList *)(a_NBT.FindChildByPath("Level\\Entities"))); - LoadBlockEntitiesFromNBT(BlockEntities, (cNBTList *)(a_NBT.FindChildByPath("Level\\TileEntities"))); + LoadEntitiesFromNBT (Entities, a_NBT, a_NBT.FindChildByName(Level, "Entities")); + LoadBlockEntitiesFromNBT(BlockEntities, a_NBT, a_NBT.FindChildByName(Level, "TileEntities")); #if (AXIS_ORDER == AXIS_ORDER_YZX) // Reorder the chunk data - walk the MCA-formatted data sequentially and copy it into the right place in the ChunkData: @@ -466,6 +461,18 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, cNBTTag & a_NBT) +void cWSSAnvil::CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString & a_ChildName, char * a_Destination, int a_Length) +{ + int Child = a_NBT.FindChildByName(a_Tag, a_ChildName); + if ((Child >= 0) && (a_NBT.GetType(Child) == TAG_ByteArray) && (a_NBT.GetDataLength(Child) == a_Length)) + { + memcpy(a_Destination, a_NBT.GetData(Child), a_Length); + } +} + + + + cNBTTag * cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk) { @@ -511,7 +518,7 @@ cNBTTag * cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk) -void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entitites, const cNBTList * a_NBT) +void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entitites, const cParsedNBT & a_NBT, int a_TagIdx) { // TODO: Load the entities } @@ -520,80 +527,77 @@ void cWSSAnvil::LoadEntitiesFromNBT(cEntityList & a_Entitites, const cNBTList * -void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, const cNBTList * a_NBT) +void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) { - if ((a_NBT == NULL) || (a_NBT->GetType() != cNBTTag::TAG_List)) + if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List)) { return; } - const cNBTTags & Tags = a_NBT->GetChildren(); - for (cNBTTags::const_iterator itr = Tags.begin(); itr != Tags.end(); ++itr) + for (int Child = a_NBT.GetFirstChild(a_TagIdx); Child != -1; Child = a_NBT.GetNextSibling(Child)) { - if ((*itr)->GetType() != cNBTTag::TAG_Compound) + if (a_NBT.GetType(Child) != TAG_Compound) { continue; } - cNBTString * sID = (cNBTString *)((*itr)->FindChildByName("id")); - if (sID == NULL) + int sID = a_NBT.FindChildByName(Child, "id"); + if (sID < 0) { continue; } - if (sID->m_Value == "Chest") + if (strncmp(a_NBT.GetData(sID), "Chest", a_NBT.GetDataLength(sID)) == 0) { - LoadChestFromNBT(a_BlockEntities, (cNBTCompound *)(*itr)); + LoadChestFromNBT(a_BlockEntities, a_NBT, Child); } // TODO: Other block entities - } // for itr - Tags[] + } // for Child - tag children } -void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cNBTCompound * a_NBT) +void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx) { - ASSERT(a_NBT != NULL); - ASSERT(a_NBT->GetType() == cNBTTag::TAG_Compound); + ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound); int x, y, z; - if (!GetBlockEntityNBTPos(a_NBT, x, y, z)) + if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z)) { return; } - cNBTList * Items = (cNBTList *)(a_NBT->FindChildByName("Items")); - if ((Items == NULL) || (Items->GetType() != cNBTTag::TAG_List)) + int Items = a_NBT.FindChildByName(a_TagIdx, "Items"); + if ((Items < 0) || (a_NBT.GetType(Items) != TAG_List)) { - return; // Make it an empty chest + return; // Make it an empty chest - the chunk loader will provide an empty cChestEntity for this } std::auto_ptr Chest(new cChestEntity(x, y, z, m_World)); - const cNBTTags & ItemDefs = Items->GetChildren(); - for (cNBTTags::const_iterator itr = ItemDefs.begin(); itr != ItemDefs.end(); ++itr) + for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child)) { - cNBTByte * Slot = (cNBTByte *)((*itr)->FindChildByName("Slot")); - if ((Slot == NULL) || (Slot->GetType() != cNBTTag::TAG_Byte)) + int Slot = a_NBT.FindChildByName(Child, "Slot"); + if ((Slot < 0) || (a_NBT.GetType(Slot) != TAG_Byte)) { continue; } cItem Item; - cNBTShort * ID = (cNBTShort *)((*itr)->FindChildByName("id")); - if ((ID == NULL) || (ID->GetType() != cNBTTag::TAG_Short)) + int ID = a_NBT.FindChildByName(Child, "id"); + if ((ID < 0) || (a_NBT.GetType(ID) != TAG_Short)) { continue; } - Item.m_ItemID = (ENUM_ITEM_ID)(ID->m_Value); - cNBTShort * Damage = (cNBTShort *)((*itr)->FindChildByName("Damage")); - if ((Damage == NULL) || (Damage->GetType() != cNBTTag::TAG_Short)) + Item.m_ItemID = (ENUM_ITEM_ID)(a_NBT.GetShort(ID)); + int Damage = a_NBT.FindChildByName(Child, "Damage"); + if ((Damage < 0) || (a_NBT.GetType(Damage) != TAG_Short)) { continue; } - Item.m_ItemHealth = Damage->m_Value; - cNBTByte * Count = (cNBTByte *)((*itr)->FindChildByName("Count")); - if ((Count == NULL) || (Count->GetType() != cNBTTag::TAG_Byte)) + Item.m_ItemHealth = a_NBT.GetShort(Damage); + int Count = a_NBT.FindChildByName(Child, "Count"); + if ((Count < 0) || (a_NBT.GetType(Count) != TAG_Byte)) { continue; } - Item.m_ItemCount = Count->m_Value; - Chest->SetSlot(Slot->m_Value, Item); + Item.m_ItemCount = a_NBT.GetByte(Count); + Chest->SetSlot(a_NBT.GetByte(Slot), Item); } // for itr - ItemDefs[] a_BlockEntities.push_back(Chest.release()); } @@ -602,26 +606,26 @@ void cWSSAnvil::LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cNBTC -bool cWSSAnvil::GetBlockEntityNBTPos(const cNBTCompound * a_NBT, int & a_X, int & a_Y, int & a_Z) +bool cWSSAnvil::GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z) { - cNBTInt * x = (cNBTInt *)(a_NBT->FindChildByName("x")); - if ((x == NULL) || (x->GetType() != cNBTTag::TAG_Int)) + int x = a_NBT.FindChildByName(a_TagIdx, "x"); + if ((x < 0) || (a_NBT.GetType(x) != TAG_Int)) { return false; } - cNBTInt * y = (cNBTInt *)(a_NBT->FindChildByName("y")); - if ((y == NULL) || (y->GetType() != cNBTTag::TAG_Int)) + int y = a_NBT.FindChildByName(a_TagIdx, "y"); + if ((y < 0) || (a_NBT.GetType(y) != TAG_Int)) { return false; } - cNBTInt * z = (cNBTInt *)(a_NBT->FindChildByName("z")); - if ((z == NULL) || (z->GetType() != cNBTTag::TAG_Int)) + int z = a_NBT.FindChildByName(a_TagIdx, "z"); + if ((z < 0) || (a_NBT.GetType(z) != TAG_Int)) { return false; } - a_X = x->m_Value; - a_Y = y->m_Value; - a_Z = z->m_Value; + a_X = a_NBT.GetInt(x); + a_Y = a_NBT.GetInt(y); + a_Z = a_NBT.GetInt(z); return true; } diff --git a/source/WSSAnvil.h b/source/WSSAnvil.h index 37d813f28..58259f0e8 100644 --- a/source/WSSAnvil.h +++ b/source/WSSAnvil.h @@ -9,6 +9,7 @@ #pragma once #include "WorldStorage.h" +#include "FastNBT.h" @@ -99,24 +100,27 @@ protected: bool SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data); /// Loads the chunk from NBT data (no locking needed) - bool LoadChunkFromNBT(const cChunkCoords & a_Chunk, cNBTTag & a_NBT); + 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); - /// Loads the chunk's entities from NBT data (a_NBT is the Level\\Entities list tag; may be NULL) - void LoadEntitiesFromNBT(cEntityList & a_Entitites, const cNBTList * a_NBT); + /// 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); - /// Loads the chunk's BlockEntities from NBT data (a_NBT is the Level\\TileEntities list tag; may be NULL) - void LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntitites, const cNBTList * a_NBT); + /// Loads the chunk's BlockEntities from NBT data (a_Tag is the Level\\TileEntities list tag; may be -1) + void LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntitites, const cParsedNBT & a_NBT, int a_Tag); - void LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cNBTCompound * a_NBT); + void LoadChestFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx); /// Helper function for extracting the X, Y, and Z int subtags of a NBT compound; returns true if successful - bool GetBlockEntityNBTPos(const cNBTCompound * a_NBT, int & a_X, int & a_Y, int & a_Z); + bool GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z); /// Gets the correct MCA file either from cache or from disk, manages the m_MCAFiles cache; assumes m_CS is locked cMCAFile * LoadMCAFile(const cChunkCoords & a_Chunk); + + /// Copies a_Length bytes of data from the specified NBT Tag's Child into the a_Destination buffer + void CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString & a_ChildName, char * a_Destination, int a_Length); // cWSSchema overrides: virtual bool LoadChunk(const cChunkCoords & a_Chunk) override; -- cgit v1.2.3