1
0

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
This commit is contained in:
madmaxoft@gmail.com 2012-05-07 20:22:04 +00:00
parent b73fba7c08
commit 35c8bc4c58
9 changed files with 702 additions and 104 deletions

View File

@ -572,14 +572,6 @@
RelativePath="..\source\MersenneTwister.h" RelativePath="..\source\MersenneTwister.h"
> >
</File> </File>
<File
RelativePath="..\source\NBT.cpp"
>
</File>
<File
RelativePath="..\source\NBT.h"
>
</File>
<File <File
RelativePath="..\source\StackWalker.cpp" RelativePath="..\source\StackWalker.cpp"
> >
@ -1629,6 +1621,22 @@
<Filter <Filter
Name="World storage" Name="World storage"
> >
<File
RelativePath="..\source\FastNBT.cpp"
>
</File>
<File
RelativePath="..\source\FastNBT.h"
>
</File>
<File
RelativePath="..\source\NBT.cpp"
>
</File>
<File
RelativePath="..\source\NBT.h"
>
</File>
<File <File
RelativePath="..\source\WorldStorage.cpp" RelativePath="..\source\WorldStorage.cpp"
> >

View File

@ -390,6 +390,7 @@
<ClCompile Include="..\source\cWorldGenerator_Test.cpp" /> <ClCompile Include="..\source\cWorldGenerator_Test.cpp" />
<ClCompile Include="..\source\cZombie.cpp" /> <ClCompile Include="..\source\cZombie.cpp" />
<ClCompile Include="..\source\cZombiepigman.cpp" /> <ClCompile Include="..\source\cZombiepigman.cpp" />
<ClCompile Include="..\source\FastNBT.cpp" />
<ClCompile Include="..\source\Globals.cpp"> <ClCompile Include="..\source\Globals.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug with optimized Noise|Win32'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug with optimized Noise|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader> <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
@ -575,6 +576,7 @@
<ClInclude Include="..\source\cZombie.h" /> <ClInclude Include="..\source\cZombie.h" />
<ClInclude Include="..\source\cZombiepigman.h" /> <ClInclude Include="..\source\cZombiepigman.h" />
<ClInclude Include="..\source\Endianness.h" /> <ClInclude Include="..\source\Endianness.h" />
<ClInclude Include="..\source\FastNBT.h" />
<ClInclude Include="..\Source\FileDefine.h" /> <ClInclude Include="..\Source\FileDefine.h" />
<ClInclude Include="..\source\Globals.h" /> <ClInclude Include="..\source\Globals.h" />
<ClInclude Include="..\source\LeakFinder.h" /> <ClInclude Include="..\source\LeakFinder.h" />

View File

@ -445,6 +445,9 @@
<Filter Include="Simulator\cSimulator\cRedstoneSimulator"> <Filter Include="Simulator\cSimulator\cRedstoneSimulator">
<UniqueIdentifier>{4b3b7b43-8e8b-4823-b112-2259fdfff7d3}</UniqueIdentifier> <UniqueIdentifier>{4b3b7b43-8e8b-4823-b112-2259fdfff7d3}</UniqueIdentifier>
</Filter> </Filter>
<Filter Include="Storage">
<UniqueIdentifier>{038cf4bd-108e-44e2-bdcb-9d48fb26676e}</UniqueIdentifier>
</Filter>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\source\cServer.cpp"> <ClCompile Include="..\source\cServer.cpp">
@ -887,20 +890,31 @@
<ClCompile Include="..\source\cSocketThreads.cpp" /> <ClCompile Include="..\source\cSocketThreads.cpp" />
<ClCompile Include="..\source\LeakFinder.cpp" /> <ClCompile Include="..\source\LeakFinder.cpp" />
<ClCompile Include="..\source\StackWalker.cpp" /> <ClCompile Include="..\source\StackWalker.cpp" />
<ClCompile Include="..\source\WorldStorage.cpp" />
<ClCompile Include="..\source\WSSCompact.cpp" />
<ClCompile Include="..\source\StringCompression.cpp" /> <ClCompile Include="..\source\StringCompression.cpp" />
<ClCompile Include="..\source\LightingThread.cpp" /> <ClCompile Include="..\source\LightingThread.cpp" />
<ClCompile Include="..\source\cRedstoneSimulator.cpp"> <ClCompile Include="..\source\cRedstoneSimulator.cpp">
<Filter>Simulator\cSimulator\cRedstoneSimulator</Filter> <Filter>Simulator\cSimulator\cRedstoneSimulator</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\source\ChunkSender.cpp" /> <ClCompile Include="..\source\ChunkSender.cpp" />
<ClCompile Include="..\source\WSSAnvil.cpp" />
<ClCompile Include="..\source\NBT.cpp" />
<ClCompile Include="..\source\WGFlat.cpp" /> <ClCompile Include="..\source\WGFlat.cpp" />
<ClCompile Include="..\source\packets\cPacket_Player.cpp"> <ClCompile Include="..\source\packets\cPacket_Player.cpp">
<Filter>Packets</Filter> <Filter>Packets</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\source\NBT.cpp">
<Filter>Storage</Filter>
</ClCompile>
<ClCompile Include="..\source\WorldStorage.cpp">
<Filter>Storage</Filter>
</ClCompile>
<ClCompile Include="..\source\WSSAnvil.cpp">
<Filter>Storage</Filter>
</ClCompile>
<ClCompile Include="..\source\WSSCompact.cpp">
<Filter>Storage</Filter>
</ClCompile>
<ClCompile Include="..\source\FastNBT.cpp">
<Filter>Storage</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClInclude Include="..\source\cServer.h"> <ClInclude Include="..\source\cServer.h">
@ -1379,23 +1393,34 @@
<ClInclude Include="..\source\StringUtils.h" /> <ClInclude Include="..\source\StringUtils.h" />
<ClInclude Include="..\source\cIsThread.h" /> <ClInclude Include="..\source\cIsThread.h" />
<ClInclude Include="..\source\cSocketThreads.h" /> <ClInclude Include="..\source\cSocketThreads.h" />
<ClInclude Include="..\source\WSSCompact.h" />
<ClInclude Include="..\source\LeakFinder.h" /> <ClInclude Include="..\source\LeakFinder.h" />
<ClInclude Include="..\source\StackWalker.h" /> <ClInclude Include="..\source\StackWalker.h" />
<ClInclude Include="..\source\WorldStorage.h" />
<ClInclude Include="..\source\StringCompression.h" /> <ClInclude Include="..\source\StringCompression.h" />
<ClInclude Include="..\source\LightingThread.h" /> <ClInclude Include="..\source\LightingThread.h" />
<ClInclude Include="..\source\cRedstoneSimulator.h"> <ClInclude Include="..\source\cRedstoneSimulator.h">
<Filter>Simulator\cSimulator\cRedstoneSimulator</Filter> <Filter>Simulator\cSimulator\cRedstoneSimulator</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\source\ChunkSender.h" /> <ClInclude Include="..\source\ChunkSender.h" />
<ClInclude Include="..\source\WSSAnvil.h" />
<ClInclude Include="..\source\NBT.h" />
<ClInclude Include="..\source\WGFlat.h" /> <ClInclude Include="..\source\WGFlat.h" />
<ClInclude Include="..\source\ChunkDef.h" /> <ClInclude Include="..\source\ChunkDef.h" />
<ClInclude Include="..\source\packets\cPacket_Player.h"> <ClInclude Include="..\source\packets\cPacket_Player.h">
<Filter>Packets</Filter> <Filter>Packets</Filter>
</ClInclude> </ClInclude>
<ClInclude Include="..\source\NBT.h">
<Filter>Storage</Filter>
</ClInclude>
<ClInclude Include="..\source\WorldStorage.h">
<Filter>Storage</Filter>
</ClInclude>
<ClInclude Include="..\source\WSSAnvil.h">
<Filter>Storage</Filter>
</ClInclude>
<ClInclude Include="..\source\WSSCompact.h">
<Filter>Storage</Filter>
</ClInclude>
<ClInclude Include="..\source\FastNBT.h">
<Filter>Storage</Filter>
</ClInclude>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="..\source\AllToLua.pkg"> <None Include="..\source\AllToLua.pkg">

320
source/FastNBT.cpp Normal file
View File

@ -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;
}

234
source/FastNBT.h Normal file
View File

@ -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<cFastNBTTag> 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;
} ;

View File

@ -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 // 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 // This should be used in the private: declarations for any class that shouldn't allow copying itself
#define DISALLOW_COPY_AND_ASSIGN(TypeName) \ #define DISALLOW_COPY_AND_ASSIGN(TypeName) \

View File

@ -13,14 +13,6 @@
typedef long long Int64;
typedef int Int32;
typedef short Int16;
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Representation classes: // Representation classes:

View File

@ -15,6 +15,7 @@
#include "cEntity.h" #include "cEntity.h"
#include "cBlockEntity.h" #include "cBlockEntity.h"
#include "cMakeDir.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()))); Data->Add(new cNBTInt(Data, "SpawnZ", (int)(a_World->GetSpawnZ())));
AString Uncompressed; AString Uncompressed;
cNBTSerializer::Serialize(Root.get(), Uncompressed); cNBTSerializer::Serialize(Root.get(), Uncompressed);
gzFile gz = gzopen(fnam.c_str(), "wb"); gzFile gz = gzopen(fnam.c_str(), "wb");
if (gz != NULL) if (gz != NULL)
{ {
@ -304,14 +306,15 @@ bool cWSSAnvil::LoadChunkFromData(const cChunkCoords & a_Chunk, const AString &
} }
// Parse the NBT data: // Parse the NBT data:
std::auto_ptr<cNBTTree> Tree(cNBTParser::Parse(Uncompressed, strm.total_out)); cParsedNBT NBT(Uncompressed, strm.total_out);
if (Tree.get() == NULL) if (!NBT.IsValid())
{ {
// NBT Parsing failed
return false; return false;
} }
// Load the data from NBT: // 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) // The data arrays, in MCA-native y/z/x ordering (will be reordered for the final chunk data)
BLOCKTYPE BlockData[cChunkDef::BlockDataSize]; 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 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: // Load the blockdata, blocklight and skylight:
cNBTList * Sections = (cNBTList *)a_NBT.FindChildByPath("Level\\Sections"); int Level = a_NBT.FindChildByName(0, "Level");
if ((Sections == NULL) || (Sections->GetType() != cNBTTag::TAG_List) || (Sections->GetChildrenType() != cNBTTag::TAG_Compound)) if (Level < 0)
{ {
return false; return false;
} }
const cNBTTags & LevelSections = Sections->GetChildren(); int Sections = a_NBT.FindChildByName(Level, "Sections");
for (cNBTTags::const_iterator itr = LevelSections.begin(); itr != LevelSections.end(); ++itr) 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; int y = 0;
cNBTByte * SectionY = (cNBTByte *)((*itr)->FindChildByName("Y")); int SectionY = a_NBT.FindChildByName(Child, "Y");
if ((SectionY == NULL) || (SectionY->GetType() != cNBTTag::TAG_Byte) || (SectionY->m_Value < 0) || (SectionY->m_Value > 15)) if ((SectionY < 0) || (a_NBT.GetType(SectionY) != TAG_Byte))
{ {
continue; continue;
} }
y = SectionY->m_Value; y = a_NBT.GetByte(SectionY);
cNBTByteArray * baBlocks = (cNBTByteArray *)((*itr)->FindChildByName("Blocks")); if ((y < 0) || (y > 15))
if ((baBlocks != NULL) && (baBlocks->GetType() == cNBTTag::TAG_ByteArray) && (baBlocks->m_Value.size() == 4096))
{ {
memcpy(&(BlockData[y * 4096]), baBlocks->m_Value.data(), 4096); continue;
}
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))
{
memcpy(&(BlockLight[y * 2048]), baBlockLight->m_Value.data(), 2048);
} }
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[] } // for itr - LevelSections[]
cEntityList Entities; cEntityList Entities;
cBlockEntityList BlockEntities; cBlockEntityList BlockEntities;
// Load the entities from NBT: // Load the entities from NBT:
LoadEntitiesFromNBT (Entities, (cNBTList *)(a_NBT.FindChildByPath("Level\\Entities"))); LoadEntitiesFromNBT (Entities, a_NBT, a_NBT.FindChildByName(Level, "Entities"));
LoadBlockEntitiesFromNBT(BlockEntities, (cNBTList *)(a_NBT.FindChildByPath("Level\\TileEntities"))); LoadBlockEntitiesFromNBT(BlockEntities, a_NBT, a_NBT.FindChildByName(Level, "TileEntities"));
#if (AXIS_ORDER == AXIS_ORDER_YZX) #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: // 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) 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 // 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; 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; continue;
} }
cNBTString * sID = (cNBTString *)((*itr)->FindChildByName("id")); int sID = a_NBT.FindChildByName(Child, "id");
if (sID == NULL) if (sID < 0)
{ {
continue; 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 // 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(a_TagIdx) == TAG_Compound);
ASSERT(a_NBT->GetType() == cNBTTag::TAG_Compound);
int x, y, z; int x, y, z;
if (!GetBlockEntityNBTPos(a_NBT, x, y, z)) if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
{ {
return; return;
} }
cNBTList * Items = (cNBTList *)(a_NBT->FindChildByName("Items")); int Items = a_NBT.FindChildByName(a_TagIdx, "Items");
if ((Items == NULL) || (Items->GetType() != cNBTTag::TAG_List)) 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<cChestEntity> Chest(new cChestEntity(x, y, z, m_World)); std::auto_ptr<cChestEntity> Chest(new cChestEntity(x, y, z, m_World));
const cNBTTags & ItemDefs = Items->GetChildren(); for (int Child = a_NBT.GetFirstChild(Items); Child != -1; Child = a_NBT.GetNextSibling(Child))
for (cNBTTags::const_iterator itr = ItemDefs.begin(); itr != ItemDefs.end(); ++itr)
{ {
cNBTByte * Slot = (cNBTByte *)((*itr)->FindChildByName("Slot")); int Slot = a_NBT.FindChildByName(Child, "Slot");
if ((Slot == NULL) || (Slot->GetType() != cNBTTag::TAG_Byte)) if ((Slot < 0) || (a_NBT.GetType(Slot) != TAG_Byte))
{ {
continue; continue;
} }
cItem Item; cItem Item;
cNBTShort * ID = (cNBTShort *)((*itr)->FindChildByName("id")); int ID = a_NBT.FindChildByName(Child, "id");
if ((ID == NULL) || (ID->GetType() != cNBTTag::TAG_Short)) if ((ID < 0) || (a_NBT.GetType(ID) != TAG_Short))
{ {
continue; continue;
} }
Item.m_ItemID = (ENUM_ITEM_ID)(ID->m_Value); Item.m_ItemID = (ENUM_ITEM_ID)(a_NBT.GetShort(ID));
cNBTShort * Damage = (cNBTShort *)((*itr)->FindChildByName("Damage")); int Damage = a_NBT.FindChildByName(Child, "Damage");
if ((Damage == NULL) || (Damage->GetType() != cNBTTag::TAG_Short)) if ((Damage < 0) || (a_NBT.GetType(Damage) != TAG_Short))
{ {
continue; continue;
} }
Item.m_ItemHealth = Damage->m_Value; Item.m_ItemHealth = a_NBT.GetShort(Damage);
cNBTByte * Count = (cNBTByte *)((*itr)->FindChildByName("Count")); int Count = a_NBT.FindChildByName(Child, "Count");
if ((Count == NULL) || (Count->GetType() != cNBTTag::TAG_Byte)) if ((Count < 0) || (a_NBT.GetType(Count) != TAG_Byte))
{ {
continue; continue;
} }
Item.m_ItemCount = Count->m_Value; Item.m_ItemCount = a_NBT.GetByte(Count);
Chest->SetSlot(Slot->m_Value, Item); Chest->SetSlot(a_NBT.GetByte(Slot), Item);
} // for itr - ItemDefs[] } // for itr - ItemDefs[]
a_BlockEntities.push_back(Chest.release()); 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")); int x = a_NBT.FindChildByName(a_TagIdx, "x");
if ((x == NULL) || (x->GetType() != cNBTTag::TAG_Int)) if ((x < 0) || (a_NBT.GetType(x) != TAG_Int))
{ {
return false; return false;
} }
cNBTInt * y = (cNBTInt *)(a_NBT->FindChildByName("y")); int y = a_NBT.FindChildByName(a_TagIdx, "y");
if ((y == NULL) || (y->GetType() != cNBTTag::TAG_Int)) if ((y < 0) || (a_NBT.GetType(y) != TAG_Int))
{ {
return false; return false;
} }
cNBTInt * z = (cNBTInt *)(a_NBT->FindChildByName("z")); int z = a_NBT.FindChildByName(a_TagIdx, "z");
if ((z == NULL) || (z->GetType() != cNBTTag::TAG_Int)) if ((z < 0) || (a_NBT.GetType(z) != TAG_Int))
{ {
return false; return false;
} }
a_X = x->m_Value; a_X = a_NBT.GetInt(x);
a_Y = y->m_Value; a_Y = a_NBT.GetInt(y);
a_Z = z->m_Value; a_Z = a_NBT.GetInt(z);
return true; return true;
} }

View File

@ -9,6 +9,7 @@
#pragma once #pragma once
#include "WorldStorage.h" #include "WorldStorage.h"
#include "FastNBT.h"
@ -99,25 +100,28 @@ protected:
bool SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data); bool SaveChunkToData(const cChunkCoords & a_Chunk, AString & a_Data);
/// Loads the chunk from NBT data (no locking needed) /// 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 /// Saves the chunk into NBT data; returns NULL for failure
cNBTTag * SaveChunkToNBT(const cChunkCoords & a_Chunk); 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) /// 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 cNBTList * a_NBT); 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) /// 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 cNBTList * a_NBT); 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 /// 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 /// 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); 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: // cWSSchema overrides:
virtual bool LoadChunk(const cChunkCoords & a_Chunk) override; virtual bool LoadChunk(const cChunkCoords & a_Chunk) override;
virtual bool SaveChunk(const cChunkCoords & a_Chunk) override; virtual bool SaveChunk(const cChunkCoords & a_Chunk) override;