summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt4
-rw-r--r--Tools/GeneratorPerformanceTest/CMakeLists.txt12
-rw-r--r--Tools/GeneratorPerformanceTest/GeneratorPerformanceTest.cpp7
-rw-r--r--src/Bindings/ManualBindings.cpp64
-rw-r--r--src/BlockArea.cpp168
-rw-r--r--src/BlockArea.h27
-rw-r--r--src/ForEachChunkProvider.h14
-rw-r--r--src/Generating/CMakeLists.txt2
-rw-r--r--src/Mobs/Villager.cpp2
-rw-r--r--src/World.h7
-rw-r--r--src/WorldStorage/SchematicFileSerializer.cpp172
-rw-r--r--src/WorldStorage/SchematicFileSerializer.h29
12 files changed, 318 insertions, 190 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 181b286e9..70728706d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -201,5 +201,9 @@ if (NOT MSVC)
string(REPLACE "-w" "" CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG}")
endif()
+if(${BUILD_TOOLS})
+add_subdirectory(Tools/GeneratorPerformanceTest/)
+endif()
+
add_subdirectory (src)
diff --git a/Tools/GeneratorPerformanceTest/CMakeLists.txt b/Tools/GeneratorPerformanceTest/CMakeLists.txt
new file mode 100644
index 000000000..8adc88882
--- /dev/null
+++ b/Tools/GeneratorPerformanceTest/CMakeLists.txt
@@ -0,0 +1,12 @@
+
+cmake_minimum_required(VERSION 2.8)
+project(GeneratorPerformanceTest)
+
+include_directories(../../src/Generating)
+include_directories(../../src)
+include_directories(../../lib)
+
+add_executable(GeneratorPerformanceTest GeneratorPerformanceTest.cpp ../../src/StringUtils ../../src/MCLogger ../../src/Log ../../src/BlockID ../../src/Noise ../../src/Enchantments ../../src/BlockArea)
+
+target_link_libraries(GeneratorPerformanceTest Generating)
+
diff --git a/Tools/GeneratorPerformanceTest/GeneratorPerformanceTest.cpp b/Tools/GeneratorPerformanceTest/GeneratorPerformanceTest.cpp
new file mode 100644
index 000000000..75d18149f
--- /dev/null
+++ b/Tools/GeneratorPerformanceTest/GeneratorPerformanceTest.cpp
@@ -0,0 +1,7 @@
+
+#include "Globals.h"
+#include "ChunkGenerator.h"
+
+int main(int argc, char * argv[]) {
+ cChunkGenerator Generator = cChunkGenerator();
+}
diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp
index f1160f941..e7c66c6fb 100644
--- a/src/Bindings/ManualBindings.cpp
+++ b/src/Bindings/ManualBindings.cpp
@@ -13,6 +13,7 @@
#include "../Entities/Player.h"
#include "../WebAdmin.h"
#include "../ClientHandle.h"
+#include "../BlockArea.h"
#include "../BlockEntities/ChestEntity.h"
#include "../BlockEntities/CommandBlockEntity.h"
#include "../BlockEntities/DispenserEntity.h"
@@ -22,6 +23,7 @@
#include "../BlockEntities/NoteEntity.h"
#include "md5/md5.h"
#include "../LineBlockTracer.h"
+#include "../WorldStorage/SchematicFileSerializer.h"
@@ -2234,6 +2236,63 @@ static int tolua_cHopperEntity_GetOutputBlockPos(lua_State * tolua_S)
+
+static int tolua_cBlockArea_LoadFromSchematicFile(lua_State * tolua_S)
+{
+ // function cBlockArea::LoadFromSchematicFile
+ // Exported manually because function has been moved to SchematicFileSerilizer.cpp
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cBlockArea") ||
+ !L.CheckParamString (2) ||
+ !L.CheckParamEnd (3)
+ )
+ {
+ return 0;
+ }
+ cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, 0);
+ if (self == NULL)
+ {
+ tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea::LoadFromSchematicFile'", NULL);
+ return 0;
+ }
+
+ AString Filename = tolua_tostring(tolua_S, 2, 0);
+ bool res = cSchematicFileSerializer::LoadFromSchematicFile(*self,Filename);
+ tolua_pushboolean(tolua_S, res);
+ return 1;
+}
+
+
+
+
+static int tolua_cBlockArea_SaveToSchematicFile(lua_State * tolua_S)
+{
+ // function cBlockArea::SaveToSchematicFile
+ // Exported manually because function has been moved to SchematicFileSerilizer.cpp
+ cLuaState L(tolua_S);
+ if (
+ !L.CheckParamUserType(1, "cBlockArea") ||
+ !L.CheckParamString (2) ||
+ !L.CheckParamEnd (3)
+ )
+ {
+ return 0;
+ }
+ cBlockArea * self = (cBlockArea *)tolua_tousertype(tolua_S, 1, 0);
+ if (self == NULL)
+ {
+ tolua_error(tolua_S, "invalid 'self' in function 'cBlockArea::SaveToSchematicFile'", NULL);
+ return 0;
+ }
+ AString Filename = tolua_tostring(tolua_S, 2, 0);
+ bool res = cSchematicFileSerializer::SaveToSchematicFile(*self,Filename);
+ tolua_pushboolean(tolua_S, res);
+ return 1;
+}
+
+
+
void ManualBindings::Bind(lua_State * tolua_S)
{
tolua_beginmodule(tolua_S, NULL);
@@ -2249,6 +2308,11 @@ void ManualBindings::Bind(lua_State * tolua_S)
tolua_function(tolua_S, "GetFolderContents", tolua_cFile_GetFolderContents);
tolua_endmodule(tolua_S);
+ tolua_beginmodule(tolua_S, "cBlockArea");
+ tolua_function(tolua_S, "LoadFromSchematicFile", tolua_cBlockArea_LoadFromSchematicFile);
+ tolua_function(tolua_S, "SaveToSchematicFile", tolua_cBlockArea_SaveToSchematicFile);
+ tolua_endmodule(tolua_S);
+
tolua_beginmodule(tolua_S, "cHopperEntity");
tolua_function(tolua_S, "GetOutputBlockPos", tolua_cHopperEntity_GetOutputBlockPos);
tolua_endmodule(tolua_S);
diff --git a/src/BlockArea.cpp b/src/BlockArea.cpp
index 910661f60..194e2d68a 100644
--- a/src/BlockArea.cpp
+++ b/src/BlockArea.cpp
@@ -6,9 +6,7 @@
#include "Globals.h"
#include "BlockArea.h"
-#include "World.h"
#include "OSSupport/GZipFile.h"
-#include "WorldStorage/FastNBT.h"
#include "Blocks/BlockHandler.h"
@@ -266,7 +264,7 @@ void cBlockArea::SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ)
-bool cBlockArea::Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes)
+bool cBlockArea::Read(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes)
{
// Normalize the coords:
if (a_MinBlockX > a_MaxBlockX)
@@ -327,7 +325,7 @@ bool cBlockArea::Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_
cChunkDef::AbsoluteToRelative(a_MaxBlockX, a_MaxBlockY, a_MaxBlockZ, MaxChunkX, MaxChunkZ);
// Query block data:
- if (!a_World->ForEachChunkInRect(MinChunkX, MaxChunkX, MinChunkZ, MaxChunkZ, Reader))
+ if (!a_ForEachChunkProvider->ForEachChunkInRect(MinChunkX, MaxChunkX, MinChunkZ, MaxChunkZ, Reader))
{
Clear();
return false;
@@ -340,7 +338,7 @@ bool cBlockArea::Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_
-bool cBlockArea::Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes)
+bool cBlockArea::Write(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes)
{
ASSERT((a_DataTypes & GetDataTypes()) == a_DataTypes); // Are you requesting only the data that I have?
a_DataTypes = a_DataTypes & GetDataTypes(); // For release builds, silently cut off the datatypes that I don't have
@@ -357,7 +355,7 @@ bool cBlockArea::Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a
a_MinBlockY = cChunkDef::Height - m_SizeY;
}
- return a_World->WriteBlockArea(*this, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes);
+ return a_ForEachChunkProvider->WriteBlockArea(*this, a_MinBlockX, a_MinBlockY, a_MinBlockZ, a_DataTypes);
}
@@ -448,85 +446,12 @@ void cBlockArea::DumpToRawFile(const AString & a_FileName)
-bool cBlockArea::LoadFromSchematicFile(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(NBT);
-}
-bool cBlockArea::SaveToSchematicFile(const AString & a_FileName)
-{
- cFastNBTWriter Writer("Schematic");
- Writer.AddShort("Width", m_SizeX);
- Writer.AddShort("Height", m_SizeY);
- Writer.AddShort("Length", m_SizeZ);
- Writer.AddString("Materials", "Alpha");
- if (HasBlockTypes())
- {
- Writer.AddByteArray("Blocks", (const char *)m_BlockTypes, GetBlockCount());
- }
- else
- {
- AString Dummy(GetBlockCount(), 0);
- Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size());
- }
- if (HasBlockMetas())
- {
- Writer.AddByteArray("Data", (const char *)m_BlockMetas, GetBlockCount());
- }
- else
- {
- AString Dummy(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;
-}
+
@@ -2018,89 +1943,6 @@ void cBlockArea::ExpandNibbles(NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMa
}
-
-
-
-bool cBlockArea::LoadFromSchematicNBT(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);
-
- Clear();
- SetSize(SizeX, SizeY, SizeZ, AreMetasPresent ? (baTypes | baMetas) : baTypes);
-
- // Copy the block types and metas:
- int NumBytes = m_SizeX * m_SizeY * 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(m_BlockTypes, a_NBT.GetData(TBlockTypes), NumBytes);
-
- if (AreMetasPresent)
- {
- int NumBytes = m_SizeX * m_SizeY * 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(m_BlockMetas, a_NBT.GetData(TBlockMetas), NumBytes);
- }
-
- return true;
-}
-
-
-
-
void cBlockArea::RelSetData(
int a_RelX, int a_RelY, int a_RelZ,
int a_DataTypes, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta,
diff --git a/src/BlockArea.h b/src/BlockArea.h
index 075cc99ec..59bc0f241 100644
--- a/src/BlockArea.h
+++ b/src/BlockArea.h
@@ -12,18 +12,7 @@
#pragma once
-
-
-
-
-// fwd: World.h
-class cWorld;
-
-// fwd: FastNBT.h
-class cParsedNBT;
-
-
-
+#include "ForEachChunkProvider.h"
// tolua_begin
@@ -68,13 +57,13 @@ public:
void SetOrigin(int a_OriginX, int a_OriginY, int a_OriginZ);
/// Reads an area of blocks specified. Returns true if successful. All coords are inclusive.
- bool Read(cWorld * a_World, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes = baTypes | baMetas);
+ bool Read(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MaxBlockX, int a_MinBlockY, int a_MaxBlockY, int a_MinBlockZ, int a_MaxBlockZ, int a_DataTypes = baTypes | baMetas);
// TODO: Write() is not too good an interface: if it fails, there's no way to repeat only for the parts that didn't write
// A better way may be to return a list of cBlockAreas for each part that didn't succeed writing, so that the caller may try again
/// Writes the area back into cWorld at the coords specified. Returns true if successful in all chunks, false if only partially / not at all
- bool Write(cWorld * a_World, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes = baTypes | baMetas);
+ bool Write(cForEachChunkProvider * a_ForEachChunkProvider, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes = baTypes | baMetas);
/// Copies this object's contents into the specified BlockArea.
void CopyTo(cBlockArea & a_Into) const;
@@ -85,12 +74,6 @@ public:
/// For testing purposes only, dumps the area into a file.
void DumpToRawFile(const AString & a_FileName);
- /// Loads an area from a .schematic file. Returns true if successful
- bool LoadFromSchematicFile(const AString & a_FileName);
-
- /// Saves the area into a .schematic file. Returns true if successful
- bool SaveToSchematicFile(const AString & a_FileName);
-
/// Crops the internal contents by the specified amount of blocks from each border.
void Crop(int a_AddMinX, int a_SubMaxX, int a_AddMinY, int a_SubMaxY, int a_AddMinZ, int a_SubMaxZ);
@@ -233,6 +216,7 @@ public:
protected:
friend class cChunkDesc;
+ friend class cSchematicFileSerializer;
class cChunkReader :
public cChunkDataCallback
@@ -291,9 +275,6 @@ protected:
// Expand helpers:
void ExpandBlockTypes(int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
void ExpandNibbles (NIBBLEARRAY & a_Array, int a_SubMinX, int a_AddMaxX, int a_SubMinY, int a_AddMaxY, int a_SubMinZ, int a_AddMaxZ);
-
- /// Loads the area from a schematic file uncompressed and parsed into a NBT tree. Returns true if successful.
- bool LoadFromSchematicNBT(cParsedNBT & a_NBT);
/// Sets the specified datatypes at the specified location.
void RelSetData(
diff --git a/src/ForEachChunkProvider.h b/src/ForEachChunkProvider.h
new file mode 100644
index 000000000..70cd2196a
--- /dev/null
+++ b/src/ForEachChunkProvider.h
@@ -0,0 +1,14 @@
+
+#pragma once
+
+class cChunkDataCallback;
+
+class cBlockArea;
+
+class cForEachChunkProvider
+{
+public:
+ virtual bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback) = 0;
+
+ virtual bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes) = 0;
+};
diff --git a/src/Generating/CMakeLists.txt b/src/Generating/CMakeLists.txt
index e1ba299fc..793f48890 100644
--- a/src/Generating/CMakeLists.txt
+++ b/src/Generating/CMakeLists.txt
@@ -9,3 +9,5 @@ file(GLOB SOURCE
)
add_library(Generating ${SOURCE})
+
+target_link_libraries(Generating OSSupport iniFile)
diff --git a/src/Mobs/Villager.cpp b/src/Mobs/Villager.cpp
index 7f89fb6cc..c8ff4f101 100644
--- a/src/Mobs/Villager.cpp
+++ b/src/Mobs/Villager.cpp
@@ -21,7 +21,7 @@ cVillager::cVillager(eVillagerType VillagerType) :
void cVillager::DoTakeDamage(TakeDamageInfo & a_TDI)
{
super::DoTakeDamage(a_TDI);
- if (a_TDI.Attacker->IsPlayer())
+ if ((a_TDI.Attacker != NULL) && a_TDI.Attacker->IsPlayer())
{
if (m_World->GetTickRandomNumber(5) == 3)
{
diff --git a/src/World.h b/src/World.h
index 933e3ba6f..d7a7241d1 100644
--- a/src/World.h
+++ b/src/World.h
@@ -22,6 +22,7 @@
#include "Item.h"
#include "Mobs/Monster.h"
#include "Entities/ProjectileEntity.h"
+#include "ForEachChunkProvider.h"
#include "Scoreboard.h"
@@ -61,7 +62,7 @@ typedef cItemCallback<cCommandBlockEntity> cCommandBlockCallback;
// tolua_begin
-class cWorld
+class cWorld : public cForEachChunkProvider
{
public:
@@ -314,7 +315,7 @@ public:
bool IsChunkLighted(int a_ChunkX, int a_ChunkZ);
/** Calls the callback for each chunk in the coords specified (all cords are inclusive). Returns true if all chunks have been processed successfully */
- bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback);
+ virtual bool ForEachChunkInRect(int a_MinChunkX, int a_MaxChunkX, int a_MinChunkZ, int a_MaxChunkZ, cChunkDataCallback & a_Callback);
// tolua_begin
@@ -360,7 +361,7 @@ public:
Prefer cBlockArea::Write() instead, this is the internal implementation; cBlockArea does error checking, too.
a_DataTypes is a bitmask of cBlockArea::baXXX constants ORed together.
*/
- bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes);
+ virtual bool WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlockY, int a_MinBlockZ, int a_DataTypes);
// tolua_begin
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);
+};