summaryrefslogtreecommitdiffstats
path: root/src/WorldStorage
diff options
context:
space:
mode:
Diffstat (limited to 'src/WorldStorage')
-rw-r--r--src/WorldStorage/EnchantmentSerializer.cpp88
-rw-r--r--src/WorldStorage/EnchantmentSerializer.h17
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp22
-rw-r--r--src/WorldStorage/SchematicFileSerializer.cpp172
-rw-r--r--src/WorldStorage/SchematicFileSerializer.h29
-rw-r--r--src/WorldStorage/ScoreboardSerializer.cpp377
-rw-r--r--src/WorldStorage/ScoreboardSerializer.h52
-rw-r--r--src/WorldStorage/WSSAnvil.cpp14
-rw-r--r--src/WorldStorage/WSSCompact.cpp182
9 files changed, 843 insertions, 110 deletions
diff --git a/src/WorldStorage/EnchantmentSerializer.cpp b/src/WorldStorage/EnchantmentSerializer.cpp
new file mode 100644
index 000000000..56072207f
--- /dev/null
+++ b/src/WorldStorage/EnchantmentSerializer.cpp
@@ -0,0 +1,88 @@
+
+#include "Globals.h"
+
+#include "EnchantmentSerializer.h"
+#include "Enchantments.h"
+#include "FastNBT.h"
+
+void EnchantmentSerializer::WriteToNBTCompound(cEnchantments const& a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName)
+{
+ // Write the enchantments into the specified NBT writer
+ // begin with the LIST tag of the specified name ("ench" or "StoredEnchantments")
+
+ a_Writer.BeginList(a_ListTagName, TAG_Compound);
+ for (cEnchantments::cMap::const_iterator itr = a_Enchantments.m_Enchantments.begin(), end = a_Enchantments.m_Enchantments.end(); itr != end; ++itr)
+ {
+ a_Writer.BeginCompound("");
+ a_Writer.AddShort("id", itr->first);
+ a_Writer.AddShort("lvl", itr->second);
+ a_Writer.EndCompound();
+ } // for itr - m_Enchantments[]
+ a_Writer.EndList();
+}
+
+
+
+
+
+void EnchantmentSerializer::ParseFromNBT(cEnchantments& a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx)
+{
+ // Read the enchantments from the specified NBT list tag (ench or StoredEnchantments)
+
+ // Verify that the tag is a list:
+ if (a_NBT.GetType(a_EnchListTagIdx) != TAG_List)
+ {
+ LOGWARNING("%s: Invalid EnchListTag type: exp %d, got %d. Enchantments not parsed",
+ __FUNCTION__, TAG_List, a_NBT.GetType(a_EnchListTagIdx)
+ );
+ ASSERT(!"Bad EnchListTag type");
+ return;
+ }
+
+ // Verify that the list is of Compounds:
+ if (a_NBT.GetChildrenType(a_EnchListTagIdx) != TAG_Compound)
+ {
+ LOGWARNING("%s: Invalid NBT list children type: exp %d, got %d. Enchantments not parsed",
+ __FUNCTION__, TAG_Compound, a_NBT.GetChildrenType(a_EnchListTagIdx)
+ );
+ ASSERT(!"Bad EnchListTag children type");
+ return;
+ }
+
+ a_Enchantments.Clear();
+
+ // Iterate over all the compound children, parse an enchantment from each:
+ for (int tag = a_NBT.GetFirstChild(a_EnchListTagIdx); tag >= 0; tag = a_NBT.GetNextSibling(tag))
+ {
+ // tag is the compound inside the "ench" list tag
+ ASSERT(a_NBT.GetType(tag) == TAG_Compound);
+
+ // Search for the id and lvl tags' values:
+ int id = -1, lvl = -1;
+ for (int ch = a_NBT.GetFirstChild(tag); ch >= 0; ch = a_NBT.GetNextSibling(ch))
+ {
+ if (a_NBT.GetType(ch) != TAG_Short)
+ {
+ continue;
+ }
+ if (a_NBT.GetName(ch) == "id")
+ {
+ id = a_NBT.GetShort(ch);
+ }
+ else if (a_NBT.GetName(ch) == "lvl")
+ {
+ lvl = a_NBT.GetShort(ch);
+ }
+ } // for ch - children of the compound tag
+
+ if ((id == -1) || (lvl <= 0))
+ {
+ // Failed to parse either the id or the lvl, skip this compound
+ continue;
+ }
+
+ // Store the enchantment:
+ a_Enchantments.m_Enchantments[id] = lvl;
+ } // for tag - children of the ench list tag
+}
+
diff --git a/src/WorldStorage/EnchantmentSerializer.h b/src/WorldStorage/EnchantmentSerializer.h
new file mode 100644
index 000000000..9ed362900
--- /dev/null
+++ b/src/WorldStorage/EnchantmentSerializer.h
@@ -0,0 +1,17 @@
+
+#pragma once
+
+class cEnchantments;
+class cFastNBTWriter;
+class cParsedNBT;
+
+namespace EnchantmentSerializer
+{
+
+ /// Writes the enchantments into the specified NBT writer; begins with the LIST tag of the specified name ("ench" or "StoredEnchantments")
+ void WriteToNBTCompound(cEnchantments const& a_Enchantments, cFastNBTWriter & a_Writer, const AString & a_ListTagName);
+
+ /// Reads the enchantments from the specified NBT list tag (ench or StoredEnchantments)
+ void ParseFromNBT(cEnchantments& a_Enchantments, const cParsedNBT & a_NBT, int a_EnchListTagIdx);
+
+};
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
index 447296c7f..e46a28caa 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -4,6 +4,7 @@
#include "Globals.h"
#include "NBTChunkSerializer.h"
+#include "EnchantmentSerializer.h"
#include "../BlockID.h"
#include "../ItemGrid.h"
#include "../StringCompression.h"
@@ -92,7 +93,7 @@ void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AStrin
{
const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
m_Writer.BeginCompound("tag");
- a_Item.m_Enchantments.WriteToNBTCompound(m_Writer, TagName);
+ EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, m_Writer, TagName);
m_Writer.EndCompound();
}
@@ -651,20 +652,21 @@ 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_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_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/SchematicFileSerializer.cpp b/src/WorldStorage/SchematicFileSerializer.cpp
new file mode 100644
index 000000000..45fd967bd
--- /dev/null
+++ b/src/WorldStorage/SchematicFileSerializer.cpp
@@ -0,0 +1,172 @@
+
+#include "Globals.h"
+
+#include "OSSupport/GZipFile.h"
+#include "FastNBT.h"
+
+#include "SchematicFileSerializer.h"
+
+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::SaveToSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName)
+{
+ cFastNBTWriter Writer("Schematic");
+ Writer.AddShort("Width", a_BlockArea.m_SizeX);
+ Writer.AddShort("Height", a_BlockArea.m_SizeY);
+ Writer.AddShort("Length", a_BlockArea.m_SizeZ);
+ 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());
+ }
+ // TODO: Save entities and block entities
+ Writer.BeginList("Entities", TAG_Compound);
+ Writer.EndList();
+ Writer.BeginList("TileEntities", TAG_Compound);
+ Writer.EndList();
+ Writer.Finish();
+
+ // Save to file
+ cGZipFile File;
+ if (!File.Open(a_FileName, cGZipFile::fmWrite))
+ {
+ LOG("Cannot open file \"%s\" for writing.", a_FileName.c_str());
+ return false;
+ }
+ if (!File.Write(Writer.GetResult()))
+ {
+ LOG("Cannot write data to file \"%s\".", a_FileName.c_str());
+ 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);
+
+ // Copy the block types and metas:
+ int NumBytes = a_BlockArea.m_SizeX * a_BlockArea.m_SizeY * a_BlockArea.m_SizeZ;
+ 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.m_SizeX * a_BlockArea.m_SizeY * a_BlockArea.m_SizeZ;
+ 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;
+}
+
+
diff --git a/src/WorldStorage/SchematicFileSerializer.h b/src/WorldStorage/SchematicFileSerializer.h
new file mode 100644
index 000000000..9be2e5b57
--- /dev/null
+++ b/src/WorldStorage/SchematicFileSerializer.h
@@ -0,0 +1,29 @@
+
+#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);
+
+ /// Saves the area into a .schematic file. Returns true if successful
+ static bool SaveToSchematicFile(cBlockArea & a_BlockArea, const AString & a_FileName);
+
+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);
+};
diff --git a/src/WorldStorage/ScoreboardSerializer.cpp b/src/WorldStorage/ScoreboardSerializer.cpp
new file mode 100644
index 000000000..c65e13f98
--- /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::E_DISPLAY_SLOT_LIST);
+ a_Writer.AddString("slot_0", (Objective == NULL) ? "" : Objective->GetName());
+
+ Objective = m_ScoreBoard->GetObjectiveIn(cScoreboard::E_DISPLAY_SLOT_SIDEBAR);
+ a_Writer.AddString("slot_1", (Objective == NULL) ? "" : Objective->GetName());
+
+ Objective = m_ScoreBoard->GetObjectiveIn(cScoreboard::E_DISPLAY_SLOT_NAME);
+ 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;
+
+ 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, CanSeeFriendlyInvisible;
+
+ 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::E_DISPLAY_SLOT_LIST);
+ }
+
+ CurrLine = a_NBT.FindChildByName(DisplaySlots, "slot_1");
+ if (CurrLine >= 0)
+ {
+ AString Name = a_NBT.GetString(CurrLine);
+
+ m_ScoreBoard->SetDisplay(Name, cScoreboard::E_DISPLAY_SLOT_SIDEBAR);
+ }
+
+ CurrLine = a_NBT.FindChildByName(DisplaySlots, "slot_2");
+ if (CurrLine >= 0)
+ {
+ AString Name = a_NBT.GetString(CurrLine);
+
+ m_ScoreBoard->SetDisplay(Name, cScoreboard::E_DISPLAY_SLOT_NAME);
+ }
+
+ 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 96a77152b..e2a882f65 100644
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -7,6 +7,7 @@
#include "WSSAnvil.h"
#include "NBTChunkSerializer.h"
#include "FastNBT.h"
+#include "EnchantmentSerializer.h"
#include "zlib/zlib.h"
#include "../World.h"
#include "../BlockID.h"
@@ -644,7 +645,7 @@ bool cWSSAnvil::LoadItemFromNBT(cItem & a_Item, const cParsedNBT & a_NBT, int a_
int EnchTag = a_NBT.FindChildByName(TagTag, EnchName);
if (EnchTag > 0)
{
- a_Item.m_Enchantments.ParseFromNBT(a_NBT, EnchTag);
+ EnchantmentSerializer::ParseFromNBT(a_Item.m_Enchantments, a_NBT, EnchTag);
}
return true;
@@ -1925,17 +1926,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;
}
diff --git a/src/WorldStorage/WSSCompact.cpp b/src/WorldStorage/WSSCompact.cpp
index ea17a8ec1..4c0684dd8 100644
--- a/src/WorldStorage/WSSCompact.cpp
+++ b/src/WorldStorage/WSSCompact.cpp
@@ -10,6 +10,7 @@
#include "json/json.h"
#include "../StringCompression.h"
#include "../BlockEntities/ChestEntity.h"
+#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/DispenserEntity.h"
#include "../BlockEntities/FurnaceEntity.h"
#include "../BlockEntities/JukeboxEntity.h"
@@ -71,14 +72,15 @@ 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_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;
default:
{
@@ -263,126 +265,114 @@ 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 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[]
}