Cuberite
A lightweight, fast and extensible game server for Minecraft
SchematicFileSerializer.cpp
Go to the documentation of this file.
1 
2 // SchematicFileSerializer.cpp
3 
4 // Implements the cSchematicFileSerializer class representing the interface to load and save cBlockArea to a .schematic file
5 
6 #include "Globals.h"
7 
8 #include "FastNBT.h"
10 #include "../OSSupport/GZipFile.h"
11 
12 
13 
14 
15 
17 // cSchematicFileSerializer:
18 
19 void cSchematicFileSerializer::LoadFromSchematicFile(cBlockArea & a_BlockArea, const std::string & a_FileName)
20 {
21  const auto Data = GZipFile::ReadRestOfFile(a_FileName);
22  const cParsedNBT NBT(Data.GetView());
23 
24  if (!NBT.IsValid())
25  {
26  throw std::runtime_error(fmt::format("Cannot parse the NBT in the schematic file \"{}\".", a_FileName));
27  }
28 
29  LoadFromSchematicNBT(a_BlockArea, NBT);
30 }
31 
32 
33 
34 
35 
37 {
38  const auto Extracted = Compression::Extractor().ExtractGZip(a_SchematicData);
39  const cParsedNBT NBT(Extracted.GetView());
40 
41  if (!NBT.IsValid())
42  {
43  throw std::runtime_error("Cannot parse the NBT in the schematic data.");
44  }
45 
46  LoadFromSchematicNBT(a_BlockArea, NBT);
47 }
48 
49 
50 
51 
52 
53 void cSchematicFileSerializer::SaveToSchematicFile(const cBlockArea & a_BlockArea, const std::string & a_FileName)
54 {
55  GZipFile::Write(a_FileName, SaveToSchematicNBT(a_BlockArea));
56 }
57 
58 
59 
60 
61 
63 {
65 }
66 
67 
68 
69 
70 
72 {
73  int TMaterials = a_NBT.FindChildByName(a_NBT.GetRoot(), "Materials");
74  if ((TMaterials > 0) && (a_NBT.GetType(TMaterials) == TAG_String))
75  {
76  AString Materials = a_NBT.GetString(TMaterials);
77  if (Materials.compare("Alpha") != 0)
78  {
79  throw std::runtime_error(fmt::format("Materials tag is present and \"{}\" instead of \"Alpha\". Possibly a wrong-format schematic file.", Materials));
80  }
81  }
82  int TSizeX = a_NBT.FindChildByName(a_NBT.GetRoot(), "Width");
83  int TSizeY = a_NBT.FindChildByName(a_NBT.GetRoot(), "Height");
84  int TSizeZ = a_NBT.FindChildByName(a_NBT.GetRoot(), "Length");
85  if (
86  (TSizeX < 0) || (TSizeY < 0) || (TSizeZ < 0) ||
87  (a_NBT.GetType(TSizeX) != TAG_Short) ||
88  (a_NBT.GetType(TSizeY) != TAG_Short) ||
89  (a_NBT.GetType(TSizeZ) != TAG_Short)
90  )
91  {
92  throw std::runtime_error(fmt::format(
93  "Dimensions are missing from the schematic file ({}, {}, {}), ({}, {}, {})",
94  TSizeX, TSizeY, TSizeZ,
95  (TSizeX >= 0) ? a_NBT.GetType(TSizeX) : -1,
96  (TSizeY >= 0) ? a_NBT.GetType(TSizeY) : -1,
97  (TSizeZ >= 0) ? a_NBT.GetType(TSizeZ) : -1
98  ));
99  }
100 
101  int SizeX = a_NBT.GetShort(TSizeX);
102  int SizeY = a_NBT.GetShort(TSizeY);
103  int SizeZ = a_NBT.GetShort(TSizeZ);
104  if ((SizeX < 1) || (SizeX > 65535) || (SizeY < 1) || (SizeY > 65535) || (SizeZ < 1) || (SizeZ > 65535))
105  {
106  throw std::runtime_error(fmt::format("Dimensions are invalid in the schematic file: {}, {}, {}", SizeX, SizeY, SizeZ));
107  }
108 
109  int TBlockTypes = a_NBT.FindChildByName(a_NBT.GetRoot(), "Blocks");
110  int TBlockMetas = a_NBT.FindChildByName(a_NBT.GetRoot(), "Data");
111  if ((TBlockTypes < 0) || (a_NBT.GetType(TBlockTypes) != TAG_ByteArray))
112  {
113  throw std::runtime_error(fmt::format("BlockTypes are invalid in the schematic file: {}", TBlockTypes));
114  }
115  bool AreMetasPresent = (TBlockMetas > 0) && (a_NBT.GetType(TBlockMetas) == TAG_ByteArray);
116 
117  a_BlockArea.Clear();
118  a_BlockArea.SetSize(SizeX, SizeY, SizeZ, AreMetasPresent ? (cBlockArea::baTypes | cBlockArea::baMetas) : cBlockArea::baTypes);
119 
120  int TOffsetX = a_NBT.FindChildByName(a_NBT.GetRoot(), "WEOffsetX");
121  int TOffsetY = a_NBT.FindChildByName(a_NBT.GetRoot(), "WEOffsetY");
122  int TOffsetZ = a_NBT.FindChildByName(a_NBT.GetRoot(), "WEOffsetZ");
123 
124  if (
125  (TOffsetX < 0) || (TOffsetY < 0) || (TOffsetZ < 0) ||
126  (a_NBT.GetType(TOffsetX) != TAG_Int) ||
127  (a_NBT.GetType(TOffsetY) != TAG_Int) ||
128  (a_NBT.GetType(TOffsetZ) != TAG_Int)
129  )
130  {
131  // Not every schematic file has an offset, so we shoudn't give a warn message.
132  a_BlockArea.SetWEOffset(0, 0, 0);
133  }
134  else
135  {
136  a_BlockArea.SetWEOffset(a_NBT.GetInt(TOffsetX), a_NBT.GetInt(TOffsetY), a_NBT.GetInt(TOffsetZ));
137  }
138 
139  // Copy the block types and metas:
140  size_t NumTypeBytes = a_BlockArea.GetBlockCount();
141  if (a_NBT.GetDataLength(TBlockTypes) < NumTypeBytes)
142  {
143  LOG("BlockTypes truncated in the schematic file (exp %u, got %u bytes). Loading partial.",
144  static_cast<unsigned>(NumTypeBytes), static_cast<unsigned>(a_NBT.GetDataLength(TBlockTypes))
145  );
146  NumTypeBytes = a_NBT.GetDataLength(TBlockTypes);
147  }
148  memcpy(a_BlockArea.GetBlockTypes(), a_NBT.GetData(TBlockTypes), NumTypeBytes);
149 
150  if (AreMetasPresent)
151  {
152  size_t NumMetaBytes = a_BlockArea.GetBlockCount();
153  if (a_NBT.GetDataLength(TBlockMetas) < NumMetaBytes)
154  {
155  LOG("BlockMetas truncated in the schematic file (exp %u, got %u bytes). Loading partial.",
156  static_cast<unsigned>(NumMetaBytes), static_cast<unsigned>(a_NBT.GetDataLength(TBlockMetas))
157  );
158  NumMetaBytes = a_NBT.GetDataLength(TBlockMetas);
159  }
160  memcpy(a_BlockArea.GetBlockMetas(), a_NBT.GetData(TBlockMetas), NumMetaBytes);
161  }
162 }
163 
164 
165 
166 
167 
169 {
170  cFastNBTWriter Writer("Schematic");
171  Writer.AddShort("Width", static_cast<Int16>(a_BlockArea.m_Size.x));
172  Writer.AddShort("Height", static_cast<Int16>(a_BlockArea.m_Size.y));
173  Writer.AddShort("Length", static_cast<Int16>(a_BlockArea.m_Size.z));
174  Writer.AddString("Materials", "Alpha");
175  if (a_BlockArea.HasBlockTypes())
176  {
177  Writer.AddByteArray("Blocks", reinterpret_cast<const char *>(a_BlockArea.GetBlockTypes()), a_BlockArea.GetBlockCount());
178  }
179  else
180  {
181  AString Dummy(a_BlockArea.GetBlockCount(), 0);
182  Writer.AddByteArray("Blocks", Dummy.data(), Dummy.size());
183  }
184  if (a_BlockArea.HasBlockMetas())
185  {
186  Writer.AddByteArray("Data", reinterpret_cast<const char *>(a_BlockArea.GetBlockMetas()), a_BlockArea.GetBlockCount());
187  }
188  else
189  {
190  AString Dummy(a_BlockArea.GetBlockCount(), 0);
191  Writer.AddByteArray("Data", Dummy.data(), Dummy.size());
192  }
193 
194  Writer.AddInt("WEOffsetX", a_BlockArea.m_WEOffset.x);
195  Writer.AddInt("WEOffsetY", a_BlockArea.m_WEOffset.y);
196  Writer.AddInt("WEOffsetZ", a_BlockArea.m_WEOffset.z);
197 
198  // TODO: Save entities and block entities
199  Writer.BeginList("Entities", TAG_Compound);
200  Writer.EndList();
201  Writer.BeginList("TileEntities", TAG_Compound);
202  Writer.EndList();
203  Writer.Finish();
204 
205  return ContiguousByteBuffer(Writer.GetResult());
206 }
signed short Int16
Definition: Globals.h:153
std::basic_string_view< std::byte > ContiguousByteBufferView
Definition: Globals.h:376
std::basic_string< std::byte > ContiguousByteBuffer
Definition: Globals.h:375
void LOG(std::string_view a_Format, const Args &... args)
Definition: LoggerSimple.h:55
std::string AString
Definition: StringUtils.h:11
@ TAG_ByteArray
Definition: FastNBT.h:39
@ TAG_String
Definition: FastNBT.h:40
@ TAG_Short
Definition: FastNBT.h:34
@ TAG_Compound
Definition: FastNBT.h:42
@ TAG_Int
Definition: FastNBT.h:35
void Write(const std::string &a_FileName, ContiguousByteBufferView a_Contents)
Writes a_Contents into file, compressing it along the way.
Definition: GZipFile.cpp:27
Compression::Result ReadRestOfFile(const std::string &a_FileName)
Reads the rest of the file and returns the decompressed contents.
Definition: GZipFile.cpp:14
NIBBLETYPE * GetBlockMetas(void) const
Definition: BlockArea.h:390
Vector3i m_Size
Definition: BlockArea.h:461
Vector3i m_WEOffset
An extra data value sometimes stored in the .schematic file.
Definition: BlockArea.h:465
void SetWEOffset(int a_OffsetX, int a_OffsetY, int a_OffsetZ)
Definition: BlockArea.cpp:369
bool SetSize(int a_SizeX, int a_SizeY, int a_SizeZ, int a_DataTypes)
Clears the data stored and prepares a fresh new block area with the specified dimensions.
Definition: BlockArea.cpp:2076
size_t GetBlockCount(void) const
Definition: BlockArea.h:393
bool HasBlockTypes(void) const
Definition: BlockArea.h:361
BLOCKTYPE * GetBlockTypes(void) const
Returns the internal pointer to the block types.
Definition: BlockArea.h:389
bool HasBlockMetas(void) const
Definition: BlockArea.h:362
void Clear(void)
Clears the data stored to reclaim memory.
Definition: BlockArea.cpp:323
Contains the result of a compression or extraction operation.
Contains routines for data compression.
Result CompressGZip(ContiguousByteBufferView Input)
Contains routines for data extraction.
Result ExtractGZip(ContiguousByteBufferView Input)
T x
Definition: Vector3.h:17
T y
Definition: Vector3.h:17
T z
Definition: Vector3.h:17
Parses and contains the parsed data Also implements data accessor functions for tree traversal and va...
Definition: FastNBT.h:153
Int16 GetShort(int a_Tag) const
Returns the value stored in a Short tag.
Definition: FastNBT.h:227
const std::byte * GetData(int a_Tag) const
Returns the data stored in this tag.
Definition: FastNBT.h:191
bool IsValid(void) const
Definition: FastNBT.h:157
eTagType GetType(int a_Tag) const
Definition: FastNBT.h:210
int FindChildByName(int a_Tag, const AString &a_Name) const
Returns the direct child tag of the specified name, or -1 if no such tag.
Definition: FastNBT.h:199
size_t GetDataLength(int a_Tag) const
Returns the length of the tag's data, in bytes.
Definition: FastNBT.h:182
int GetRoot(void) const
Returns the root tag of the hierarchy.
Definition: FastNBT.h:166
Int32 GetInt(int a_Tag) const
Returns the value stored in an Int tag.
Definition: FastNBT.h:234
AString GetString(int a_Tag) const
Returns the value stored in a String tag.
Definition: FastNBT.h:280
void AddByteArray(const AString &a_Name, const char *a_Value, size_t a_NumElements)
Definition: FastNBT.cpp:628
void AddShort(const AString &a_Name, Int16 a_Value)
Definition: FastNBT.cpp:561
void AddInt(const AString &a_Name, Int32 a_Value)
Definition: FastNBT.cpp:572
void Finish(void)
Definition: FastNBT.cpp:674
void AddString(const AString &a_Name, std::string_view a_Value)
Definition: FastNBT.cpp:616
void EndList(void)
Definition: FastNBT.cpp:536
void BeginList(const AString &a_Name, eTagType a_ChildrenType)
Definition: FastNBT.cpp:512
ContiguousByteBufferView GetResult(void) const
Definition: FastNBT.h:351
static void SaveToSchematicFile(const cBlockArea &a_BlockArea, const std::string &a_FileName)
Saves the area into a .schematic file.
static void LoadFromSchematicString(cBlockArea &a_BlockArea, ContiguousByteBufferView a_SchematicData)
Loads an area from a string containing the .schematic file data.
static Compression::Result SaveToSchematicString(const cBlockArea &a_BlockArea)
Saves the area into a string containing the .schematic file data.
static void LoadFromSchematicFile(cBlockArea &a_BlockArea, const std::string &a_FileName)
Loads an area from a .schematic file.
static ContiguousByteBuffer SaveToSchematicNBT(const cBlockArea &a_BlockArea)
Saves the area into a NBT representation and returns the NBT data as a string.
static void LoadFromSchematicNBT(cBlockArea &a_BlockArea, const cParsedNBT &a_NBT)
Loads the area from a schematic file uncompressed and parsed into a NBT tree.