summaryrefslogtreecommitdiffstats
path: root/src/WorldStorage
diff options
context:
space:
mode:
Diffstat (limited to 'src/WorldStorage')
-rw-r--r--src/WorldStorage/FastNBT.cpp16
-rw-r--r--src/WorldStorage/FastNBT.h112
-rw-r--r--src/WorldStorage/FireworksSerializer.cpp262
-rw-r--r--src/WorldStorage/FireworksSerializer.h92
-rw-r--r--src/WorldStorage/MapSerializer.cpp280
-rw-r--r--src/WorldStorage/MapSerializer.h86
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp182
-rw-r--r--src/WorldStorage/NBTChunkSerializer.h14
-rw-r--r--src/WorldStorage/SchematicFileSerializer.cpp305
-rw-r--r--src/WorldStorage/SchematicFileSerializer.h54
-rw-r--r--src/WorldStorage/ScoreboardSerializer.cpp377
-rw-r--r--src/WorldStorage/ScoreboardSerializer.h52
-rw-r--r--src/WorldStorage/WSSAnvil.cpp532
-rw-r--r--src/WorldStorage/WSSAnvil.h15
-rw-r--r--src/WorldStorage/WSSCompact.cpp229
-rw-r--r--src/WorldStorage/WSSCompact.h2
16 files changed, 2429 insertions, 181 deletions
diff --git a/src/WorldStorage/FastNBT.cpp b/src/WorldStorage/FastNBT.cpp
index 8f80c3f75..be25fd1a4 100644
--- a/src/WorldStorage/FastNBT.cpp
+++ b/src/WorldStorage/FastNBT.cpp
@@ -506,22 +506,18 @@ void cFastNBTWriter::AddIntArray(const AString & a_Name, const int * a_Value, si
{
TagCommon(a_Name, TAG_IntArray);
Int32 len = htonl(a_NumElements);
+ size_t cap = m_Result.capacity();
+ size_t size = m_Result.length();
+ if ((cap - size) < (4 + a_NumElements * 4))
+ {
+ m_Result.reserve(size + 4 + (a_NumElements * 4));
+ }
m_Result.append((const char *)&len, 4);
-#if defined(ANDROID_NDK)
- // Android has alignment issues - cannot byteswap (htonl) an int that is not 32-bit-aligned, which happens in the regular version
for (size_t i = 0; i < a_NumElements; i++)
{
int Element = htonl(a_Value[i]);
m_Result.append((const char *)&Element, 4);
}
-#else
- 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]);
- }
-#endif
}
diff --git a/src/WorldStorage/FastNBT.h b/src/WorldStorage/FastNBT.h
index b84eda1a1..5e5af3ca3 100644
--- a/src/WorldStorage/FastNBT.h
+++ b/src/WorldStorage/FastNBT.h
@@ -106,6 +106,10 @@ public:
/** 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.
+The parser decomposes the input data into a tree of tags that is stored as an array of cFastNBTTag items,
+and accessing the tree is done by using the array indices for tags. Each tag stores the indices for its parent,
+first child, last child, prev sibling and next sibling, a value of -1 indicates that the indice is not valid.
+Each primitive tag also stores the length of the contained data, in bytes.
*/
class cParsedNBT
{
@@ -114,86 +118,134 @@ public:
bool IsValid(void) const {return m_IsValid; }
+ /** Returns the root tag of the hierarchy. */
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; }
+ /** Returns the first child of the specified tag, or -1 if none / not applicable. */
+ int GetFirstChild (int a_Tag) const { return m_Tags[(size_t)a_Tag].m_FirstChild; }
+
+ /** Returns the last child of the specified tag, or -1 if none / not applicable. */
+ int GetLastChild (int a_Tag) const { return m_Tags[(size_t)a_Tag].m_LastChild; }
+
+ /** Returns the next sibling of the specified tag, or -1 if none. */
+ int GetNextSibling(int a_Tag) const { return m_Tags[(size_t)a_Tag].m_NextSibling; }
+
+ /** Returns the previous sibling of the specified tag, or -1 if none. */
+ int GetPrevSibling(int a_Tag) const { return m_Tags[(size_t)a_Tag].m_PrevSibling; }
+
+ /** Returns the length of the tag's data, in bytes.
+ Not valid for Compound or List tags! */
+ int GetDataLength (int a_Tag) const
+ {
+ ASSERT(m_Tags[(size_t)a_Tag].m_Type != TAG_List);
+ ASSERT(m_Tags[(size_t)a_Tag].m_Type != TAG_Compound);
+ return m_Tags[(size_t)a_Tag].m_DataLength;
+ }
+
+ /** Returns the data stored in this tag.
+ Not valid for Compound or List tags! */
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;
+ ASSERT(m_Tags[(size_t)a_Tag].m_Type != TAG_List);
+ ASSERT(m_Tags[(size_t)a_Tag].m_Type != TAG_Compound);
+ return m_Data + m_Tags[(size_t)a_Tag].m_DataStart;
}
+ /** Returns the direct child tag of the specified name, or -1 if no such tag. */
int FindChildByName(int a_Tag, const AString & a_Name) const
{
return FindChildByName(a_Tag, a_Name.c_str(), a_Name.length());
}
+ /** Returns the direct child tag of the specified name, or -1 if no such tag. */
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;
+
+ /** Returns the child tag of the specified path (Name1\Name2\Name3...), or -1 if no such tag. */
+ int FindTagByPath(int a_Tag, const AString & a_Path) const;
- eTagType GetType(int a_Tag) const { return m_Tags[a_Tag].m_Type; }
+ eTagType GetType(int a_Tag) const { return m_Tags[(size_t)a_Tag].m_Type; }
- /// Returns the children type for a list tag; undefined on other tags. If list empty, returns TAG_End
+ /** 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;
+ ASSERT(m_Tags[(size_t)a_Tag].m_Type == TAG_List);
+ return (m_Tags[(size_t)a_Tag].m_FirstChild < 0) ? TAG_End : m_Tags[(size_t)m_Tags[(size_t)a_Tag].m_FirstChild].m_Type;
}
+ /** Returns the value stored in a Byte tag. Not valid for any other tag 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]);
+ ASSERT(m_Tags[(size_t)a_Tag].m_Type == TAG_Byte);
+ return (unsigned char)(m_Data[(size_t)m_Tags[(size_t)a_Tag].m_DataStart]);
}
+ /** Returns the value stored in a Short tag. Not valid for any other tag type. */
inline Int16 GetShort(int a_Tag) const
{
- ASSERT(m_Tags[a_Tag].m_Type == TAG_Short);
- return GetBEShort(m_Data + m_Tags[a_Tag].m_DataStart);
+ ASSERT(m_Tags[(size_t)a_Tag].m_Type == TAG_Short);
+ return GetBEShort(m_Data + m_Tags[(size_t)a_Tag].m_DataStart);
}
+ /** Returns the value stored in an Int tag. Not valid for any other tag type. */
inline Int32 GetInt(int a_Tag) const
{
- ASSERT(m_Tags[a_Tag].m_Type == TAG_Int);
- return GetBEInt(m_Data + m_Tags[a_Tag].m_DataStart);
+ ASSERT(m_Tags[(size_t)a_Tag].m_Type == TAG_Int);
+ return GetBEInt(m_Data + m_Tags[(size_t)a_Tag].m_DataStart);
}
+ /** Returns the value stored in a Long tag. Not valid for any other tag type. */
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);
+ ASSERT(m_Tags[(size_t)a_Tag].m_Type == TAG_Long);
+ return NetworkToHostLong8(m_Data + m_Tags[(size_t)a_Tag].m_DataStart);
}
+ /** Returns the value stored in a Float tag. Not valid for any other tag type. */
inline float GetFloat(int a_Tag) const
{
- ASSERT(m_Tags[a_Tag].m_Type == TAG_Float);
- Int32 tmp = GetBEInt(m_Data + m_Tags[a_Tag].m_DataStart);
- return *((float *)&tmp);
+ ASSERT(m_Tags[(size_t)a_Tag].m_Type == TAG_Float);
+
+ // Cause a compile-time error if sizeof(float) != 4
+ // If your platform produces a compiler error here, you'll need to add code that manually decodes 32-bit floats
+ char Check1[5 - sizeof(float)]; // Fails if sizeof(float) > 4
+ char Check2[sizeof(float) - 3]; // Fails if sizeof(float) < 4
+ UNUSED(Check1);
+ UNUSED(Check2);
+
+ Int32 i = GetBEInt(m_Data + m_Tags[(size_t)a_Tag].m_DataStart);
+ float f;
+ memcpy(&f, &i, sizeof(f));
+ return f;
}
+ /** Returns the value stored in a Double tag. Not valid for any other tag type. */
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);
+ // Cause a compile-time error if sizeof(double) != 8
+ // If your platform produces a compiler error here, you'll need to add code that manually decodes 64-bit doubles
+ char Check1[9 - sizeof(double)]; // Fails if sizeof(double) > 8
+ char Check2[sizeof(double) - 7]; // Fails if sizeof(double) < 8
+ UNUSED(Check1);
+ UNUSED(Check2);
+
+ ASSERT(m_Tags[(size_t)a_Tag].m_Type == TAG_Double);
+ return NetworkToHostDouble8(m_Data + m_Tags[(size_t)a_Tag].m_DataStart);
}
+ /** Returns the value stored in a String tag. Not valid for any other tag type. */
inline AString GetString(int a_Tag) const
{
- ASSERT(m_Tags[a_Tag].m_Type == TAG_String);
+ ASSERT(m_Tags[(size_t)a_Tag].m_Type == TAG_String);
AString res;
- res.assign(m_Data + m_Tags[a_Tag].m_DataStart, m_Tags[a_Tag].m_DataLength);
+ res.assign(m_Data + m_Tags[(size_t)a_Tag].m_DataStart, m_Tags[(size_t)a_Tag].m_DataLength);
return res;
}
+ /** Returns the tag's name. For tags that are not named, returns an empty string. */
inline AString GetName(int a_Tag) const
{
AString res;
- res.assign(m_Data + m_Tags[a_Tag].m_NameStart, m_Tags[a_Tag].m_NameLength);
+ res.assign(m_Data + m_Tags[(size_t)a_Tag].m_NameStart, m_Tags[(size_t)a_Tag].m_NameLength);
return res;
}
@@ -257,7 +309,7 @@ protected:
eTagType m_ItemType; // for TAG_List, the element type
} ;
- static const int MAX_STACK = 50; // Highliy doubtful that an NBT would be constructed this many levels deep
+ static const int MAX_STACK = 50; // Highly 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];
diff --git a/src/WorldStorage/FireworksSerializer.cpp b/src/WorldStorage/FireworksSerializer.cpp
new file mode 100644
index 000000000..744fc731f
--- /dev/null
+++ b/src/WorldStorage/FireworksSerializer.cpp
@@ -0,0 +1,262 @@
+
+#include "Globals.h"
+#include "FireworksSerializer.h"
+#include "WorldStorage/FastNBT.h"
+
+
+
+
+
+void cFireworkItem::WriteToNBTCompound(const cFireworkItem & a_FireworkItem, cFastNBTWriter & a_Writer, const ENUM_ITEM_ID a_Type)
+{
+ switch (a_Type)
+ {
+ case E_ITEM_FIREWORK_ROCKET:
+ {
+ a_Writer.BeginCompound("Fireworks");
+ a_Writer.AddByte("Flight", a_FireworkItem.m_FlightTimeInTicks / 20);
+ a_Writer.BeginList("Explosions", TAG_Compound);
+ a_Writer.BeginCompound("");
+ a_Writer.AddByte("Flicker", a_FireworkItem.m_HasFlicker);
+ a_Writer.AddByte("Trail", a_FireworkItem.m_HasTrail);
+ a_Writer.AddByte("Type", a_FireworkItem.m_Type);
+ if (!a_FireworkItem.m_Colours.empty())
+ {
+ a_Writer.AddIntArray("Colors", &(a_FireworkItem.m_Colours[0]), a_FireworkItem.m_Colours.size());
+ }
+ if (!a_FireworkItem.m_FadeColours.empty())
+ {
+ a_Writer.AddIntArray("FadeColors", &(a_FireworkItem.m_FadeColours[0]), a_FireworkItem.m_FadeColours.size());
+ }
+ a_Writer.EndCompound();
+ a_Writer.EndList();
+ a_Writer.EndCompound();
+ break;
+ }
+ case E_ITEM_FIREWORK_STAR:
+ {
+ a_Writer.BeginCompound("Explosion");
+ a_Writer.AddByte("Flicker", a_FireworkItem.m_HasFlicker);
+ a_Writer.AddByte("Trail", a_FireworkItem.m_HasTrail);
+ a_Writer.AddByte("Type", a_FireworkItem.m_Type);
+ if (!a_FireworkItem.m_Colours.empty())
+ {
+ a_Writer.AddIntArray("Colors", &(a_FireworkItem.m_Colours[0]), a_FireworkItem.m_Colours.size());
+ }
+ if (!a_FireworkItem.m_FadeColours.empty())
+ {
+ a_Writer.AddIntArray("FadeColors", &(a_FireworkItem.m_FadeColours[0]), a_FireworkItem.m_FadeColours.size());
+ }
+ a_Writer.EndCompound();
+ break;
+ }
+ default: ASSERT(!"Unhandled firework item!"); break;
+ }
+}
+
+
+
+
+
+void cFireworkItem::ParseFromNBT(cFireworkItem & a_FireworkItem, const cParsedNBT & a_NBT, int a_TagIdx, const ENUM_ITEM_ID a_Type)
+{
+ if (a_TagIdx < 0)
+ {
+ return;
+ }
+
+ switch (a_Type)
+ {
+ case E_ITEM_FIREWORK_STAR:
+ {
+ for (int explosiontag = a_NBT.GetFirstChild(a_TagIdx); explosiontag >= 0; explosiontag = a_NBT.GetNextSibling(explosiontag))
+ {
+ eTagType TagType = a_NBT.GetType(explosiontag);
+ if (TagType == TAG_Byte) // Custon name tag
+ {
+ AString ExplosionName = a_NBT.GetName(explosiontag);
+
+ if (ExplosionName == "Flicker")
+ {
+ a_FireworkItem.m_HasFlicker = (a_NBT.GetByte(explosiontag) == 1);
+ }
+ else if (ExplosionName == "Trail")
+ {
+ a_FireworkItem.m_HasTrail = (a_NBT.GetByte(explosiontag) == 1);
+ }
+ else if (ExplosionName == "Type")
+ {
+ a_FireworkItem.m_Type = a_NBT.GetByte(explosiontag);
+ }
+ }
+ else if (TagType == TAG_IntArray)
+ {
+ AString ExplosionName = a_NBT.GetName(explosiontag);
+
+ if (ExplosionName == "Colors")
+ {
+ // Divide by four as data length returned in bytes
+ int DataLength = a_NBT.GetDataLength(explosiontag);
+ // round to the next highest multiple of four
+ DataLength -= DataLength % 4;
+ if (DataLength == 0)
+ {
+ continue;
+ }
+
+ const char * ColourData = (a_NBT.GetData(explosiontag));
+ for (int i = 0; i < DataLength; i += 4 /* Size of network int*/)
+ {
+ a_FireworkItem.m_Colours.push_back(GetBEInt(ColourData + i));
+ }
+ }
+ else if (ExplosionName == "FadeColors")
+ {
+ int DataLength = a_NBT.GetDataLength(explosiontag) / 4;
+ // round to the next highest multiple of four
+ DataLength -= DataLength % 4;
+ if (DataLength == 0)
+ {
+ continue;
+ }
+
+ const char * FadeColourData = (a_NBT.GetData(explosiontag));
+ for (int i = 0; i < DataLength; i += 4 /* Size of network int*/)
+ {
+ a_FireworkItem.m_FadeColours.push_back(GetBEInt(FadeColourData + i));
+ }
+ }
+ }
+ }
+ break;
+ }
+ case E_ITEM_FIREWORK_ROCKET:
+ {
+ for (int fireworkstag = a_NBT.GetFirstChild(a_TagIdx); fireworkstag >= 0; fireworkstag = a_NBT.GetNextSibling(fireworkstag))
+ {
+ eTagType TagType = a_NBT.GetType(fireworkstag);
+ if (TagType == TAG_Byte) // Custon name tag
+ {
+ if (a_NBT.GetName(fireworkstag) == "Flight")
+ {
+ a_FireworkItem.m_FlightTimeInTicks = a_NBT.GetByte(fireworkstag) * 20;
+ }
+ }
+ else if ((TagType == TAG_List) && (a_NBT.GetName(fireworkstag) == "Explosions"))
+ {
+ int ExplosionsChild = a_NBT.GetFirstChild(fireworkstag);
+ if ((a_NBT.GetType(ExplosionsChild) == TAG_Compound) && (a_NBT.GetName(ExplosionsChild).empty()))
+ {
+ ParseFromNBT(a_FireworkItem, a_NBT, ExplosionsChild, E_ITEM_FIREWORK_STAR);
+ }
+ }
+ }
+ break;
+ }
+ default: ASSERT(!"Unhandled firework item!"); break;
+ }
+}
+
+
+
+
+
+AString cFireworkItem::ColoursToString(const cFireworkItem & a_FireworkItem)
+{
+ AString Result;
+
+ for (std::vector<int>::const_iterator itr = a_FireworkItem.m_Colours.begin(); itr != a_FireworkItem.m_Colours.end(); ++itr)
+ {
+ AppendPrintf(Result, "%i;", *itr);
+ }
+
+ return Result;
+}
+
+
+
+
+
+void cFireworkItem::ColoursFromString(const AString & a_String, cFireworkItem & a_FireworkItem)
+{
+ AStringVector Split = StringSplit(a_String, ";");
+
+ for (size_t itr = 0; itr < Split.size(); ++itr)
+ {
+ if (Split[itr].empty())
+ {
+ continue;
+ }
+
+ a_FireworkItem.m_Colours.push_back(atoi(Split[itr].c_str()));
+ }
+}
+
+
+
+
+
+AString cFireworkItem::FadeColoursToString(const cFireworkItem & a_FireworkItem)
+{
+ AString Result;
+
+ for (std::vector<int>::const_iterator itr = a_FireworkItem.m_FadeColours.begin(); itr != a_FireworkItem.m_FadeColours.end(); ++itr)
+ {
+ AppendPrintf(Result, "%i;", *itr);
+ }
+
+ return Result;
+}
+
+
+
+
+
+void cFireworkItem::FadeColoursFromString(const AString & a_String, cFireworkItem & a_FireworkItem)
+{
+ AStringVector Split = StringSplit(a_String, ";");
+
+ for (size_t itr = 0; itr < Split.size(); ++itr)
+ {
+ if (Split[itr].empty())
+ {
+ continue;
+ }
+
+ a_FireworkItem.m_FadeColours.push_back(atoi(Split[itr].c_str()));
+ }
+}
+
+
+
+
+
+int cFireworkItem::GetVanillaColourCodeFromDye(short a_DyeMeta)
+{
+ /*
+ Colours are supposed to be calculated via: R << 16 + G << 8 + B
+ However, the RGB values fireworks use aren't the same as the ones for dyes (the ones listed in the MC Wiki)
+ Therefore, here is a list of numbers gotten via the Protocol Proxy
+ */
+
+ switch (a_DyeMeta)
+ {
+ case E_META_DYE_BLACK: return 0x1E1B1B;
+ case E_META_DYE_RED: return 0xB3312C;
+ case E_META_DYE_GREEN: return 0x3B511A;
+ case E_META_DYE_BROWN: return 0x51301A;
+ case E_META_DYE_BLUE: return 0x253192;
+ case E_META_DYE_PURPLE: return 0x7B2FBE;
+ case E_META_DYE_CYAN: return 0x287697;
+ case E_META_DYE_LIGHTGRAY: return 0xABABAB;
+ case E_META_DYE_GRAY: return 0x434343;
+ case E_META_DYE_PINK: return 0xD88198;
+ case E_META_DYE_LIGHTGREEN: return 0x41CD34;
+ case E_META_DYE_YELLOW: return 0xDECF2A;
+ case E_META_DYE_LIGHTBLUE: return 0x6689D3;
+ case E_META_DYE_MAGENTA: return 0xC354CD;
+ case E_META_DYE_ORANGE: return 0xEB8844;
+ case E_META_DYE_WHITE: return 0xF0F0F0;
+ default: ASSERT(!"Unhandled dye meta whilst trying to get colour code for fireworks!"); return 0;
+ }
+}
diff --git a/src/WorldStorage/FireworksSerializer.h b/src/WorldStorage/FireworksSerializer.h
new file mode 100644
index 000000000..cbc544a14
--- /dev/null
+++ b/src/WorldStorage/FireworksSerializer.h
@@ -0,0 +1,92 @@
+
+// FireworksSerializer.h
+
+// Declares the cFireworkItem class representing a firework or firework star
+
+
+
+
+
+#pragma once
+
+#include "Defines.h"
+
+class cFastNBTWriter;
+class cParsedNBT;
+
+
+
+
+
+class cFireworkItem
+{
+public:
+ cFireworkItem(void) :
+ m_HasFlicker(false),
+ m_HasTrail(false),
+ m_Type(0),
+ m_FlightTimeInTicks(0)
+ {
+ }
+
+ inline void CopyFrom(const cFireworkItem & a_Item)
+ {
+ m_FlightTimeInTicks = a_Item.m_FlightTimeInTicks;
+ m_HasFlicker = a_Item.m_HasFlicker;
+ m_HasTrail = a_Item.m_HasTrail;
+ m_Type = a_Item.m_Type;
+ m_Colours = a_Item.m_Colours;
+ m_FadeColours = a_Item.m_FadeColours;
+ }
+
+ inline void EmptyData(void)
+ {
+ m_FlightTimeInTicks = 0;
+ m_HasFlicker = false;
+ m_Type = 0;
+ m_HasTrail = false;
+ m_Colours.clear();
+ m_FadeColours.clear();
+ }
+
+ inline bool IsEqualTo(const cFireworkItem & a_Item) const
+ {
+ return
+ (
+ (m_FlightTimeInTicks == a_Item.m_FlightTimeInTicks) &&
+ (m_HasFlicker == a_Item.m_HasFlicker) &&
+ (m_HasTrail == a_Item.m_HasTrail) &&
+ (m_Type == a_Item.m_Type) &&
+ (m_Colours == a_Item.m_Colours) &&
+ (m_FadeColours == a_Item.m_FadeColours)
+ );
+ }
+
+ /** Writes firework NBT data to a Writer object */
+ static void WriteToNBTCompound(const cFireworkItem & a_FireworkItem, cFastNBTWriter & a_Writer, const ENUM_ITEM_ID a_Type);
+
+ /** Reads NBT data from a NBT object and populates a FireworkItem with it */
+ static void ParseFromNBT(cFireworkItem & a_FireworkItem, const cParsedNBT & a_NBT, int a_TagIdx, const ENUM_ITEM_ID a_Type);
+
+ /** Converts the firework's vector of colours into a string of values separated by a semicolon */
+ static AString ColoursToString(const cFireworkItem & a_FireworkItem);
+
+ /** Parses a string containing encoded firework colours and populates a FireworkItem with it */
+ static void ColoursFromString(const AString & a_String, cFireworkItem & a_FireworkItem);
+
+ /** Converts the firework's vector of fade colours into a string of values separated by a semicolon */
+ static AString FadeColoursToString(const cFireworkItem & a_FireworkItem);
+
+ /** Parses a string containing encoded firework fade colours and populates a FireworkItem with it */
+ static void FadeColoursFromString(const AString & a_String, cFireworkItem & a_FireworkItem);
+
+ /** Returns a colour code for fireworks used by the network code */
+ static int GetVanillaColourCodeFromDye(short a_DyeMeta);
+
+ bool m_HasFlicker;
+ bool m_HasTrail;
+ NIBBLETYPE m_Type;
+ short m_FlightTimeInTicks;
+ std::vector<int> m_Colours;
+ std::vector<int> m_FadeColours;
+};
diff --git a/src/WorldStorage/MapSerializer.cpp b/src/WorldStorage/MapSerializer.cpp
new file mode 100644
index 000000000..df72d1cc9
--- /dev/null
+++ b/src/WorldStorage/MapSerializer.cpp
@@ -0,0 +1,280 @@
+
+// MapSerializer.cpp
+
+
+#include "Globals.h"
+#include "MapSerializer.h"
+#include "../StringCompression.h"
+#include "zlib/zlib.h"
+#include "FastNBT.h"
+
+#include "../Map.h"
+#include "../World.h"
+
+
+
+
+
+cMapSerializer::cMapSerializer(const AString& a_WorldName, cMap * a_Map)
+ : m_Map(a_Map)
+{
+ AString DataPath;
+ Printf(DataPath, "%s/data", a_WorldName.c_str());
+
+ Printf(m_Path, "%s/map_%i.dat", DataPath.c_str(), a_Map->GetID());
+
+ cFile::CreateFolder(FILE_IO_PREFIX + DataPath);
+}
+
+
+
+
+
+bool cMapSerializer::Load(void)
+{
+ AString Data = cFile::ReadWholeFile(FILE_IO_PREFIX + m_Path);
+ if (Data.empty())
+ {
+ return false;
+ }
+
+ AString Uncompressed;
+ int res = UncompressStringGZIP(Data.data(), Data.size(), Uncompressed);
+
+ if (res != Z_OK)
+ {
+ return false;
+ }
+
+ // Parse the NBT data:
+ cParsedNBT NBT(Uncompressed.data(), Uncompressed.size());
+ if (!NBT.IsValid())
+ {
+ // NBT Parsing failed
+ return false;
+ }
+
+ return LoadMapFromNBT(NBT);
+}
+
+
+
+
+
+bool cMapSerializer::Save(void)
+{
+ cFastNBTWriter Writer;
+
+ SaveMapToNBT(Writer);
+
+ Writer.Finish();
+
+ #ifdef _DEBUG
+ cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size());
+ ASSERT(TestParse.IsValid());
+ #endif // _DEBUG
+
+ cFile File;
+ if (!File.Open(FILE_IO_PREFIX + m_Path, cFile::fmWrite))
+ {
+ return false;
+ }
+
+ AString Compressed;
+ int res = CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
+
+ if (res != Z_OK)
+ {
+ return false;
+ }
+
+ File.Write(Compressed.data(), Compressed.size());
+ File.Close();
+
+ return true;
+}
+
+
+
+
+
+void cMapSerializer::SaveMapToNBT(cFastNBTWriter & a_Writer)
+{
+ a_Writer.BeginCompound("data");
+
+ a_Writer.AddByte("scale", m_Map->GetScale());
+ a_Writer.AddByte("dimension", (int) m_Map->GetDimension());
+
+ a_Writer.AddShort("width", m_Map->GetWidth());
+ a_Writer.AddShort("height", m_Map->GetHeight());
+
+ a_Writer.AddInt("xCenter", m_Map->GetCenterX());
+ a_Writer.AddInt("zCenter", m_Map->GetCenterZ());
+
+ const cMap::cColorList & Data = m_Map->GetData();
+ a_Writer.AddByteArray("colors", (char *) &Data[0], Data.size());
+
+ a_Writer.EndCompound();
+}
+
+
+
+
+
+bool cMapSerializer::LoadMapFromNBT(const cParsedNBT & a_NBT)
+{
+ int Data = a_NBT.FindChildByName(0, "data");
+ if (Data < 0)
+ {
+ return false;
+ }
+
+ int CurrLine = a_NBT.FindChildByName(Data, "scale");
+ if (CurrLine >= 0)
+ {
+ unsigned int Scale = a_NBT.GetByte(CurrLine);
+ m_Map->SetScale(Scale);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "dimension");
+ if (CurrLine >= 0)
+ {
+ eDimension Dimension = (eDimension) a_NBT.GetByte(CurrLine);
+
+ if (Dimension != m_Map->m_World->GetDimension())
+ {
+ // TODO 2014-03-20 xdot: We should store nether maps in nether worlds, e.t.c.
+ return false;
+ }
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "width");
+ if (CurrLine >= 0)
+ {
+ unsigned int Width = a_NBT.GetShort(CurrLine);
+ m_Map->m_Width = Width;
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "height");
+ if (CurrLine >= 0)
+ {
+ unsigned int Height = a_NBT.GetShort(CurrLine);
+ m_Map->m_Height = Height;
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "xCenter");
+ if (CurrLine >= 0)
+ {
+ int CenterX = a_NBT.GetInt(CurrLine);
+ m_Map->m_CenterX = CenterX;
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "zCenter");
+ if (CurrLine >= 0)
+ {
+ int CenterZ = a_NBT.GetInt(CurrLine);
+ m_Map->m_CenterZ = CenterZ;
+ }
+
+ unsigned int NumPixels = m_Map->GetNumPixels();
+ m_Map->m_Data.resize(NumPixels);
+
+ CurrLine = a_NBT.FindChildByName(Data, "colors");
+ if ((CurrLine >= 0) && (a_NBT.GetType(CurrLine) == TAG_ByteArray))
+ {
+ memcpy(&m_Map->m_Data[0], a_NBT.GetData(CurrLine), NumPixels);
+ }
+
+ return true;
+}
+
+
+
+
+
+cIDCountSerializer::cIDCountSerializer(const AString & a_WorldName) : m_MapCount(0)
+{
+ AString DataPath;
+ Printf(DataPath, "%s/data", a_WorldName.c_str());
+
+ Printf(m_Path, "%s/idcounts.dat", DataPath.c_str());
+
+ cFile::CreateFolder(FILE_IO_PREFIX + DataPath);
+}
+
+
+
+
+
+bool cIDCountSerializer::Load(void)
+{
+ AString Data = cFile::ReadWholeFile(FILE_IO_PREFIX + m_Path);
+ if (Data.empty())
+ {
+ return false;
+ }
+
+ // NOTE: idcounts.dat is not compressed (raw format)
+
+ // Parse the NBT data:
+ cParsedNBT NBT(Data.data(), Data.size());
+ if (!NBT.IsValid())
+ {
+ // NBT Parsing failed
+ return false;
+ }
+
+ int CurrLine = NBT.FindChildByName(0, "map");
+ if (CurrLine >= 0)
+ {
+ m_MapCount = (int)NBT.GetShort(CurrLine) + 1;
+ }
+ else
+ {
+ m_MapCount = 0;
+ }
+
+ return true;
+}
+
+
+
+
+
+bool cIDCountSerializer::Save(void)
+{
+ cFastNBTWriter Writer;
+
+ if (m_MapCount > 0)
+ {
+ Writer.AddShort("map", m_MapCount - 1);
+ }
+
+ Writer.Finish();
+
+ #ifdef _DEBUG
+ cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size());
+ ASSERT(TestParse.IsValid());
+ #endif // _DEBUG
+
+ cFile File;
+ if (!File.Open(FILE_IO_PREFIX + m_Path, cFile::fmWrite))
+ {
+ return false;
+ }
+
+ // NOTE: idcounts.dat is not compressed (raw format)
+
+ File.Write(Writer.GetResult().data(), Writer.GetResult().size());
+ File.Close();
+
+ return true;
+}
+
+
+
+
+
+
+
+
diff --git a/src/WorldStorage/MapSerializer.h b/src/WorldStorage/MapSerializer.h
new file mode 100644
index 000000000..eb7678a08
--- /dev/null
+++ b/src/WorldStorage/MapSerializer.h
@@ -0,0 +1,86 @@
+
+// MapSerializer.h
+
+// Declares the cMapSerializer class that is used for saving maps into NBT format used by Anvil
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd:
+class cFastNBTWriter;
+class cParsedNBT;
+class cMap;
+
+
+
+
+/** Utility class used to serialize maps. */
+class cMapSerializer
+{
+public:
+
+ cMapSerializer(const AString& a_WorldName, cMap * a_Map);
+
+ /** Try to load the scoreboard */
+ bool Load(void);
+
+ /** Try to save the scoreboard */
+ bool Save(void);
+
+
+private:
+
+ void SaveMapToNBT(cFastNBTWriter & a_Writer);
+
+ bool LoadMapFromNBT(const cParsedNBT & a_NBT);
+
+ cMap * m_Map;
+
+ AString m_Path;
+
+
+} ;
+
+
+
+
+/** Utility class used to serialize item ID counts.
+ *
+ * In order to perform bounds checking (while loading),
+ * the last registered ID of each item is serialized to an NBT file.
+ */
+class cIDCountSerializer
+{
+public:
+
+ cIDCountSerializer(const AString & a_WorldName);
+
+ /** Try to load the ID counts */
+ bool Load(void);
+
+ /** Try to save the ID counts */
+ bool Save(void);
+
+ inline unsigned int GetMapCount(void) const { return m_MapCount; }
+
+ inline void SetMapCount(unsigned int a_MapCount) { m_MapCount = a_MapCount; }
+
+
+private:
+
+ AString m_Path;
+
+ unsigned int m_MapCount;
+
+};
+
+
+
+
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
index c84128022..415693ae2 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -19,13 +19,20 @@
#include "../BlockEntities/JukeboxEntity.h"
#include "../BlockEntities/NoteEntity.h"
#include "../BlockEntities/SignEntity.h"
+#include "../BlockEntities/MobHeadEntity.h"
+#include "../BlockEntities/FlowerPotEntity.h"
#include "../Entities/Entity.h"
+#include "../Entities/EnderCrystal.h"
#include "../Entities/FallingBlock.h"
#include "../Entities/Boat.h"
#include "../Entities/Minecart.h"
#include "../Entities/Pickup.h"
#include "../Entities/ProjectileEntity.h"
+#include "../Entities/TNTEntity.h"
+#include "../Entities/ExpOrb.h"
+#include "../Entities/HangingEntity.h"
+#include "../Entities/ItemFrame.h"
#include "../Mobs/Monster.h"
#include "../Mobs/Bat.h"
@@ -37,6 +44,7 @@
#include "../Mobs/Slime.h"
#include "../Mobs/Skeleton.h"
#include "../Mobs/Villager.h"
+#include "../Mobs/Wither.h"
#include "../Mobs/Wolf.h"
#include "../Mobs/Zombie.h"
@@ -89,11 +97,19 @@ void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AStrin
}
// Write the enchantments:
- if (!a_Item.m_Enchantments.IsEmpty())
+ if (!a_Item.m_Enchantments.IsEmpty() || ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR)))
{
- const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
m_Writer.BeginCompound("tag");
- EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, m_Writer, TagName);
+ if ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR))
+ {
+ cFireworkItem::WriteToNBTCompound(a_Item.m_FireworkItem, m_Writer, (ENUM_ITEM_ID)a_Item.m_ItemType);
+ }
+
+ if (!a_Item.m_Enchantments.IsEmpty())
+ {
+ const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
+ EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, m_Writer, TagName);
+ }
m_Writer.EndCompound();
}
@@ -248,11 +264,38 @@ void cNBTChunkSerializer::AddCommandBlockEntity(cCommandBlockEntity * a_CmdBlock
void cNBTChunkSerializer::AddSignEntity(cSignEntity * a_Sign)
{
m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Sign, "Sign");
- m_Writer.AddString("Text1", a_Sign->GetLine(0));
- m_Writer.AddString("Text2", a_Sign->GetLine(1));
- m_Writer.AddString("Text3", a_Sign->GetLine(2));
- m_Writer.AddString("Text4", a_Sign->GetLine(3));
+ AddBasicTileEntity(a_Sign, "Sign");
+ m_Writer.AddString("Text1", a_Sign->GetLine(0));
+ m_Writer.AddString("Text2", a_Sign->GetLine(1));
+ m_Writer.AddString("Text3", a_Sign->GetLine(2));
+ m_Writer.AddString("Text4", a_Sign->GetLine(3));
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddMobHeadEntity(cMobHeadEntity * a_MobHead)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_MobHead, "Skull");
+ m_Writer.AddByte ("SkullType", a_MobHead->GetType() & 0xFF);
+ m_Writer.AddByte ("Rot", a_MobHead->GetRotation() & 0xFF);
+ m_Writer.AddString("ExtraType", a_MobHead->GetOwner());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddFlowerPotEntity(cFlowerPotEntity * a_FlowerPot)
+{
+ m_Writer.BeginCompound("");
+ AddBasicTileEntity(a_FlowerPot, "FlowerPot");
+ m_Writer.AddInt ("Item", (Int32) a_FlowerPot->GetItem().m_ItemType);
+ m_Writer.AddInt ("Data", (Int32) a_FlowerPot->GetItem().m_ItemDamage);
m_Writer.EndCompound();
}
@@ -294,6 +337,17 @@ void cNBTChunkSerializer::AddBoatEntity(cBoat * a_Boat)
+void cNBTChunkSerializer::AddEnderCrystalEntity(cEnderCrystal * a_EnderCrystal)
+{
+ m_Writer.BeginCompound("");
+ AddBasicEntity(a_EnderCrystal, "EnderCrystal");
+ m_Writer.EndCompound();
+}
+
+
+
+
+
void cNBTChunkSerializer::AddFallingBlockEntity(cFallingBlock * a_FallingBlock)
{
m_Writer.BeginCompound("");
@@ -381,7 +435,7 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
case cMonster::mtSquid: EntityClass = "Squid"; break;
case cMonster::mtVillager: EntityClass = "Villager"; break;
case cMonster::mtWitch: EntityClass = "Witch"; break;
- case cMonster::mtWither: EntityClass = "Wither"; break;
+ case cMonster::mtWither: EntityClass = "WitherBoss"; break;
case cMonster::mtWolf: EntityClass = "Wolf"; break;
case cMonster::mtZombie: EntityClass = "Zombie"; break;
case cMonster::mtZombiePigman: EntityClass = "PigZombie"; break;
@@ -394,6 +448,14 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
m_Writer.BeginCompound("");
AddBasicEntity(a_Monster, EntityClass);
+ m_Writer.BeginList("DropChances", TAG_Float);
+ m_Writer.AddFloat("", a_Monster->GetDropChanceWeapon());
+ m_Writer.AddFloat("", a_Monster->GetDropChanceHelmet());
+ m_Writer.AddFloat("", a_Monster->GetDropChanceChestplate());
+ m_Writer.AddFloat("", a_Monster->GetDropChanceLeggings());
+ m_Writer.AddFloat("", a_Monster->GetDropChanceBoots());
+ m_Writer.EndList();
+ m_Writer.AddByte("CanPickUpLoot", (char)a_Monster->CanPickUpLoot());
switch (a_Monster->GetMobType())
{
case cMonster::mtBat:
@@ -452,10 +514,17 @@ void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
m_Writer.AddInt("Profession", ((const cVillager *)a_Monster)->GetVilType());
break;
}
+ case cMonster::mtWither:
+ {
+ m_Writer.AddInt("Invul", ((const cWither *)a_Monster)->GetNumInvulnerableTicks());
+ break;
+ }
case cMonster::mtWolf:
{
- // TODO:
- // _X: CopyPasta error: m_Writer.AddInt("Profession", ((const cVillager *)a_Monster)->GetVilType());
+ m_Writer.AddString("Owner", ((const cWolf *)a_Monster)->GetOwner());
+ m_Writer.AddByte("Sitting", (((const cWolf *)a_Monster)->IsSitting() ? 1 : 0));
+ m_Writer.AddByte("Angry", (((const cWolf *)a_Monster)->IsAngry() ? 1 : 0));
+ m_Writer.AddInt("CollarColor", ((const cWolf *)a_Monster)->GetCollarColor());
break;
}
case cMonster::mtZombie:
@@ -478,8 +547,8 @@ void cNBTChunkSerializer::AddPickupEntity(cPickup * a_Pickup)
m_Writer.BeginCompound("");
AddBasicEntity(a_Pickup, "Item");
AddItem(a_Pickup->GetItem(), -1, "Item");
- m_Writer.AddShort("Health", a_Pickup->GetHealth());
- m_Writer.AddShort("Age", a_Pickup->GetAge());
+ m_Writer.AddShort("Health", (Int16)(unsigned char)a_Pickup->GetHealth());
+ m_Writer.AddShort("Age", (Int16)a_Pickup->GetAge());
m_Writer.EndCompound();
}
@@ -544,6 +613,66 @@ void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile)
+void cNBTChunkSerializer::AddHangingEntity(cHangingEntity * a_Hanging)
+{
+ m_Writer.AddByte("Direction", (unsigned char)a_Hanging->GetDirection());
+ m_Writer.AddInt("TileX", a_Hanging->GetTileX());
+ m_Writer.AddInt("TileY", a_Hanging->GetTileY());
+ m_Writer.AddInt("TileZ", a_Hanging->GetTileZ());
+ switch (a_Hanging->GetDirection())
+ {
+ case 0: m_Writer.AddByte("Dir", (unsigned char)2); break;
+ case 1: m_Writer.AddByte("Dir", (unsigned char)1); break;
+ case 2: m_Writer.AddByte("Dir", (unsigned char)0); break;
+ case 3: m_Writer.AddByte("Dir", (unsigned char)3); break;
+ }
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddTNTEntity(cTNTEntity * a_TNT)
+{
+ m_Writer.BeginCompound("");
+ AddBasicEntity(a_TNT, "PrimedTnt");
+ m_Writer.AddByte("Fuse", (unsigned char)a_TNT->GetFuseTicks());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddExpOrbEntity(cExpOrb * a_ExpOrb)
+{
+ m_Writer.BeginCompound("");
+ AddBasicEntity(a_ExpOrb, "XPOrb");
+ m_Writer.AddShort("Health", (Int16)(unsigned char)a_ExpOrb->GetHealth());
+ m_Writer.AddShort("Age", (Int16)a_ExpOrb->GetAge());
+ m_Writer.AddShort("Value", (Int16)a_ExpOrb->GetReward());
+ m_Writer.EndCompound();
+}
+
+
+
+
+
+void cNBTChunkSerializer::AddItemFrameEntity(cItemFrame * a_ItemFrame)
+{
+ m_Writer.BeginCompound("");
+ AddBasicEntity(a_ItemFrame, "ItemFrame");
+ AddHangingEntity(a_ItemFrame);
+ AddItem(a_ItemFrame->GetItem(), -1, "Item");
+ m_Writer.AddByte("ItemRotation", (unsigned char)a_ItemFrame->GetRotation());
+ m_Writer.AddFloat("ItemDropChance", 1.0F);
+ m_Writer.EndCompound();
+}
+
+
+
+
+
void cNBTChunkSerializer::AddMinecartChestContents(cMinecartWithChest * a_Minecart)
{
m_Writer.BeginList("Items", TAG_Compound);
@@ -618,12 +747,16 @@ void cNBTChunkSerializer::Entity(cEntity * a_Entity)
switch (a_Entity->GetEntityType())
{
case cEntity::etBoat: AddBoatEntity ((cBoat *) a_Entity); break;
+ case cEntity::etEnderCrystal: AddEnderCrystalEntity((cEnderCrystal *) a_Entity); break;
case cEntity::etFallingBlock: AddFallingBlockEntity((cFallingBlock *) a_Entity); break;
case cEntity::etMinecart: AddMinecartEntity ((cMinecart *) a_Entity); break;
case cEntity::etMonster: AddMonsterEntity ((cMonster *) a_Entity); break;
case cEntity::etPickup: AddPickupEntity ((cPickup *) a_Entity); break;
case cEntity::etProjectile: AddProjectileEntity ((cProjectileEntity *)a_Entity); break;
- case cEntity::etExpOrb: /* TODO */ break;
+ case cEntity::etTNT: AddTNTEntity ((cTNTEntity *) a_Entity); break;
+ case cEntity::etExpOrb: AddExpOrbEntity ((cExpOrb *) a_Entity); break;
+ case cEntity::etItemFrame: AddItemFrameEntity ((cItemFrame *) a_Entity); break;
+ case cEntity::etPainting: /* TODO */ break;
case cEntity::etPlayer: return; // Players aren't saved into the world
default:
{
@@ -652,20 +785,23 @@ void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity)
m_Writer.BeginList("TileEntities", TAG_Compound);
}
m_IsTagOpen = true;
-
+
// Add tile-entity into NBT:
switch (a_Entity->GetBlockType())
{
- case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break;
- case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break;
- case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break;
- case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break;
- case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break;
+ case E_BLOCK_CHEST: AddChestEntity ((cChestEntity *) a_Entity); break;
+ case E_BLOCK_DISPENSER: AddDispenserEntity ((cDispenserEntity *) a_Entity); break;
+ case E_BLOCK_DROPPER: AddDropperEntity ((cDropperEntity *) a_Entity); break;
+ case E_BLOCK_FLOWER_POT: AddFlowerPotEntity ((cFlowerPotEntity *) a_Entity); break;
+ case E_BLOCK_FURNACE: AddFurnaceEntity ((cFurnaceEntity *) a_Entity); break;
+ case E_BLOCK_HOPPER: AddHopperEntity ((cHopperEntity *) a_Entity); break;
case E_BLOCK_SIGN_POST:
- case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break;
- case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break;
- case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break;
+ case E_BLOCK_WALLSIGN: AddSignEntity ((cSignEntity *) a_Entity); break;
+ case E_BLOCK_HEAD: AddMobHeadEntity ((cMobHeadEntity *) a_Entity); break;
+ case E_BLOCK_NOTE_BLOCK: AddNoteEntity ((cNoteEntity *) a_Entity); break;
+ case E_BLOCK_JUKEBOX: AddJukeboxEntity ((cJukeboxEntity *) a_Entity); break;
case E_BLOCK_COMMAND_BLOCK: AddCommandBlockEntity((cCommandBlockEntity *) a_Entity); break;
+
default:
{
ASSERT(!"Unhandled block entity saved into Anvil");
diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h
index 245b68063..51d104970 100644
--- a/src/WorldStorage/NBTChunkSerializer.h
+++ b/src/WorldStorage/NBTChunkSerializer.h
@@ -24,11 +24,14 @@ class cChestEntity;
class cCommandBlockEntity;
class cDispenserEntity;
class cDropperEntity;
+class cEnderCrystal;
class cFurnaceEntity;
class cHopperEntity;
class cJukeboxEntity;
class cNoteEntity;
class cSignEntity;
+class cMobHeadEntity;
+class cFlowerPotEntity;
class cFallingBlock;
class cMinecart;
class cMinecartWithChest;
@@ -39,6 +42,10 @@ class cMonster;
class cPickup;
class cItemGrid;
class cProjectileEntity;
+class cTNTEntity;
+class cExpOrb;
+class cHangingEntity;
+class cItemFrame;
@@ -93,16 +100,23 @@ protected:
void AddJukeboxEntity (cJukeboxEntity * a_Jukebox);
void AddNoteEntity (cNoteEntity * a_Note);
void AddSignEntity (cSignEntity * a_Sign);
+ void AddMobHeadEntity (cMobHeadEntity * a_MobHead);
void AddCommandBlockEntity(cCommandBlockEntity * a_CmdBlock);
+ void AddFlowerPotEntity(cFlowerPotEntity * a_FlowerPot);
// Entities:
void AddBasicEntity (cEntity * a_Entity, const AString & a_ClassName);
void AddBoatEntity (cBoat * a_Boat);
+ void AddEnderCrystalEntity(cEnderCrystal * a_EnderCrystal);
void AddFallingBlockEntity(cFallingBlock * a_FallingBlock);
void AddMinecartEntity (cMinecart * a_Minecart);
void AddMonsterEntity (cMonster * a_Monster);
void AddPickupEntity (cPickup * a_Pickup);
void AddProjectileEntity (cProjectileEntity * a_Projectile);
+ void AddHangingEntity (cHangingEntity * a_Hanging);
+ void AddTNTEntity (cTNTEntity * a_TNT);
+ void AddExpOrbEntity (cExpOrb * a_ExpOrb);
+ void AddItemFrameEntity (cItemFrame * a_ItemFrame);
void AddMinecartChestContents(cMinecartWithChest * a_Minecart);
diff --git a/src/WorldStorage/SchematicFileSerializer.cpp b/src/WorldStorage/SchematicFileSerializer.cpp
new file mode 100644
index 000000000..9d594a084
--- /dev/null
+++ b/src/WorldStorage/SchematicFileSerializer.cpp
@@ -0,0 +1,305 @@
+
+// SchematicFileSerializer.cpp
+
+// Implements the cSchematicFileSerializer class representing the interface to load and save cBlockArea to a .schematic file
+
+#include "Globals.h"
+
+#include "OSSupport/GZipFile.h"
+#include "FastNBT.h"
+#include "SchematicFileSerializer.h"
+#include "../StringCompression.h"
+
+
+
+
+
+#ifdef SELF_TEST
+
+static class cSchematicStringSelfTest
+{
+public:
+ cSchematicStringSelfTest(void)
+ {
+ cBlockArea ba;
+ ba.Create(21, 256, 21);
+ ba.RelLine(0, 0, 0, 9, 8, 7, cBlockArea::baTypes | cBlockArea::baMetas, E_BLOCK_WOODEN_STAIRS, 1);
+ AString Schematic;
+ if (!cSchematicFileSerializer::SaveToSchematicString(ba, Schematic))
+ {
+ assert_test(!"Schematic failed to save!");
+ }
+ cBlockArea ba2;
+ if (!cSchematicFileSerializer::LoadFromSchematicString(ba2, Schematic))
+ {
+ assert_test(!"Schematic failed to load!");
+ }
+ }
+} g_SelfTest;
+
+#endif
+
+
+
+
+
+
+///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+// cSchematicFileSerializer:
+
+bool cSchematicFileSerializer::LoadFromSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName)
+{
+ // Un-GZip the contents:
+ AString Contents;
+ cGZipFile File;
+ if (!File.Open(a_FileName, cGZipFile::fmRead))
+ {
+ LOG("Cannot open the schematic file \"%s\".", a_FileName.c_str());
+ return false;
+ }
+ int NumBytesRead = File.ReadRestOfFile(Contents);
+ if (NumBytesRead < 0)
+ {
+ LOG("Cannot read GZipped data in the schematic file \"%s\", error %d", a_FileName.c_str(), NumBytesRead);
+ return false;
+ }
+ File.Close();
+
+ // Parse the NBT:
+ cParsedNBT NBT(Contents.data(), Contents.size());
+ if (!NBT.IsValid())
+ {
+ LOG("Cannot parse the NBT in the schematic file \"%s\".", a_FileName.c_str());
+ return false;
+ }
+
+ return LoadFromSchematicNBT(a_BlockArea, NBT);
+}
+
+
+
+
+
+
+bool cSchematicFileSerializer::LoadFromSchematicString(cBlockArea & a_BlockArea, const AString & a_SchematicData)
+{
+ // Uncompress the data:
+ AString UngzippedData;
+ if (UncompressStringGZIP(a_SchematicData.data(), a_SchematicData.size(), UngzippedData) != Z_OK)
+ {
+ LOG("%s: Cannot unGZip the schematic data.", __FUNCTION__);
+ return false;
+ }
+
+ // Parse the NBT:
+ cParsedNBT NBT(UngzippedData.data(), UngzippedData.size());
+ if (!NBT.IsValid())
+ {
+ LOG("%s: Cannot parse the NBT in the schematic data.", __FUNCTION__);
+ return false;
+ }
+
+ return LoadFromSchematicNBT(a_BlockArea, NBT);
+}
+
+
+
+
+
+bool cSchematicFileSerializer::SaveToSchematicFile(const cBlockArea & a_BlockArea, const AString & a_FileName)
+{
+ // Serialize into NBT data:
+ AString NBT = SaveToSchematicNBT(a_BlockArea);
+ if (NBT.empty())
+ {
+ LOG("%s: Cannot serialize the area into an NBT representation for file \"%s\".", __FUNCTION__, a_FileName.c_str());
+ return false;
+ }
+
+ // Save to file
+ cGZipFile File;
+ if (!File.Open(a_FileName, cGZipFile::fmWrite))
+ {
+ LOG("%s: Cannot open file \"%s\" for writing.", __FUNCTION__, a_FileName.c_str());
+ return false;
+ }
+ if (!File.Write(NBT))
+ {
+ LOG("%s: Cannot write data to file \"%s\".", __FUNCTION__, a_FileName.c_str());
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+
+bool cSchematicFileSerializer::SaveToSchematicString(const cBlockArea & a_BlockArea, AString & a_Out)
+{
+ // Serialize into NBT data:
+ AString NBT = SaveToSchematicNBT(a_BlockArea);
+ if (NBT.empty())
+ {
+ LOG("%s: Cannot serialize the area into an NBT representation.", __FUNCTION__);
+ return false;
+ }
+
+ // Gzip the data:
+ int res = CompressStringGZIP(NBT.data(), NBT.size(), a_Out);
+ if (res != Z_OK)
+ {
+ LOG("%s: Cannot Gzip the area data NBT representation: %d", __FUNCTION__, res);
+ return false;
+ }
+ return true;
+}
+
+
+
+
+
+bool cSchematicFileSerializer::LoadFromSchematicNBT(cBlockArea & a_BlockArea, cParsedNBT & a_NBT)
+{
+ int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials");
+ if ((TMaterials > 0) && (a_NBT.GetType(TMaterials) == TAG_String))
+ {
+ AString Materials = a_NBT.GetString(TMaterials);
+ if (Materials.compare("Alpha") != 0)
+ {
+ LOG("Materials tag is present and \"%s\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials.c_str());
+ return false;
+ }
+ }
+ int TSizeX = a_NBT.FindChildByName(a_NBT.GetRoot(), "Width");
+ int TSizeY = a_NBT.FindChildByName(a_NBT.GetRoot(), "Height");
+ int TSizeZ = a_NBT.FindChildByName(a_NBT.GetRoot(), "Length");
+ if (
+ (TSizeX < 0) || (TSizeY < 0) || (TSizeZ < 0) ||
+ (a_NBT.GetType(TSizeX) != TAG_Short) ||
+ (a_NBT.GetType(TSizeY) != TAG_Short) ||
+ (a_NBT.GetType(TSizeZ) != TAG_Short)
+ )
+ {
+ LOG("Dimensions are missing from the schematic file (%d, %d, %d), (%d, %d, %d)",
+ TSizeX, TSizeY, TSizeZ,
+ a_NBT.GetType(TSizeX), a_NBT.GetType(TSizeY), a_NBT.GetType(TSizeZ)
+ );
+ return false;
+ }
+
+ int SizeX = a_NBT.GetShort(TSizeX);
+ int SizeY = a_NBT.GetShort(TSizeY);
+ int SizeZ = a_NBT.GetShort(TSizeZ);
+ if ((SizeX < 1) || (SizeY < 1) || (SizeZ < 1))
+ {
+ LOG("Dimensions are invalid in the schematic file: %d, %d, %d", SizeX, SizeY, SizeZ);
+ return false;
+ }
+
+ int TBlockTypes = a_NBT.FindChildByName(a_NBT.GetRoot(), "Blocks");
+ int TBlockMetas = a_NBT.FindChildByName(a_NBT.GetRoot(), "Data");
+ if ((TBlockTypes < 0) || (a_NBT.GetType(TBlockTypes) != TAG_ByteArray))
+ {
+ LOG("BlockTypes are invalid in the schematic file: %d", TBlockTypes);
+ return false;
+ }
+ bool AreMetasPresent = (TBlockMetas > 0) && (a_NBT.GetType(TBlockMetas) == TAG_ByteArray);
+
+ a_BlockArea.Clear();
+ a_BlockArea.SetSize(SizeX, SizeY, SizeZ, AreMetasPresent ? (cBlockArea::baTypes | cBlockArea::baMetas) : cBlockArea::baTypes);
+
+ int TOffsetX = a_NBT.FindChildByName(a_NBT.GetRoot(), "WEOffsetX");
+ int TOffsetY = a_NBT.FindChildByName(a_NBT.GetRoot(), "WEOffsetY");
+ int TOffsetZ = a_NBT.FindChildByName(a_NBT.GetRoot(), "WEOffsetZ");
+
+ if (
+ (TOffsetX < 0) || (TOffsetY < 0) || (TOffsetZ < 0) ||
+ (a_NBT.GetType(TOffsetX) != TAG_Int) ||
+ (a_NBT.GetType(TOffsetY) != TAG_Int) ||
+ (a_NBT.GetType(TOffsetZ) != TAG_Int)
+ )
+ {
+ // Not every schematic file has an offset, so we shoudn't give a warn message.
+ a_BlockArea.SetWEOffset(0, 0, 0);
+ }
+ else
+ {
+ a_BlockArea.SetWEOffset(a_NBT.GetInt(TOffsetX), a_NBT.GetInt(TOffsetY), a_NBT.GetInt(TOffsetZ));
+ }
+
+ // Copy the block types and metas:
+ int NumBytes = a_BlockArea.GetBlockCount();
+ if (a_NBT.GetDataLength(TBlockTypes) < NumBytes)
+ {
+ LOG("BlockTypes truncated in the schematic file (exp %d, got %d bytes). Loading partial.",
+ NumBytes, a_NBT.GetDataLength(TBlockTypes)
+ );
+ NumBytes = a_NBT.GetDataLength(TBlockTypes);
+ }
+ memcpy(a_BlockArea.m_BlockTypes, a_NBT.GetData(TBlockTypes), NumBytes);
+
+ if (AreMetasPresent)
+ {
+ int NumBytes = a_BlockArea.GetBlockCount();
+ if (a_NBT.GetDataLength(TBlockMetas) < NumBytes)
+ {
+ LOG("BlockMetas truncated in the schematic file (exp %d, got %d bytes). Loading partial.",
+ NumBytes, a_NBT.GetDataLength(TBlockMetas)
+ );
+ NumBytes = a_NBT.GetDataLength(TBlockMetas);
+ }
+ memcpy(a_BlockArea.m_BlockMetas, a_NBT.GetData(TBlockMetas), NumBytes);
+ }
+
+ return true;
+}
+
+
+
+
+
+AString cSchematicFileSerializer::SaveToSchematicNBT(const cBlockArea & a_BlockArea)
+{
+ cFastNBTWriter Writer("Schematic");
+ Writer.AddShort("Width", a_BlockArea.m_Size.x);
+ Writer.AddShort("Height", a_BlockArea.m_Size.y);
+ Writer.AddShort("Length", a_BlockArea.m_Size.z);
+ Writer.AddString("Materials", "Alpha");
+ if (a_BlockArea.HasBlockTypes())
+ {
+ Writer.AddByteArray("Blocks", (const char *)a_BlockArea.m_BlockTypes, a_BlockArea.GetBlockCount());
+ }
+ else
+ {
+ AString Dummy(a_BlockArea.GetBlockCount(), 0);
+ Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size());
+ }
+ if (a_BlockArea.HasBlockMetas())
+ {
+ Writer.AddByteArray("Data", (const char *)a_BlockArea.m_BlockMetas, a_BlockArea.GetBlockCount());
+ }
+ else
+ {
+ AString Dummy(a_BlockArea.GetBlockCount(), 0);
+ Writer.AddByteArray("Data", Dummy.data(), Dummy.size());
+ }
+
+ Writer.AddInt("WEOffsetX", a_BlockArea.m_WEOffset.x);
+ Writer.AddInt("WEOffsetY", a_BlockArea.m_WEOffset.y);
+ Writer.AddInt("WEOffsetZ", a_BlockArea.m_WEOffset.z);
+
+ // TODO: Save entities and block entities
+ Writer.BeginList("Entities", TAG_Compound);
+ Writer.EndList();
+ Writer.BeginList("TileEntities", TAG_Compound);
+ Writer.EndList();
+ Writer.Finish();
+
+ return Writer.GetResult();
+}
+
+
+
+
diff --git a/src/WorldStorage/SchematicFileSerializer.h b/src/WorldStorage/SchematicFileSerializer.h
new file mode 100644
index 000000000..05b6c74f4
--- /dev/null
+++ b/src/WorldStorage/SchematicFileSerializer.h
@@ -0,0 +1,54 @@
+
+// SchematicFileSerializer.h
+
+// Declares the cSchematicFileSerializer class representing the interface to load and save cBlockArea to a .schematic file
+
+
+
+
+
+#pragma once
+
+#include "../BlockArea.h"
+
+
+
+
+
+// fwd: FastNBT.h
+class cParsedNBT;
+
+
+
+
+
+class cSchematicFileSerializer
+{
+public:
+
+ /** Loads an area from a .schematic file. Returns true if successful. */
+ static bool LoadFromSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName);
+
+ /** Loads an area from a string containing the .schematic file data. Returns true if successful. */
+ static bool LoadFromSchematicString(cBlockArea & a_BlockArea, const AString & a_SchematicData);
+
+ /** Saves the area into a .schematic file. Returns true if successful. */
+ static bool SaveToSchematicFile(const cBlockArea & a_BlockArea, const AString & a_FileName);
+
+ /** Saves the area into a string containing the .schematic file data.
+ Returns true if successful, false on failure. The data is stored into a_Out. */
+ static bool SaveToSchematicString(const cBlockArea & a_BlockArea, AString & a_Out);
+
+private:
+ /** Loads the area from a schematic file uncompressed and parsed into a NBT tree.
+ Returns true if successful. */
+ static bool LoadFromSchematicNBT(cBlockArea & a_BlockArea, cParsedNBT & a_NBT);
+
+ /** Saves the area into a NBT representation and returns the NBT data as a string.
+ Returns an empty string if failed. */
+ static AString SaveToSchematicNBT(const cBlockArea & a_BlockArea);
+};
+
+
+
+
diff --git a/src/WorldStorage/ScoreboardSerializer.cpp b/src/WorldStorage/ScoreboardSerializer.cpp
new file mode 100644
index 000000000..6c885bb45
--- /dev/null
+++ b/src/WorldStorage/ScoreboardSerializer.cpp
@@ -0,0 +1,377 @@
+
+// ScoreboardSerializer.cpp
+
+
+#include "Globals.h"
+#include "ScoreboardSerializer.h"
+#include "../StringCompression.h"
+#include "zlib/zlib.h"
+#include "FastNBT.h"
+
+#include "../Scoreboard.h"
+
+
+
+
+
+cScoreboardSerializer::cScoreboardSerializer(const AString & a_WorldName, cScoreboard* a_ScoreBoard)
+ : m_ScoreBoard(a_ScoreBoard)
+{
+ AString DataPath;
+ Printf(DataPath, "%s/data", a_WorldName.c_str());
+
+ m_Path = DataPath + "/scoreboard.dat";
+
+ cFile::CreateFolder(FILE_IO_PREFIX + DataPath);
+}
+
+
+
+
+
+bool cScoreboardSerializer::Load(void)
+{
+ AString Data = cFile::ReadWholeFile(FILE_IO_PREFIX + m_Path);
+ if (Data.empty())
+ {
+ return false;
+ }
+
+ AString Uncompressed;
+ int res = UncompressStringGZIP(Data.data(), Data.size(), Uncompressed);
+
+ if (res != Z_OK)
+ {
+ return false;
+ }
+
+ // Parse the NBT data:
+ cParsedNBT NBT(Uncompressed.data(), Uncompressed.size());
+ if (!NBT.IsValid())
+ {
+ // NBT Parsing failed
+ return false;
+ }
+
+ return LoadScoreboardFromNBT(NBT);
+}
+
+
+
+
+
+bool cScoreboardSerializer::Save(void)
+{
+ cFastNBTWriter Writer;
+
+ SaveScoreboardToNBT(Writer);
+
+ Writer.Finish();
+
+ #ifdef _DEBUG
+ cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size());
+ ASSERT(TestParse.IsValid());
+ #endif // _DEBUG
+
+ cFile File;
+ if (!File.Open(FILE_IO_PREFIX + m_Path, cFile::fmWrite))
+ {
+ return false;
+ }
+
+ AString Compressed;
+ int res = CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
+
+ if (res != Z_OK)
+ {
+ return false;
+ }
+
+ File.Write(Compressed.data(), Compressed.size());
+ File.Close();
+
+ return true;
+}
+
+
+
+
+
+void cScoreboardSerializer::SaveScoreboardToNBT(cFastNBTWriter & a_Writer)
+{
+ a_Writer.BeginCompound("data");
+
+ a_Writer.BeginList("Objectives", TAG_Compound);
+
+ for (cScoreboard::cObjectiveMap::const_iterator it = m_ScoreBoard->m_Objectives.begin(); it != m_ScoreBoard->m_Objectives.end(); ++it)
+ {
+ const cObjective & Objective = it->second;
+
+ a_Writer.BeginCompound("");
+
+ a_Writer.AddString("CriteriaName", cObjective::TypeToString(Objective.GetType()));
+
+ a_Writer.AddString("DisplayName", Objective.GetDisplayName());
+ a_Writer.AddString("Name", it->first);
+
+ a_Writer.EndCompound();
+ }
+
+ a_Writer.EndList(); // Objectives
+
+ a_Writer.BeginList("PlayerScores", TAG_Compound);
+
+ for (cScoreboard::cObjectiveMap::const_iterator it = m_ScoreBoard->m_Objectives.begin(); it != m_ScoreBoard->m_Objectives.end(); ++it)
+ {
+ const cObjective & Objective = it->second;
+
+ for (cObjective::cScoreMap::const_iterator it2 = Objective.m_Scores.begin(); it2 != Objective.m_Scores.end(); ++it2)
+ {
+ a_Writer.BeginCompound("");
+
+ a_Writer.AddInt("Score", it2->second);
+
+ a_Writer.AddString("Name", it2->first);
+ a_Writer.AddString("Objective", it->first);
+
+ a_Writer.EndCompound();
+ }
+ }
+
+ a_Writer.EndList(); // PlayerScores
+
+ a_Writer.BeginList("Teams", TAG_Compound);
+
+ for (cScoreboard::cTeamMap::const_iterator it = m_ScoreBoard->m_Teams.begin(); it != m_ScoreBoard->m_Teams.end(); ++it)
+ {
+ const cTeam & Team = it->second;
+
+ a_Writer.BeginCompound("");
+
+ a_Writer.AddByte("AllowFriendlyFire", Team.AllowsFriendlyFire() ? 1 : 0);
+ a_Writer.AddByte("SeeFriendlyInvisibles", Team.CanSeeFriendlyInvisible() ? 1 : 0);
+
+ a_Writer.AddString("DisplayName", Team.GetDisplayName());
+ a_Writer.AddString("Name", it->first);
+
+ a_Writer.AddString("Prefix", Team.GetPrefix());
+ a_Writer.AddString("Suffix", Team.GetSuffix());
+
+ a_Writer.BeginList("Players", TAG_String);
+
+ for (cTeam::cPlayerNameSet::const_iterator it2 = Team.m_Players.begin(); it2 != Team.m_Players.end(); ++it2)
+ {
+ a_Writer.AddString("", *it2);
+ }
+
+ a_Writer.EndList();
+
+ a_Writer.EndCompound();
+ }
+
+ a_Writer.EndList(); // Teams
+
+ a_Writer.BeginCompound("DisplaySlots");
+
+ cObjective * Objective = m_ScoreBoard->GetObjectiveIn(cScoreboard::dsList);
+ a_Writer.AddString("slot_0", (Objective == NULL) ? "" : Objective->GetName());
+
+ Objective = m_ScoreBoard->GetObjectiveIn(cScoreboard::dsSidebar);
+ a_Writer.AddString("slot_1", (Objective == NULL) ? "" : Objective->GetName());
+
+ Objective = m_ScoreBoard->GetObjectiveIn(cScoreboard::dsName);
+ a_Writer.AddString("slot_2", (Objective == NULL) ? "" : Objective->GetName());
+
+ a_Writer.EndCompound(); // DisplaySlots
+
+ a_Writer.EndCompound(); // Data
+}
+
+
+
+
+
+bool cScoreboardSerializer::LoadScoreboardFromNBT(const cParsedNBT & a_NBT)
+{
+ int Data = a_NBT.FindChildByName(0, "data");
+ if (Data < 0)
+ {
+ return false;
+ }
+
+ int Objectives = a_NBT.FindChildByName(Data, "Objectives");
+ if (Objectives < 0)
+ {
+ return false;
+ }
+
+ for (int Child = a_NBT.GetFirstChild(Objectives); Child >= 0; Child = a_NBT.GetNextSibling(Child))
+ {
+ AString CriteriaName, DisplayName, Name;
+
+ int CurrLine = a_NBT.FindChildByName(Child, "CriteriaName");
+ if (CurrLine >= 0)
+ {
+ CriteriaName = a_NBT.GetString(CurrLine);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "DisplayName");
+ if (CurrLine >= 0)
+ {
+ DisplayName = a_NBT.GetString(CurrLine);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "Name");
+ if (CurrLine >= 0)
+ {
+ Name = a_NBT.GetString(CurrLine);
+ }
+
+ cObjective::eType Type = cObjective::StringToType(CriteriaName);
+
+ m_ScoreBoard->RegisterObjective(Name, DisplayName, Type);
+ }
+
+ int PlayerScores = a_NBT.FindChildByName(Data, "PlayerScores");
+ if (PlayerScores < 0)
+ {
+ return false;
+ }
+
+ for (int Child = a_NBT.GetFirstChild(PlayerScores); Child >= 0; Child = a_NBT.GetNextSibling(Child))
+ {
+ AString Name, ObjectiveName;
+
+ cObjective::Score Score = 0;
+
+ int CurrLine = a_NBT.FindChildByName(Child, "Score");
+ if (CurrLine >= 0)
+ {
+ Score = a_NBT.GetInt(CurrLine);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "Name");
+ if (CurrLine >= 0)
+ {
+ Name = a_NBT.GetString(CurrLine);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "Objective");
+ if (CurrLine >= 0)
+ {
+ ObjectiveName = a_NBT.GetString(CurrLine);
+ }
+
+ cObjective * Objective = m_ScoreBoard->GetObjective(ObjectiveName);
+
+ if (Objective)
+ {
+ Objective->SetScore(Name, Score);
+ }
+ }
+
+ int Teams = a_NBT.FindChildByName(Data, "Teams");
+ if (Teams < 0)
+ {
+ return false;
+ }
+
+ for (int Child = a_NBT.GetFirstChild(Teams); Child >= 0; Child = a_NBT.GetNextSibling(Child))
+ {
+ AString Name, DisplayName, Prefix, Suffix;
+
+ bool AllowsFriendlyFire = true, CanSeeFriendlyInvisible = false;
+
+ int CurrLine = a_NBT.FindChildByName(Child, "Name");
+ if (CurrLine >= 0)
+ {
+ Name = a_NBT.GetInt(CurrLine);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "DisplayName");
+ if (CurrLine >= 0)
+ {
+ DisplayName = a_NBT.GetInt(CurrLine);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "Prefix");
+ if (CurrLine >= 0)
+ {
+ Prefix = a_NBT.GetInt(CurrLine);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "Suffix");
+ if (CurrLine >= 0)
+ {
+ Suffix = a_NBT.GetInt(CurrLine);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "AllowFriendlyFire");
+ if (CurrLine >= 0)
+ {
+ AllowsFriendlyFire = (a_NBT.GetInt(CurrLine) != 0);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Child, "SeeFriendlyInvisibles");
+ if (CurrLine >= 0)
+ {
+ CanSeeFriendlyInvisible = (a_NBT.GetInt(CurrLine) != 0);
+ }
+
+ cTeam * Team = m_ScoreBoard->RegisterTeam(Name, DisplayName, Prefix, Suffix);
+
+ Team->SetFriendlyFire(AllowsFriendlyFire);
+ Team->SetCanSeeFriendlyInvisible(CanSeeFriendlyInvisible);
+
+ int Players = a_NBT.FindChildByName(Child, "Players");
+ if (Players < 0)
+ {
+ continue;
+ }
+
+ for (int ChildB = a_NBT.GetFirstChild(Players); ChildB >= 0; ChildB = a_NBT.GetNextSibling(ChildB))
+ {
+ Team->AddPlayer(a_NBT.GetString(ChildB));
+ }
+ }
+
+ int DisplaySlots = a_NBT.FindChildByName(Data, "DisplaySlots");
+ if (DisplaySlots < 0)
+ {
+ return false;
+ }
+
+ int CurrLine = a_NBT.FindChildByName(DisplaySlots, "slot_0");
+ if (CurrLine >= 0)
+ {
+ AString Name = a_NBT.GetString(CurrLine);
+
+ m_ScoreBoard->SetDisplay(Name, cScoreboard::dsList);
+ }
+
+ CurrLine = a_NBT.FindChildByName(DisplaySlots, "slot_1");
+ if (CurrLine >= 0)
+ {
+ AString Name = a_NBT.GetString(CurrLine);
+
+ m_ScoreBoard->SetDisplay(Name, cScoreboard::dsSidebar);
+ }
+
+ CurrLine = a_NBT.FindChildByName(DisplaySlots, "slot_2");
+ if (CurrLine >= 0)
+ {
+ AString Name = a_NBT.GetString(CurrLine);
+
+ m_ScoreBoard->SetDisplay(Name, cScoreboard::dsName);
+ }
+
+ return true;
+}
+
+
+
+
+
+
+
+
diff --git a/src/WorldStorage/ScoreboardSerializer.h b/src/WorldStorage/ScoreboardSerializer.h
new file mode 100644
index 000000000..048fa3ab4
--- /dev/null
+++ b/src/WorldStorage/ScoreboardSerializer.h
@@ -0,0 +1,52 @@
+
+// ScoreboardSerializer.h
+
+// Declares the cScoreboardSerializer class that is used for saving scoreboards into NBT format used by Anvil
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd:
+class cFastNBTWriter;
+class cParsedNBT;
+class cScoreboard;
+
+
+
+
+class cScoreboardSerializer
+{
+public:
+
+ cScoreboardSerializer(const AString & a_WorldName, cScoreboard* a_ScoreBoard);
+
+ /// Try to load the scoreboard
+ bool Load(void);
+
+ /// Try to save the scoreboard
+ bool Save(void);
+
+
+private:
+
+ void SaveScoreboardToNBT(cFastNBTWriter & a_Writer);
+
+ bool LoadScoreboardFromNBT(const cParsedNBT & a_NBT);
+
+ cScoreboard* m_ScoreBoard;
+
+ AString m_Path;
+
+
+} ;
+
+
+
+
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index 8983f25ba..48934d074 100644
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -24,16 +24,23 @@
#include "../BlockEntities/JukeboxEntity.h"
#include "../BlockEntities/NoteEntity.h"
#include "../BlockEntities/SignEntity.h"
+#include "../BlockEntities/MobHeadEntity.h"
+#include "../BlockEntities/FlowerPotEntity.h"
#include "../Mobs/Monster.h"
#include "../Mobs/IncludeAllMonsters.h"
#include "../Entities/Boat.h"
+#include "../Entities/EnderCrystal.h"
#include "../Entities/FallingBlock.h"
#include "../Entities/Minecart.h"
#include "../Entities/Pickup.h"
#include "../Entities/ProjectileEntity.h"
+#include "../Entities/TNTEntity.h"
+#include "../Entities/ExpOrb.h"
+#include "../Entities/HangingEntity.h"
+#include "../Entities/ItemFrame.h"
@@ -363,6 +370,7 @@ bool cWSSAnvil::LoadChunkFromNBT(const cChunkCoords & a_Chunk, const cParsedNBT
{
case E_BLOCK_AIR:
case E_BLOCK_LEAVES:
+ case E_BLOCK_NEW_LEAVES:
{
// nothing needed
break;
@@ -504,10 +512,10 @@ cChunkDef::BiomeMap * cWSSAnvil::LoadBiomeMapFromNBT(cChunkDef::BiomeMap * a_Bio
// The biomes stored don't match in size
return NULL;
}
- const int * BiomeData = (const int *)(a_NBT.GetData(a_TagIdx));
+ const char * BiomeData = (a_NBT.GetData(a_TagIdx));
for (size_t i = 0; i < ARRAYCOUNT(*a_BiomeMap); i++)
{
- (*a_BiomeMap)[i] = (EMCSBiome)(ntohl(BiomeData[i]));
+ (*a_BiomeMap)[i] = (EMCSBiome)(GetBEInt(&BiomeData[i * 4]));
if ((*a_BiomeMap)[i] == 0xff)
{
// Unassigned biomes
@@ -577,6 +585,10 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
{
LoadDropperFromNBT(a_BlockEntities, a_NBT, Child);
}
+ else if (strncmp(a_NBT.GetData(sID), "FlowerPot", a_NBT.GetDataLength(sID)) == 0)
+ {
+ LoadFlowerPotFromNBT(a_BlockEntities, a_NBT, Child);
+ }
else if (strncmp(a_NBT.GetData(sID), "Furnace", a_NBT.GetDataLength(sID)) == 0)
{
LoadFurnaceFromNBT(a_BlockEntities, a_NBT, Child, a_BlockTypes, a_BlockMetas);
@@ -597,6 +609,10 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
{
LoadSignFromNBT(a_BlockEntities, a_NBT, Child);
}
+ else if (strncmp(a_NBT.GetData(sID), "Skull", a_NBT.GetDataLength(sID)) == 0)
+ {
+ LoadMobHeadFromNBT(a_BlockEntities, a_NBT, Child);
+ }
else if (strncmp(a_NBT.GetData(sID), "Trap", a_NBT.GetDataLength(sID)) == 0)
{
LoadDispenserFromNBT(a_BlockEntities, a_NBT, Child);
@@ -611,12 +627,18 @@ void cWSSAnvil::LoadBlockEntitiesFromNBT(cBlockEntityList & a_BlockEntities, con
bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_TagIdx)
{
- int ID = a_NBT.FindChildByName(a_TagIdx, "id");
- if ((ID < 0) || (a_NBT.GetType(ID) != TAG_Short))
+ int Type = a_NBT.FindChildByName(a_TagIdx, "id");
+ if ((Type < 0) || (a_NBT.GetType(Type) != TAG_Short))
{
return false;
}
- a_Item.m_ItemType = (ENUM_ITEM_ID)(a_NBT.GetShort(ID));
+ a_Item.m_ItemType = a_NBT.GetShort(Type);
+ if (a_Item.m_ItemType < 0)
+ {
+ LOGD("Encountered an item with negative type (%d). Replacing with an empty item.", a_NBT.GetShort(Type));
+ a_Item.Empty();
+ return true;
+ }
int Damage = a_NBT.FindChildByName(a_TagIdx, "Damage");
if ((Damage < 0) || (a_NBT.GetType(Damage) != TAG_Short))
@@ -647,6 +669,12 @@ bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_
{
EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, a_NBT, EnchTag);
}
+
+ int FireworksTag = a_NBT.FindChildByName(TagTag, ((a_Item.m_ItemType == E_ITEM_FIREWORK_STAR) ? "Fireworks" : "Explosion"));
+ if (EnchTag > 0)
+ {
+ cFireworkItem::ParseFromNBT(a_Item.m_FireworkItem, a_NBT, FireworksTag, (ENUM_ITEM_ID)a_Item.m_ItemType);
+ }
return true;
}
@@ -749,6 +777,37 @@ void cWSSAnvil::LoadDropperFromNBT(cBlockEntityList & a_BlockEntities, const cPa
+void cWSSAnvil::LoadFlowerPotFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
+ int x, y, z;
+ if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ {
+ return;
+ }
+ std::auto_ptr<cFlowerPotEntity> FlowerPot(new cFlowerPotEntity(x, y, z, m_World));
+ short ItemType = 0, ItemData = 0;
+
+ int currentLine = a_NBT.FindChildByName(a_TagIdx, "Item");
+ if (currentLine >= 0)
+ {
+ ItemType = (short) a_NBT.GetInt(currentLine);
+ }
+
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "Data");
+ if (currentLine >= 0)
+ {
+ ItemData = (short) a_NBT.GetInt(currentLine);
+ }
+
+ FlowerPot->SetItem(cItem(ItemType, 1, ItemData));
+ a_BlockEntities.push_back(FlowerPot.release());
+}
+
+
+
+
+
void cWSSAnvil::LoadFurnaceFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
@@ -921,6 +980,41 @@ void cWSSAnvil::LoadSignFromNBT(cBlockEntityList & a_BlockEntities, const cParse
+void cWSSAnvil::LoadMobHeadFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
+ int x, y, z;
+ if (!GetBlockEntityNBTPos(a_NBT, a_TagIdx, x, y, z))
+ {
+ return;
+ }
+ std::auto_ptr<cMobHeadEntity> MobHead(new cMobHeadEntity(x, y, z, m_World));
+
+ int currentLine = a_NBT.FindChildByName(a_TagIdx, "SkullType");
+ if (currentLine >= 0)
+ {
+ MobHead->SetType(static_cast<eMobHeadType>(a_NBT.GetByte(currentLine)));
+ }
+
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "Rot");
+ if (currentLine >= 0)
+ {
+ MobHead->SetRotation(static_cast<eMobHeadRotation>(a_NBT.GetByte(currentLine)));
+ }
+
+ currentLine = a_NBT.FindChildByName(a_TagIdx, "ExtraType");
+ if (currentLine >= 0)
+ {
+ MobHead->SetOwner(a_NBT.GetString(currentLine));
+ }
+
+ a_BlockEntities.push_back(MobHead.release());
+}
+
+
+
+
+
void cWSSAnvil::LoadCommandBlockFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx)
{
ASSERT(a_NBT.GetType(a_TagIdx) == TAG_Compound);
@@ -964,6 +1058,10 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
LoadBoatFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
+ else if (strncmp(a_IDTag, "EnderCrystal", a_IDTagLength) == 0)
+ {
+ LoadEnderCrystalFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
else if (strncmp(a_IDTag, "FallingBlock", a_IDTagLength) == 0)
{
LoadFallingBlockFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
@@ -1009,6 +1107,18 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
LoadPickupFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
+ else if (strncmp(a_IDTag, "PrimedTnt", a_IDTagLength) == 0)
+ {
+ LoadTNTFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "XPOrb", a_IDTagLength) == 0)
+ {
+ LoadExpOrbFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
+ else if (strncmp(a_IDTag, "ItemFrame", a_IDTagLength) == 0)
+ {
+ LoadItemFrameFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
else if (strncmp(a_IDTag, "Arrow", a_IDTagLength) == 0)
{
LoadArrowFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
@@ -1077,6 +1187,10 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
LoadHorseFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
+ else if (strncmp(a_IDTag, "Villager", a_IDTagLength) == 0)
+ {
+ LoadVillagerFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
+ }
else if (strncmp(a_IDTag, "VillagerGolem", a_IDTagLength) == 0)
{
LoadIronGolemFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
@@ -1125,15 +1239,11 @@ void cWSSAnvil::LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
LoadSquidFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
- else if (strncmp(a_IDTag, "Villager", a_IDTagLength) == 0)
- {
- LoadVillagerFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
- }
else if (strncmp(a_IDTag, "Witch", a_IDTagLength) == 0)
{
LoadWitchFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
- else if (strncmp(a_IDTag, "Wither", a_IDTagLength) == 0)
+ else if (strncmp(a_IDTag, "WitherBoss", a_IDTagLength) == 0)
{
LoadWitherFromNBT(a_Entities, a_NBT, a_EntityTagIdx);
}
@@ -1170,6 +1280,20 @@ void cWSSAnvil::LoadBoatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_N
+void cWSSAnvil::LoadEnderCrystalFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ std::auto_ptr<cEnderCrystal> EnderCrystal(new cEnderCrystal(0, 0, 0));
+ if (!LoadEntityBaseFromNBT(*EnderCrystal.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+ a_Entities.push_back(EnderCrystal.release());
+}
+
+
+
+
+
void cWSSAnvil::LoadFallingBlockFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
int TypeIdx = a_NBT.FindChildByName(a_TagIdx, "TileID");
@@ -1291,6 +1415,7 @@ void cWSSAnvil::LoadMinecartHFromNBT(cEntityList & a_Entities, const cParsedNBT
void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
+ // Load item:
int ItemTag = a_NBT.FindChildByName(a_TagIdx, "Item");
if ((ItemTag < 0) || (a_NBT.GetType(ItemTag) != TAG_Compound))
{
@@ -1301,11 +1426,27 @@ void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
return;
}
+
std::auto_ptr<cPickup> Pickup(new cPickup(0, 0, 0, Item, false)); // Pickup delay doesn't matter, just say false
if (!LoadEntityBaseFromNBT(*Pickup.get(), a_NBT, a_TagIdx))
{
return;
}
+
+ // Load health:
+ int Health = a_NBT.FindChildByName(a_TagIdx, "Health");
+ if (Health > 0)
+ {
+ Pickup->SetHealth((int) (a_NBT.GetShort(Health) & 0xFF));
+ }
+
+ // Load age:
+ int Age = a_NBT.FindChildByName(a_TagIdx, "Age");
+ if (Age > 0)
+ {
+ Pickup->SetAge(a_NBT.GetShort(Age));
+ }
+
a_Entities.push_back(Pickup.release());
}
@@ -1313,6 +1454,148 @@ void cWSSAnvil::LoadPickupFromNBT(cEntityList & a_Entities, const cParsedNBT & a
+void cWSSAnvil::LoadTNTFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ std::auto_ptr<cTNTEntity> TNT(new cTNTEntity(0.0, 0.0, 0.0, 0));
+ if (!LoadEntityBaseFromNBT(*TNT.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ // Load Fuse Ticks:
+ int FuseTicks = a_NBT.FindChildByName(a_TagIdx, "Fuse");
+ if (FuseTicks > 0)
+ {
+ TNT->SetFuseTicks((int) a_NBT.GetByte(FuseTicks));
+ }
+
+ a_Entities.push_back(TNT.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadExpOrbFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ std::auto_ptr<cExpOrb> ExpOrb(new cExpOrb(0.0, 0.0, 0.0, 0));
+ if (!LoadEntityBaseFromNBT(*ExpOrb.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ // Load Health:
+ int Health = a_NBT.FindChildByName(a_TagIdx, "Health");
+ if (Health > 0)
+ {
+ ExpOrb->SetHealth((int) (a_NBT.GetShort(Health) & 0xFF));
+ }
+
+ // Load Age:
+ int Age = a_NBT.FindChildByName(a_TagIdx, "Age");
+ if (Age > 0)
+ {
+ ExpOrb->SetAge(a_NBT.GetShort(Age));
+ }
+
+ // Load Reward (Value):
+ int Reward = a_NBT.FindChildByName(a_TagIdx, "Value");
+ if (Reward > 0)
+ {
+ ExpOrb->SetReward(a_NBT.GetShort(Reward));
+ }
+
+ a_Entities.push_back(ExpOrb.release());
+}
+
+
+
+
+
+void cWSSAnvil::LoadHangingFromNBT(cHangingEntity & a_Hanging, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ int Direction = a_NBT.FindChildByName(a_TagIdx, "Direction");
+ if (Direction > 0)
+ {
+ Direction = (int)a_NBT.GetByte(Direction);
+ if ((Direction < 0) || (Direction > 5))
+ {
+ a_Hanging.SetDirection(BLOCK_FACE_NORTH);
+ }
+ else
+ {
+ a_Hanging.SetDirection(static_cast<eBlockFace>(Direction));
+ }
+ }
+ else
+ {
+ Direction = a_NBT.FindChildByName(a_TagIdx, "Dir");
+ if (Direction > 0)
+ {
+ switch ((int)a_NBT.GetByte(Direction))
+ {
+ case 0: a_Hanging.SetDirection(BLOCK_FACE_NORTH); break;
+ case 1: a_Hanging.SetDirection(BLOCK_FACE_TOP); break;
+ case 2: a_Hanging.SetDirection(BLOCK_FACE_BOTTOM); break;
+ case 3: a_Hanging.SetDirection(BLOCK_FACE_SOUTH); break;
+ }
+ }
+ }
+
+ int TileX = a_NBT.FindChildByName(a_TagIdx, "TileX");
+ int TileY = a_NBT.FindChildByName(a_TagIdx, "TileY");
+ int TileZ = a_NBT.FindChildByName(a_TagIdx, "TileZ");
+ if ((TileX > 0) && (TileY > 0) && (TileZ > 0))
+ {
+ a_Hanging.SetPosition(
+ (double)a_NBT.GetInt(TileX),
+ (double)a_NBT.GetInt(TileY),
+ (double)a_NBT.GetInt(TileZ)
+ );
+ }
+}
+
+
+
+
+
+void cWSSAnvil::LoadItemFrameFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ // Load item:
+ int ItemTag = a_NBT.FindChildByName(a_TagIdx, "Item");
+ if ((ItemTag < 0) || (a_NBT.GetType(ItemTag) != TAG_Compound))
+ {
+ return;
+ }
+ cItem Item;
+ if (!LoadItemFromNBT(Item, a_NBT, ItemTag))
+ {
+ return;
+ }
+
+ std::auto_ptr<cItemFrame> ItemFrame(new cItemFrame(BLOCK_FACE_NONE, 0.0, 0.0, 0.0));
+ if (!LoadEntityBaseFromNBT(*ItemFrame.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+ ItemFrame->SetItem(Item);
+
+ LoadHangingFromNBT(*ItemFrame.get(), a_NBT, a_TagIdx);
+
+ // Load Rotation:
+ int Rotation = a_NBT.FindChildByName(a_TagIdx, "ItemRotation");
+ if (Rotation > 0)
+ {
+ ItemFrame->SetRotation((Byte)a_NBT.GetByte(Rotation));
+ }
+
+ a_Entities.push_back(ItemFrame.release());
+}
+
+
+
+
+
void cWSSAnvil::LoadArrowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx)
{
std::auto_ptr<cArrowEntity> Arrow(new cArrowEntity(NULL, 0, 0, 0, Vector3d(0, 0, 0)));
@@ -1439,6 +1722,11 @@ void cWSSAnvil::LoadBatFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1454,6 +1742,11 @@ void cWSSAnvil::LoadBlazeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1469,6 +1762,11 @@ void cWSSAnvil::LoadCaveSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1484,6 +1782,11 @@ void cWSSAnvil::LoadChickenFromNBT(cEntityList & a_Entities, const cParsedNBT &
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1499,6 +1802,11 @@ void cWSSAnvil::LoadCowFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1514,6 +1822,11 @@ void cWSSAnvil::LoadCreeperFromNBT(cEntityList & a_Entities, const cParsedNBT &
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1529,6 +1842,11 @@ void cWSSAnvil::LoadEnderDragonFromNBT(cEntityList & a_Entities, const cParsedNB
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1545,6 +1863,11 @@ void cWSSAnvil::LoadEndermanFromNBT(cEntityList & a_Entities, const cParsedNBT &
return;
}
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
a_Entities.push_back(Monster.release());
}
@@ -1560,6 +1883,11 @@ void cWSSAnvil::LoadGhastFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
return;
}
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
a_Entities.push_back(Monster.release());
}
@@ -1574,6 +1902,11 @@ void cWSSAnvil::LoadGiantFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1600,6 +1933,11 @@ void cWSSAnvil::LoadHorseFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1615,6 +1953,11 @@ void cWSSAnvil::LoadIronGolemFromNBT(cEntityList & a_Entities, const cParsedNBT
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1636,6 +1979,11 @@ void cWSSAnvil::LoadMagmaCubeFromNBT(cEntityList & a_Entities, const cParsedNBT
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1651,6 +1999,11 @@ void cWSSAnvil::LoadMooshroomFromNBT(cEntityList & a_Entities, const cParsedNBT
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1666,6 +2019,11 @@ void cWSSAnvil::LoadOcelotFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1681,6 +2039,11 @@ void cWSSAnvil::LoadPigFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NB
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1702,6 +2065,11 @@ void cWSSAnvil::LoadSheepFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1717,6 +2085,11 @@ void cWSSAnvil::LoadSilverfishFromNBT(cEntityList & a_Entities, const cParsedNBT
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1738,6 +2111,11 @@ void cWSSAnvil::LoadSkeletonFromNBT(cEntityList & a_Entities, const cParsedNBT &
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1759,6 +2137,11 @@ void cWSSAnvil::LoadSlimeFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1774,6 +2157,11 @@ void cWSSAnvil::LoadSnowGolemFromNBT(cEntityList & a_Entities, const cParsedNBT
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1789,6 +2177,11 @@ void cWSSAnvil::LoadSpiderFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1804,6 +2197,11 @@ void cWSSAnvil::LoadSquidFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1825,6 +2223,11 @@ void cWSSAnvil::LoadVillagerFromNBT(cEntityList & a_Entities, const cParsedNBT &
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1840,6 +2243,11 @@ void cWSSAnvil::LoadWitchFromNBT(cEntityList & a_Entities, const cParsedNBT & a_
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1855,6 +2263,17 @@ void cWSSAnvil::LoadWitherFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
+ int CurrLine = a_NBT.FindChildByName(a_TagIdx, "Invul");
+ if (CurrLine > 0)
+ {
+ Monster->SetNumInvulnerableTicks(a_NBT.GetInt(CurrLine));
+ }
a_Entities.push_back(Monster.release());
}
@@ -1870,7 +2289,38 @@ void cWSSAnvil::LoadWolfFromNBT(cEntityList & a_Entities, const cParsedNBT & a_N
{
return;
}
-
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+ int OwnerIdx = a_NBT.FindChildByName(a_TagIdx, "Owner");
+ if (OwnerIdx > 0)
+ {
+ AString OwnerName = a_NBT.GetString(OwnerIdx);
+ if (OwnerName != "")
+ {
+ Monster->SetOwner(OwnerName);
+ Monster->SetIsTame(true);
+ }
+ }
+ int SittingIdx = a_NBT.FindChildByName(a_TagIdx, "Sitting");
+ if (SittingIdx > 0)
+ {
+ bool Sitting = ((a_NBT.GetByte(SittingIdx) == 1) ? true : false);
+ Monster->SetIsSitting(Sitting);
+ }
+ int AngryIdx = a_NBT.FindChildByName(a_TagIdx, "Angry");
+ if (AngryIdx > 0)
+ {
+ bool Angry = ((a_NBT.GetByte(AngryIdx) == 1) ? true : false);
+ Monster->SetIsAngry(Angry);
+ }
+ int CollarColorIdx = a_NBT.FindChildByName(a_TagIdx, "CollarColor");
+ if (CollarColorIdx > 0)
+ {
+ int CollarColor = a_NBT.GetInt(CollarColorIdx);
+ Monster->SetCollarColor(CollarColor);
+ }
a_Entities.push_back(Monster.release());
}
@@ -1891,6 +2341,11 @@ void cWSSAnvil::LoadZombieFromNBT(cEntityList & a_Entities, const cParsedNBT & a
{
return;
}
+
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
a_Entities.push_back(Monster.release());
}
@@ -1907,6 +2362,11 @@ void cWSSAnvil::LoadPigZombieFromNBT(cEntityList & a_Entities, const cParsedNBT
return;
}
+ if (!LoadMonsterBaseFromNBT(*Monster.get(), a_NBT, a_TagIdx))
+ {
+ return;
+ }
+
a_Entities.push_back(Monster.release());
}
@@ -1926,17 +2386,22 @@ bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_N
double Speed[3];
if (!LoadDoublesListFromNBT(Speed, 3, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Motion")))
{
- return false;
+ // Provide default speed:
+ Speed[0] = 0;
+ Speed[1] = 0;
+ Speed[2] = 0;
}
a_Entity.SetSpeed(Speed[0], Speed[1], Speed[2]);
double Rotation[3];
if (!LoadDoublesListFromNBT(Rotation, 2, a_NBT, a_NBT.FindChildByName(a_TagIdx, "Rotation")))
{
- return false;
+ // Provide default rotation:
+ Rotation[0] = 0;
+ Rotation[1] = 0;
}
a_Entity.SetYaw(Rotation[0]);
- a_Entity.SetRoll (Rotation[1]);
+ a_Entity.SetRoll(Rotation[1]);
return true;
}
@@ -1945,6 +2410,27 @@ bool cWSSAnvil::LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_N
+bool cWSSAnvil::LoadMonsterBaseFromNBT(cMonster & a_Monster, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ float DropChance[5];
+ if (!LoadFloatsListFromNBT(DropChance, 5, a_NBT, a_NBT.FindChildByName(a_TagIdx, "DropChance")))
+ {
+ return false;
+ }
+ a_Monster.SetDropChanceWeapon(DropChance[0]);
+ a_Monster.SetDropChanceHelmet(DropChance[1]);
+ a_Monster.SetDropChanceChestplate(DropChance[2]);
+ a_Monster.SetDropChanceLeggings(DropChance[3]);
+ a_Monster.SetDropChanceBoots(DropChance[4]);
+ bool CanPickUpLoot = (a_NBT.GetByte(a_NBT.FindChildByName(a_TagIdx, "CanPickUpLoot")) == 1);
+ a_Monster.SetCanPickUpLoot(CanPickUpLoot);
+ return true;
+}
+
+
+
+
+
bool cWSSAnvil::LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx)
{
if (!LoadEntityBaseFromNBT(a_Entity, a_NBT, a_TagIdx))
@@ -1987,6 +2473,24 @@ bool cWSSAnvil::LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, con
+bool cWSSAnvil::LoadFloatsListFromNBT(float * a_Floats, int a_NumFloats, const cParsedNBT & a_NBT, int a_TagIdx)
+{
+ if ((a_TagIdx < 0) || (a_NBT.GetType(a_TagIdx) != TAG_List) || (a_NBT.GetChildrenType(a_TagIdx) != TAG_Float))
+ {
+ return false;
+ }
+ int idx = 0;
+ for (int Tag = a_NBT.GetFirstChild(a_TagIdx); (Tag > 0) && (idx < a_NumFloats); Tag = a_NBT.GetNextSibling(Tag), ++idx)
+ {
+ a_Floats[idx] = a_NBT.GetFloat(Tag);
+ } // for Tag - PosTag[]
+ return (idx == a_NumFloats); // Did we read enough doubles?
+}
+
+
+
+
+
bool cWSSAnvil::GetBlockEntityNBTPos(const cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z)
{
int x = a_NBT.FindChildByName(a_TagIdx, "x");
diff --git a/src/WorldStorage/WSSAnvil.h b/src/WorldStorage/WSSAnvil.h
index 5093ad083..1773ee882 100644
--- a/src/WorldStorage/WSSAnvil.h
+++ b/src/WorldStorage/WSSAnvil.h
@@ -10,6 +10,7 @@
#include "WorldStorage.h"
#include "FastNBT.h"
+#include "../Mobs/Monster.h"
@@ -19,6 +20,7 @@
class cItemGrid;
class cProjectileEntity;
+class cHangingEntity;
@@ -134,18 +136,25 @@ protected:
void LoadChestFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadDispenserFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadDropperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadFlowerPotFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadFurnaceFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx, BLOCKTYPE * a_BlockTypes, NIBBLETYPE * a_BlockMetas);
void LoadHopperFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadJukeboxFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadNoteFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadSignFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadMobHeadFromNBT (cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadCommandBlockFromNBT(cBlockEntityList & a_BlockEntities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadEntityFromNBT(cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_EntityTagIdx, const char * a_IDTag, int a_IDTagLength);
void LoadBoatFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadEnderCrystalFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadFallingBlockFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadPickupFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadTNTFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadExpOrbFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadHangingFromNBT (cHangingEntity & a_Hanging,const cParsedNBT & a_NBT, int a_TagIdx);
+ void LoadItemFrameFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMinecartRFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
void LoadMinecartCFromNBT (cEntityList & a_Entities, const cParsedNBT & a_NBT, int a_TagIdx);
@@ -193,12 +202,18 @@ protected:
/// Loads entity common data from the NBT compound; returns true if successful
bool LoadEntityBaseFromNBT(cEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIdx);
+ /// Loads monster common data from the NBT compound; returns true if successful
+ bool LoadMonsterBaseFromNBT(cMonster & a_Monster, const cParsedNBT & a_NBT, int a_TagIdx);
+
/// Loads projectile common data from the NBT compound; returns true if successful
bool LoadProjectileBaseFromNBT(cProjectileEntity & a_Entity, const cParsedNBT & a_NBT, int a_TagIx);
/// Loads an array of doubles of the specified length from the specified NBT list tag a_TagIdx; returns true if successful
bool LoadDoublesListFromNBT(double * a_Doubles, int a_NumDoubles, const cParsedNBT & a_NBT, int a_TagIdx);
+ /// Loads an array of floats of the specified length from the specified NBT list tag a_TagIdx; returns true if successful
+ bool LoadFloatsListFromNBT(float * a_Floats, int a_NumFloats, 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 cParsedNBT & a_NBT, int a_TagIdx, int & a_X, int & a_Y, int & a_Z);
diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp
index ea17a8ec1..bb9d4b9e6 100644
--- a/src/WorldStorage/WSSCompact.cpp
+++ b/src/WorldStorage/WSSCompact.cpp
@@ -10,9 +10,12 @@
#include "json/json.h"
#include "../StringCompression.h"
#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/DispenserEntity.h"
+#include "../BlockEntities/FlowerPotEntity.h"
#include "../BlockEntities/FurnaceEntity.h"
#include "../BlockEntities/JukeboxEntity.h"
+#include "../BlockEntities/MobHeadEntity.h"
#include "../BlockEntities/NoteEntity.h"
#include "../BlockEntities/SignEntity.h"
@@ -36,7 +39,7 @@ struct cWSSCompact::sChunkHeader
/// The maximum number of PAK files that are cached
-const int MAX_PAK_FILES = 16;
+const size_t MAX_PAK_FILES = 16;
/// The maximum number of unsaved chunks before the cPAKFile saves them to disk
const int MAX_DIRTY_CHUNKS = 16;
@@ -71,14 +74,17 @@ void cJsonChunkSerializer::BlockEntity(cBlockEntity * a_BlockEntity)
const char * SaveInto = NULL;
switch (a_BlockEntity->GetBlockType())
{
- case E_BLOCK_CHEST: SaveInto = "Chests"; break;
- case E_BLOCK_DISPENSER: SaveInto = "Dispensers"; break;
- case E_BLOCK_DROPPER: SaveInto = "Droppers"; break;
- case E_BLOCK_FURNACE: SaveInto = "Furnaces"; break;
- case E_BLOCK_SIGN_POST: SaveInto = "Signs"; break;
- case E_BLOCK_WALLSIGN: SaveInto = "Signs"; break;
- case E_BLOCK_NOTE_BLOCK: SaveInto = "Notes"; break;
- case E_BLOCK_JUKEBOX: SaveInto = "Jukeboxes"; break;
+ case E_BLOCK_CHEST: SaveInto = "Chests"; break;
+ case E_BLOCK_DISPENSER: SaveInto = "Dispensers"; break;
+ case E_BLOCK_DROPPER: SaveInto = "Droppers"; break;
+ case E_BLOCK_FLOWER_POT: SaveInto = "FlowerPots"; break;
+ case E_BLOCK_FURNACE: SaveInto = "Furnaces"; break;
+ case E_BLOCK_SIGN_POST: SaveInto = "Signs"; break;
+ case E_BLOCK_WALLSIGN: SaveInto = "Signs"; break;
+ case E_BLOCK_NOTE_BLOCK: SaveInto = "Notes"; break;
+ case E_BLOCK_JUKEBOX: SaveInto = "Jukeboxes"; break;
+ case E_BLOCK_COMMAND_BLOCK: SaveInto = "CommandBlocks"; break;
+ case E_BLOCK_HEAD: SaveInto = "MobHeads"; break;
default:
{
@@ -263,126 +269,144 @@ bool cWSSCompact::EraseChunkData(const cChunkCoords & a_Chunk)
void cWSSCompact::LoadEntitiesFromJson(Json::Value & a_Value, cEntityList & a_Entities, cBlockEntityList & a_BlockEntities, cWorld * a_World)
{
- // Load chests
+ // Load chests:
Json::Value AllChests = a_Value.get("Chests", Json::nullValue);
if (!AllChests.empty())
{
for (Json::Value::iterator itr = AllChests.begin(); itr != AllChests.end(); ++itr )
{
- Json::Value & Chest = *itr;
- cChestEntity * ChestEntity = new cChestEntity(0,0,0, a_World);
- if (!ChestEntity->LoadFromJson( Chest ) )
+ std::auto_ptr<cChestEntity> ChestEntity(new cChestEntity(0, 0, 0, a_World));
+ if (!ChestEntity->LoadFromJson(*itr))
{
- LOGERROR("ERROR READING CHEST FROM JSON!" );
- delete ChestEntity;
+ LOGWARNING("ERROR READING CHEST FROM JSON!" );
}
else
{
- a_BlockEntities.push_back( ChestEntity );
+ a_BlockEntities.push_back(ChestEntity.release());
}
} // for itr - AllChests[]
}
- // Load dispensers
+ // Load dispensers:
Json::Value AllDispensers = a_Value.get("Dispensers", Json::nullValue);
- if( !AllDispensers.empty() )
+ for (Json::Value::iterator itr = AllDispensers.begin(); itr != AllDispensers.end(); ++itr)
{
- for( Json::Value::iterator itr = AllDispensers.begin(); itr != AllDispensers.end(); ++itr )
+ std::auto_ptr<cDispenserEntity> DispenserEntity(new cDispenserEntity(0, 0, 0, a_World));
+ if (!DispenserEntity->LoadFromJson(*itr))
{
- Json::Value & Dispenser = *itr;
- cDispenserEntity * DispenserEntity = new cDispenserEntity(0,0,0, a_World);
- if( !DispenserEntity->LoadFromJson( Dispenser ) )
- {
- LOGERROR("ERROR READING DISPENSER FROM JSON!" );
- delete DispenserEntity;
- }
- else
- {
- a_BlockEntities.push_back( DispenserEntity );
- }
- } // for itr - AllDispensers[]
- }
+ LOGWARNING("ERROR READING DISPENSER FROM JSON!" );
+ }
+ else
+ {
+ a_BlockEntities.push_back(DispenserEntity.release());
+ }
+ } // for itr - AllDispensers[]
- // Load furnaces
+ // Load Flowerpots:
+ Json::Value AllFlowerPots = a_Value.get("FlowerPots", Json::nullValue);
+ for (Json::Value::iterator itr = AllFlowerPots.begin(); itr != AllFlowerPots.end(); ++itr)
+ {
+ std::auto_ptr<cFlowerPotEntity> FlowerPotEntity(new cFlowerPotEntity(0, 0, 0, a_World));
+ if (!FlowerPotEntity->LoadFromJson(*itr))
+ {
+ LOGWARNING("ERROR READING FLOWERPOT FROM JSON!" );
+ }
+ else
+ {
+ a_BlockEntities.push_back(FlowerPotEntity.release());
+ }
+ } // for itr - AllFlowerPots[]
+
+ // Load furnaces:
Json::Value AllFurnaces = a_Value.get("Furnaces", Json::nullValue);
- if( !AllFurnaces.empty() )
+ for (Json::Value::iterator itr = AllFurnaces.begin(); itr != AllFurnaces.end(); ++itr)
{
- for( Json::Value::iterator itr = AllFurnaces.begin(); itr != AllFurnaces.end(); ++itr )
+ // TODO: The block type and meta aren't correct, there's no way to get them here
+ std::auto_ptr<cFurnaceEntity> FurnaceEntity(new cFurnaceEntity(0, 0, 0, E_BLOCK_FURNACE, 0, a_World));
+ if (!FurnaceEntity->LoadFromJson(*itr))
{
- Json::Value & Furnace = *itr;
- // TODO: The block type and meta aren't correct, there's no way to get them here
- cFurnaceEntity * FurnaceEntity = new cFurnaceEntity(0, 0, 0, E_BLOCK_FURNACE, 0, a_World);
- if (!FurnaceEntity->LoadFromJson(Furnace))
- {
- LOGERROR("ERROR READING FURNACE FROM JSON!" );
- delete FurnaceEntity;
- }
- else
- {
- a_BlockEntities.push_back(FurnaceEntity);
- }
- } // for itr - AllFurnaces[]
- }
+ LOGWARNING("ERROR READING FURNACE FROM JSON!" );
+ }
+ else
+ {
+ a_BlockEntities.push_back(FurnaceEntity.release());
+ }
+ } // for itr - AllFurnaces[]
- // Load signs
+ // Load signs:
Json::Value AllSigns = a_Value.get("Signs", Json::nullValue);
- if( !AllSigns.empty() )
+ for (Json::Value::iterator itr = AllSigns.begin(); itr != AllSigns.end(); ++itr)
{
- for( Json::Value::iterator itr = AllSigns.begin(); itr != AllSigns.end(); ++itr )
+ std::auto_ptr<cSignEntity> SignEntity(new cSignEntity(E_BLOCK_SIGN_POST, 0, 0, 0, a_World));
+ if (!SignEntity->LoadFromJson(*itr))
{
- Json::Value & Sign = *itr;
- cSignEntity * SignEntity = new cSignEntity( E_BLOCK_SIGN_POST, 0,0,0, a_World);
- if ( !SignEntity->LoadFromJson( Sign ) )
- {
- LOGERROR("ERROR READING SIGN FROM JSON!" );
- delete SignEntity;
- }
- else
- {
- a_BlockEntities.push_back( SignEntity );
- }
- } // for itr - AllSigns[]
- }
+ LOGWARNING("ERROR READING SIGN FROM JSON!");
+ }
+ else
+ {
+ a_BlockEntities.push_back(SignEntity.release());
+ }
+ } // for itr - AllSigns[]
- // Load note blocks
+ // Load note blocks:
Json::Value AllNotes = a_Value.get("Notes", Json::nullValue);
- if( !AllNotes.empty() )
+ for (Json::Value::iterator itr = AllNotes.begin(); itr != AllNotes.end(); ++itr)
{
- for( Json::Value::iterator itr = AllNotes.begin(); itr != AllNotes.end(); ++itr )
+ std::auto_ptr<cNoteEntity> NoteEntity(new cNoteEntity(0, 0, 0, a_World));
+ if (!NoteEntity->LoadFromJson(*itr))
{
- Json::Value & Note = *itr;
- cNoteEntity * NoteEntity = new cNoteEntity(0, 0, 0, a_World);
- if ( !NoteEntity->LoadFromJson( Note ) )
- {
- LOGERROR("ERROR READING NOTE BLOCK FROM JSON!" );
- delete NoteEntity;
- }
- else
- {
- a_BlockEntities.push_back( NoteEntity );
- }
- } // for itr - AllNotes[]
- }
+ LOGWARNING("ERROR READING NOTE BLOCK FROM JSON!" );
+ }
+ else
+ {
+ a_BlockEntities.push_back(NoteEntity.release());
+ }
+ } // for itr - AllNotes[]
- // Load jukeboxes
+ // Load jukeboxes:
Json::Value AllJukeboxes = a_Value.get("Jukeboxes", Json::nullValue);
- if( !AllJukeboxes.empty() )
+ for (Json::Value::iterator itr = AllJukeboxes.begin(); itr != AllJukeboxes.end(); ++itr)
{
- for( Json::Value::iterator itr = AllJukeboxes.begin(); itr != AllJukeboxes.end(); ++itr )
+ std::auto_ptr<cJukeboxEntity> JukeboxEntity(new cJukeboxEntity(0, 0, 0, a_World));
+ if (!JukeboxEntity->LoadFromJson(*itr))
{
- Json::Value & Jukebox = *itr;
- cJukeboxEntity * JukeboxEntity = new cJukeboxEntity(0, 0, 0, a_World);
- if ( !JukeboxEntity->LoadFromJson( Jukebox ) )
- {
- LOGERROR("ERROR READING JUKEBOX FROM JSON!" );
- delete JukeboxEntity;
- }
- else
- {
- a_BlockEntities.push_back( JukeboxEntity );
- }
- } // for itr - AllJukeboxes[]
- }
+ LOGWARNING("ERROR READING JUKEBOX FROM JSON!" );
+ }
+ else
+ {
+ a_BlockEntities.push_back(JukeboxEntity.release());
+ }
+ } // for itr - AllJukeboxes[]
+
+ // Load command blocks:
+ Json::Value AllCommandBlocks = a_Value.get("CommandBlocks", Json::nullValue);
+ for (Json::Value::iterator itr = AllCommandBlocks.begin(); itr != AllCommandBlocks.end(); ++itr)
+ {
+ std::auto_ptr<cCommandBlockEntity> CommandBlockEntity(new cCommandBlockEntity(0, 0, 0, a_World));
+ if (!CommandBlockEntity->LoadFromJson(*itr))
+ {
+ LOGWARNING("ERROR READING COMMAND BLOCK FROM JSON!" );
+ }
+ else
+ {
+ a_BlockEntities.push_back(CommandBlockEntity.release());
+ }
+ } // for itr - AllCommandBlocks[]
+
+ // Load mob heads:
+ Json::Value AllMobHeads = a_Value.get("MobHeads", Json::nullValue);
+ for (Json::Value::iterator itr = AllMobHeads.begin(); itr != AllMobHeads.end(); ++itr)
+ {
+ std::auto_ptr<cMobHeadEntity> MobHeadEntity(new cMobHeadEntity(0, 0, 0, a_World));
+ if (!MobHeadEntity->LoadFromJson(*itr))
+ {
+ LOGWARNING("ERROR READING MOB HEAD FROM JSON!" );
+ }
+ else
+ {
+ a_BlockEntities.push_back(MobHeadEntity.release());
+ }
+ } // for itr - AllMobHeads[]
}
@@ -545,7 +569,7 @@ void cWSSCompact::cPAKFile::UpdateChunk1To2()
if( ChunksConverted % 32 == 0 )
{
- LOGINFO("Updating \"%s\" version 1 to version 2: %d %%", m_FileName.c_str(), (ChunksConverted * 100) / m_ChunkHeaders.size() );
+ LOGINFO("Updating \"%s\" version 1 to version 2: " SIZE_T_FMT " %%", m_FileName.c_str(), (ChunksConverted * 100) / m_ChunkHeaders.size() );
}
ChunksConverted++;
@@ -583,7 +607,7 @@ void cWSSCompact::cPAKFile::UpdateChunk1To2()
if (UncompressedSize != (int)UncompressedData.size())
{
- LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]",
+ LOGWARNING("Uncompressed data size differs (exp %d bytes, got " SIZE_T_FMT ") for chunk [%d, %d]",
UncompressedSize, UncompressedData.size(),
Header->m_ChunkX, Header->m_ChunkZ
);
@@ -689,7 +713,7 @@ void cWSSCompact::cPAKFile::UpdateChunk2To3()
if( ChunksConverted % 32 == 0 )
{
- LOGINFO("Updating \"%s\" version 2 to version 3: %d %%", m_FileName.c_str(), (ChunksConverted * 100) / m_ChunkHeaders.size() );
+ LOGINFO("Updating \"%s\" version 2 to version 3: " SIZE_T_FMT " %%", m_FileName.c_str(), (ChunksConverted * 100) / m_ChunkHeaders.size() );
}
ChunksConverted++;
@@ -727,7 +751,7 @@ void cWSSCompact::cPAKFile::UpdateChunk2To3()
if (UncompressedSize != (int)UncompressedData.size())
{
- LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]",
+ LOGWARNING("Uncompressed data size differs (exp %d bytes, got " SIZE_T_FMT ") for chunk [%d, %d]",
UncompressedSize, UncompressedData.size(),
Header->m_ChunkX, Header->m_ChunkZ
);
@@ -740,7 +764,6 @@ void cWSSCompact::cPAKFile::UpdateChunk2To3()
// Cannot use cChunk::MakeIndex because it might change again?????????
// For compatibility, use what we know is current
- #define MAKE_2_INDEX( x, y, z ) ( y + (z * 256) + (x * 256 * 16) )
#define MAKE_3_INDEX( x, y, z ) ( x + (z * 16) + (y * 16 * 16) )
unsigned int InChunkOffset = 0;
@@ -843,7 +866,7 @@ bool cWSSCompact::LoadChunkFromData(const cChunkCoords & a_Chunk, int & a_Uncomp
if (a_UncompressedSize != (int)UncompressedData.size())
{
- LOGWARNING("Uncompressed data size differs (exp %d bytes, got %d) for chunk [%d, %d]",
+ LOGWARNING("Uncompressed data size differs (exp %d bytes, got " SIZE_T_FMT ") for chunk [%d, %d]",
a_UncompressedSize, UncompressedData.size(),
a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ
);
diff --git a/src/WorldStorage/WSSCompact.h b/src/WorldStorage/WSSCompact.h
index 64b8d7f31..4df146ec3 100644
--- a/src/WorldStorage/WSSCompact.h
+++ b/src/WorldStorage/WSSCompact.h
@@ -12,7 +12,7 @@
#define WSSCOMPACT_H_INCLUDED
#include "WorldStorage.h"
-#include "../Vector3i.h"
+#include "../Vector3.h"
#include "json/json.h"