Fast NBT writer (Saving a chunk is now about twice as fast)
git-svn-id: http://mc-server.googlecode.com/svn/trunk@484 0a769ca7-a7f5-676a-18bf-c427514a06d6
This commit is contained in:
parent
05a1f89286
commit
f2681777fb
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -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++;
|
||||
}
|
||||
}
|
||||
} ;
|
||||
|
||||
|
||||
|
@ -41,12 +41,24 @@ 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:
|
||||
|
||||
/* From cChunkDataSeparateCollector we inherit:
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user