summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/ChunkMap.cpp10
-rw-r--r--src/ChunkMap.h8
-rw-r--r--src/ChunkSender.cpp2
-rw-r--r--src/LightingThread.cpp2
-rw-r--r--src/World.cpp4
-rw-r--r--src/World.h4
-rw-r--r--src/WorldStorage/NBTChunkSerializer.cpp1648
-rw-r--r--src/WorldStorage/NBTChunkSerializer.h130
-rwxr-xr-xsrc/WorldStorage/WSSAnvil.cpp61
9 files changed, 909 insertions, 960 deletions
diff --git a/src/ChunkMap.cpp b/src/ChunkMap.cpp
index b5a6252ff..bc88f4e7b 100644
--- a/src/ChunkMap.cpp
+++ b/src/ChunkMap.cpp
@@ -444,12 +444,18 @@ void cChunkMap::ChunkLighted(
-bool cChunkMap::GetChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback)
+bool cChunkMap::GetChunkData(cChunkCoords a_Coords, cChunkDataCallback & a_Callback)
{
+ if (!a_Callback.Coords(a_Coords.m_ChunkX, a_Coords.m_ChunkZ))
+ {
+ // The callback doesn't want the data
+ return false;
+ }
cCSLock Lock(m_CSChunks);
- cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
+ cChunkPtr Chunk = GetChunkNoGen(a_Coords);
if ((Chunk == nullptr) || !Chunk->IsValid())
{
+ // The chunk is not present
return false;
}
Chunk->GetAllData(a_Callback);
diff --git a/src/ChunkMap.h b/src/ChunkMap.h
index 8f0ac3a7c..4a3dd2dfd 100644
--- a/src/ChunkMap.h
+++ b/src/ChunkMap.h
@@ -108,7 +108,9 @@ public:
const cChunkDef::BlockNibbles & a_SkyLight
);
- bool GetChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback);
+ /** Calls the callback with the chunk's data, if available (with ChunkCS locked).
+ Returns true if the chunk was reported successfully, false if not (chunk not present or callback failed). */
+ bool GetChunkData(cChunkCoords a_Coords, cChunkDataCallback & a_Callback);
/** Copies the chunk's blocktypes into a_Blocks; returns true if successful */
bool GetChunkBlockTypes (int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_Blocks);
@@ -439,7 +441,7 @@ private:
typedef std::list<cChunkStay *> cChunkStays;
- cCriticalSection m_CSChunks;
+ mutable cCriticalSection m_CSChunks;
/** A map of chunk coordinates to chunk pointers
Uses a map (as opposed to unordered_map) because sorted maps are apparently faster */
@@ -468,7 +470,7 @@ private:
// Deprecated in favor of the vector version
cChunkPtr GetChunkNoGen(int a_ChunkX, int a_ChunkZ)
{
- return GetChunkNoGen(cChunkCoords(a_ChunkX, a_ChunkZ));
+ return GetChunkNoGen({a_ChunkX, a_ChunkZ});
}
/** Constructs a chunk, returning it. Doesn't load, doesn't generate */
diff --git a/src/ChunkSender.cpp b/src/ChunkSender.cpp
index a11852ed6..6312dfdea 100644
--- a/src/ChunkSender.cpp
+++ b/src/ChunkSender.cpp
@@ -241,7 +241,7 @@ void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, std::unordered_set<cCli
}
// Query and prepare chunk data:
- if (!m_World.GetChunkData(a_ChunkX, a_ChunkZ, *this))
+ if (!m_World.GetChunkData({a_ChunkX, a_ChunkZ}, *this))
{
return;
}
diff --git a/src/LightingThread.cpp b/src/LightingThread.cpp
index c10c69ccd..53f6b2d86 100644
--- a/src/LightingThread.cpp
+++ b/src/LightingThread.cpp
@@ -336,7 +336,7 @@ void cLightingThread::ReadChunks(int a_ChunkX, int a_ChunkZ)
for (int x = 0; x < 3; x++)
{
Reader.m_ReadingChunkX = x;
- VERIFY(m_World.GetChunkData(a_ChunkX + x - 1, a_ChunkZ + z - 1, Reader));
+ VERIFY(m_World.GetChunkData({a_ChunkX + x - 1, a_ChunkZ + z - 1}, Reader));
} // for z
} // for x
diff --git a/src/World.cpp b/src/World.cpp
index 690224d35..3164322fc 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -2636,9 +2636,9 @@ void cWorld::ChunkLighted(
-bool cWorld::GetChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback)
+bool cWorld::GetChunkData(cChunkCoords a_Coords, cChunkDataCallback & a_Callback) const
{
- return m_ChunkMap->GetChunkData(a_ChunkX, a_ChunkZ, a_Callback);
+ return m_ChunkMap->GetChunkData(a_Coords, a_Callback);
}
diff --git a/src/World.h b/src/World.h
index 26e108e80..3ffbfecf0 100644
--- a/src/World.h
+++ b/src/World.h
@@ -234,7 +234,9 @@ public:
const cChunkDef::BlockNibbles & a_SkyLight
);
- bool GetChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataCallback & a_Callback);
+ /** Calls the callback with the chunk's data, if available (with ChunkCS locked).
+ Returns true if the chunk was reported successfully, false if not (chunk not present or callback failed). */
+ bool GetChunkData(cChunkCoords a_Coords, cChunkDataCallback & a_Callback) const;
/** Gets the chunk's blocks, only the block types */
bool GetChunkBlockTypes(int a_ChunkX, int a_ChunkZ, BLOCKTYPE * a_BlockTypes);
diff --git a/src/WorldStorage/NBTChunkSerializer.cpp b/src/WorldStorage/NBTChunkSerializer.cpp
index f53dba68b..0a18cb7b3 100644
--- a/src/WorldStorage/NBTChunkSerializer.cpp
+++ b/src/WorldStorage/NBTChunkSerializer.cpp
@@ -5,6 +5,7 @@
#include "Globals.h"
#include "NBTChunkSerializer.h"
#include "EnchantmentSerializer.h"
+#include "../ChunkDataCallback.h"
#include "../ItemGrid.h"
#include "../StringCompression.h"
#include "../UUID.h"
@@ -47,1032 +48,1139 @@
-cNBTChunkSerializer::cNBTChunkSerializer(cFastNBTWriter & a_Writer) :
- m_BiomesAreValid(false),
- m_Writer(a_Writer),
- m_IsTagOpen(false),
- m_HasHadEntity(false),
- m_HasHadBlockEntity(false),
- m_IsLightValid(false)
+/** Collects and stores the chunk data via the cChunkDataCallback interface */
+class SerializerCollector:
+ public cChunkDataCopyCollector
{
-}
+public:
+ // The data collected from the chunk:
+ cChunkDef::BiomeMap mBiomes;
+ UInt8 mVanillaBiomes[cChunkDef::Width * cChunkDef::Width];
+ int mVanillaHeightMap[cChunkDef::Width * cChunkDef::Width];
+ bool mBiomesAreValid;
+ /** True if a tag has been opened in the callbacks and not yet closed. */
+ bool mIsTagOpen;
+ /** True if any Entity has already been received and processed. */
+ bool mHasHadEntity;
+
+ /** True if any BlockEntity has already been received and processed. */
+ bool mHasHadBlockEntity;
+
+ /** True if the chunk lighting is valid. */
+ bool mIsLightValid;
+
+ /** The NBT writer used to store the data. */
+ cFastNBTWriter & mWriter;
-void cNBTChunkSerializer::Finish(void)
-{
- if (m_IsTagOpen)
- {
- m_Writer.EndList();
- }
- // If light not valid, reset it to defaults:
- if (!m_IsLightValid)
+
+
+ SerializerCollector(cFastNBTWriter & aWriter):
+ mBiomesAreValid(false),
+ mIsTagOpen(false),
+ mHasHadEntity(false),
+ mHasHadBlockEntity(false),
+ mIsLightValid(false),
+ mWriter(aWriter)
{
- m_Data.FillBlockLight(0x00);
- m_Data.FillSkyLight(0x0f);
}
- // Check if "Entity" and "TileEntities" lists exists. MCEdit requires this.
- if (!m_HasHadEntity)
+
+
+
+
+ virtual void LightIsValid(bool a_IsLightValid) override
{
- m_Writer.BeginList("Entities", TAG_Compound);
- m_Writer.EndList();
+ mIsLightValid = a_IsLightValid;
}
- if (!m_HasHadBlockEntity)
+
+
+
+
+
+ virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) override
{
- m_Writer.BeginList("TileEntities", TAG_Compound);
- m_Writer.EndList();
+ for (int RelX = 0; RelX < cChunkDef::Width; RelX++)
+ {
+ for (int RelZ = 0; RelZ < cChunkDef::Width; RelZ++)
+ {
+ int Height = cChunkDef::GetHeight(*a_HeightMap, RelX, RelZ);
+ mVanillaHeightMap[(RelZ << 4) | RelX] = Height;
+ }
+ }
}
-}
-void cNBTChunkSerializer::AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName)
-{
- m_Writer.BeginCompound(a_CompoundName);
- m_Writer.AddShort("id", static_cast<Int16>(a_Item.m_ItemType));
- m_Writer.AddShort("Damage", static_cast<Int16>((a_Item.m_ItemDamage)));
- m_Writer.AddByte ("Count", static_cast<Byte>(a_Item.m_ItemCount));
- if (a_Slot >= 0)
+ virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) override
{
- m_Writer.AddByte ("Slot", static_cast<unsigned char>(a_Slot));
+ memcpy(mBiomes, a_BiomeMap, sizeof(mBiomes));
+ for (size_t i = 0; i < ARRAYCOUNT(mBiomes); i++)
+ {
+ if ((*a_BiomeMap)[i] < 255)
+ {
+ // Normal MC biome, copy as-is:
+ mVanillaBiomes[i] = static_cast<Byte>((*a_BiomeMap)[i]);
+ }
+ else
+ {
+ // TODO: MCS-specific biome, need to map to some basic MC biome:
+ ASSERT(!"Unimplemented MCS-specific biome");
+ return;
+ }
+ } // for i - mBiomeMap[]
+ mBiomesAreValid = true;
}
- // Write the tag compound (for enchantment, firework, custom name and repair cost):
- if (
- (!a_Item.m_Enchantments.IsEmpty()) ||
- ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR)) ||
- (a_Item.m_RepairCost > 0) ||
- (a_Item.m_CustomName != "") ||
- (!a_Item.m_LoreTable.empty())
- )
+
+
+
+
+ virtual void Entity(cEntity * a_Entity) override
{
- m_Writer.BeginCompound("tag");
- if (a_Item.m_RepairCost > 0)
+ // Add entity into NBT:
+ if (mIsTagOpen)
+ {
+ if (!mHasHadEntity)
{
- m_Writer.AddInt("RepairCost", a_Item.m_RepairCost);
+ mWriter.EndList();
+ mWriter.BeginList("Entities", TAG_Compound);
}
+ }
+ else
+ {
+ mWriter.BeginList("Entities", TAG_Compound);
+ }
+ mIsTagOpen = true;
+ mHasHadEntity = true;
- if ((a_Item.m_CustomName != "") || (!a_Item.m_LoreTable.empty()))
+ switch (a_Entity->GetEntityType())
+ {
+ case cEntity::etBoat: AddBoatEntity (static_cast<cBoat *> (a_Entity)); break;
+ case cEntity::etEnderCrystal: AddEnderCrystalEntity(static_cast<cEnderCrystal *> (a_Entity)); break;
+ case cEntity::etFallingBlock: AddFallingBlockEntity(static_cast<cFallingBlock *> (a_Entity)); break;
+ case cEntity::etMinecart: AddMinecartEntity (static_cast<cMinecart *> (a_Entity)); break;
+ case cEntity::etMonster: AddMonsterEntity (static_cast<cMonster *> (a_Entity)); break;
+ case cEntity::etPickup: AddPickupEntity (static_cast<cPickup *> (a_Entity)); break;
+ case cEntity::etProjectile: AddProjectileEntity (static_cast<cProjectileEntity *>(a_Entity)); break;
+ case cEntity::etTNT: AddTNTEntity (static_cast<cTNTEntity *> (a_Entity)); break;
+ case cEntity::etExpOrb: AddExpOrbEntity (static_cast<cExpOrb *> (a_Entity)); break;
+ case cEntity::etItemFrame: AddItemFrameEntity (static_cast<cItemFrame *> (a_Entity)); break;
+ case cEntity::etLeashKnot: AddLeashKnotEntity (static_cast<cLeashKnot *> (a_Entity)); break;
+ case cEntity::etPainting: AddPaintingEntity (static_cast<cPainting *> (a_Entity)); break;
+ case cEntity::etPlayer: return; // Players aren't saved into the world
+ case cEntity::etFloater: return; // Floaters aren't saved either
+ default:
{
- m_Writer.BeginCompound("display");
- if (a_Item.m_CustomName != "")
- {
- m_Writer.AddString("Name", a_Item.m_CustomName);
- }
- if (!a_Item.m_LoreTable.empty())
- {
- m_Writer.BeginList("Lore", TAG_String);
+ ASSERT(!"Unhandled entity type is being saved");
+ break;
+ }
+ }
+ }
+
+
- for (const auto & Line : a_Item.m_LoreTable)
- {
- m_Writer.AddString("", Line);
- }
- m_Writer.EndList();
- }
- m_Writer.EndCompound();
- }
- if ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR))
+ virtual void BlockEntity(cBlockEntity * a_Entity) override
+ {
+ if (mIsTagOpen)
+ {
+ if (!mHasHadBlockEntity)
{
- cFireworkItem::WriteToNBTCompound(a_Item.m_FireworkItem, m_Writer, static_cast<ENUM_ITEM_ID>(a_Item.m_ItemType));
+ mWriter.EndList();
+ mWriter.BeginList("TileEntities", TAG_Compound);
}
+ }
+ else
+ {
+ mWriter.BeginList("TileEntities", TAG_Compound);
+ }
+ mIsTagOpen = true;
- if (!a_Item.m_Enchantments.IsEmpty())
+ // Add tile-entity into NBT:
+ switch (a_Entity->GetBlockType())
+ {
+ case E_BLOCK_BEACON: AddBeaconEntity (static_cast<cBeaconEntity *> (a_Entity)); break;
+ case E_BLOCK_BED: AddBedEntity (static_cast<cBedEntity *> (a_Entity)); break;
+ case E_BLOCK_BREWING_STAND: AddBrewingstandEntity(static_cast<cBrewingstandEntity *>(a_Entity)); break;
+ case E_BLOCK_CHEST: AddChestEntity (static_cast<cChestEntity *> (a_Entity), a_Entity->GetBlockType()); break;
+ case E_BLOCK_COMMAND_BLOCK: AddCommandBlockEntity(static_cast<cCommandBlockEntity *>(a_Entity)); break;
+ case E_BLOCK_DISPENSER: AddDispenserEntity (static_cast<cDispenserEntity *> (a_Entity)); break;
+ case E_BLOCK_DROPPER: AddDropperEntity (static_cast<cDropperEntity *> (a_Entity)); break;
+ case E_BLOCK_ENDER_CHEST: /* No data to be saved */ break;
+ case E_BLOCK_FLOWER_POT: AddFlowerPotEntity (static_cast<cFlowerPotEntity *> (a_Entity)); break;
+ case E_BLOCK_FURNACE: AddFurnaceEntity (static_cast<cFurnaceEntity *> (a_Entity)); break;
+ case E_BLOCK_HEAD: AddMobHeadEntity (static_cast<cMobHeadEntity *> (a_Entity)); break;
+ case E_BLOCK_HOPPER: AddHopperEntity (static_cast<cHopperEntity *> (a_Entity)); break;
+ case E_BLOCK_JUKEBOX: AddJukeboxEntity (static_cast<cJukeboxEntity *> (a_Entity)); break;
+ case E_BLOCK_LIT_FURNACE: AddFurnaceEntity (static_cast<cFurnaceEntity *> (a_Entity)); break;
+ case E_BLOCK_MOB_SPAWNER: AddMobSpawnerEntity (static_cast<cMobSpawnerEntity *> (a_Entity)); break;
+ case E_BLOCK_NOTE_BLOCK: AddNoteEntity (static_cast<cNoteEntity *> (a_Entity)); break;
+ case E_BLOCK_SIGN_POST: AddSignEntity (static_cast<cSignEntity *> (a_Entity)); break;
+ case E_BLOCK_TRAPPED_CHEST: AddChestEntity (static_cast<cChestEntity *> (a_Entity), a_Entity->GetBlockType()); break;
+ case E_BLOCK_WALLSIGN: AddSignEntity (static_cast<cSignEntity *> (a_Entity)); break;
+
+ default:
{
- const char * TagName = (a_Item.m_ItemType == E_ITEM_BOOK) ? "StoredEnchantments" : "ench";
- EnchantmentSerializer::WriteToNBTCompound(a_Item.m_Enchantments, m_Writer, TagName);
+ ASSERT(!"Unhandled block entity saved into Anvil");
}
- m_Writer.EndCompound();
+ }
+ mHasHadBlockEntity = true;
}
- m_Writer.EndCompound();
-}
+ void Finish(void)
+ {
+ if (mIsTagOpen)
+ {
+ mWriter.EndList();
+ }
-void cNBTChunkSerializer::AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum)
-{
- int NumSlots = a_Grid.GetNumSlots();
- for (int i = 0; i < NumSlots; i++)
+ // If light not valid, reset it to defaults:
+ if (!mIsLightValid)
+ {
+ m_Data.FillBlockLight(0x00);
+ m_Data.FillSkyLight(0x0f);
+ }
+
+ // Check if "Entity" and "TileEntities" lists exists. MCEdit requires this.
+ if (!mHasHadEntity)
+ {
+ mWriter.BeginList("Entities", TAG_Compound);
+ mWriter.EndList();
+ }
+ if (!mHasHadBlockEntity)
+ {
+ mWriter.BeginList("TileEntities", TAG_Compound);
+ mWriter.EndList();
+ }
+ }
+
+
+
+
+
+ /** Writes an item into the writer.
+ If aSlot >= 0, adds the Slot tag.
+ The compound is named as requested (empty name by default). */
+ void AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName = AString())
{
- const cItem & Item = a_Grid.GetSlot(i);
- if (Item.IsEmpty())
+ mWriter.BeginCompound(a_CompoundName);
+ mWriter.AddShort("id", static_cast<Int16>(a_Item.m_ItemType));
+ mWriter.AddShort("Damage", static_cast<Int16>((a_Item.m_ItemDamage)));
+ mWriter.AddByte ("Count", static_cast<Byte>(a_Item.m_ItemCount));
+ if (a_Slot >= 0)
{
- continue;
+ mWriter.AddByte ("Slot", static_cast<unsigned char>(a_Slot));
}
- AddItem(Item, i + a_BeginSlotNum);
- } // for i - chest slots[]
-}
+ // Write the tag compound (for enchantment, firework, custom name and repair cost):
+ if (
+ (!a_Item.m_Enchantments.IsEmpty()) ||
+ ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR)) ||
+ (a_Item.m_RepairCost > 0) ||
+ (a_Item.m_CustomName != "") ||
+ (!a_Item.m_LoreTable.empty())
+ )
+ {
+ mWriter.BeginCompound("tag");
+ if (a_Item.m_RepairCost > 0)
+ {
+ mWriter.AddInt("RepairCost", a_Item.m_RepairCost);
+ }
+ if ((a_Item.m_CustomName != "") || (!a_Item.m_LoreTable.empty()))
+ {
+ mWriter.BeginCompound("display");
+ if (a_Item.m_CustomName != "")
+ {
+ mWriter.AddString("Name", a_Item.m_CustomName);
+ }
+ if (!a_Item.m_LoreTable.empty())
+ {
+ mWriter.BeginList("Lore", TAG_String);
+ for (const auto & Line : a_Item.m_LoreTable)
+ {
+ mWriter.AddString("", Line);
+ }
+ mWriter.EndList();
+ }
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID)
-{
- m_Writer.AddInt ("x", a_Entity->GetPosX());
- m_Writer.AddInt ("y", a_Entity->GetPosY());
- m_Writer.AddInt ("z", a_Entity->GetPosZ());
- m_Writer.AddString("id", a_EntityTypeID);
-}
+ if ((a_Item.m_ItemType == E_ITEM_FIREWORK_ROCKET) || (a_Item.m_ItemType == E_ITEM_FIREWORK_STAR))
+ {
+ cFireworkItem::WriteToNBTCompound(a_Item.m_FireworkItem, mWriter, static_cast<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, mWriter, TagName);
+ }
+ mWriter.EndCompound();
+ }
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddBeaconEntity(cBeaconEntity * a_Entity)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Entity, "Beacon");
- m_Writer.AddInt("Levels", a_Entity->GetBeaconLevel());
- m_Writer.AddInt("Primary", static_cast<int>(a_Entity->GetPrimaryEffect()));
- m_Writer.AddInt("Secondary", static_cast<int>(a_Entity->GetSecondaryEffect()));
- m_Writer.BeginList("Items", TAG_Compound);
- AddItemGrid(a_Entity->GetContents());
- m_Writer.EndList();
- m_Writer.EndCompound();
-}
+ /** Writes an item grid into the writer.
+ Begins the stored slot numbers with a_BeginSlotNum.
+ Note that it doesn't begin nor end the list tag, so that multiple grids may be concatenated together using this function. */
+ void AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum = 0)
+ {
+ int NumSlots = a_Grid.GetNumSlots();
+ for (int i = 0; i < NumSlots; i++)
+ {
+ const cItem & Item = a_Grid.GetSlot(i);
+ if (Item.IsEmpty())
+ {
+ continue;
+ }
+ AddItem(Item, i + a_BeginSlotNum);
+ } // for i - slots[]
+ }
-void cNBTChunkSerializer::AddBedEntity(cBedEntity * a_Entity)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Entity, "Bed");
- m_Writer.AddInt("color", a_Entity->GetColor());
- m_Writer.EndCompound();
-}
+ void AddBasicTileEntity(cBlockEntity * a_Entity, const char * a_EntityTypeID)
+ {
+ mWriter.AddInt ("x", a_Entity->GetPosX());
+ mWriter.AddInt ("y", a_Entity->GetPosY());
+ mWriter.AddInt ("z", a_Entity->GetPosZ());
+ mWriter.AddString("id", a_EntityTypeID);
+ }
-void cNBTChunkSerializer::AddBrewingstandEntity(cBrewingstandEntity * a_Brewingstand)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Brewingstand, "Brewingstand");
- m_Writer.BeginList("Items", TAG_Compound);
- AddItemGrid(a_Brewingstand->GetContents());
- m_Writer.EndList();
- m_Writer.AddShort("BrewTime", a_Brewingstand->GetTimeBrewed());
- m_Writer.AddShort("Fuel", a_Brewingstand->GetRemainingFuel());
- m_Writer.EndCompound();
-}
+ void AddBeaconEntity(cBeaconEntity * a_Entity)
+ {
+ mWriter.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Beacon");
+ mWriter.AddInt("Levels", a_Entity->GetBeaconLevel());
+ mWriter.AddInt("Primary", static_cast<int>(a_Entity->GetPrimaryEffect()));
+ mWriter.AddInt("Secondary", static_cast<int>(a_Entity->GetSecondaryEffect()));
+ mWriter.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Entity->GetContents());
+ mWriter.EndList();
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddChestEntity(cChestEntity * a_Entity, BLOCKTYPE a_ChestType)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Entity, "Chest");
- m_Writer.BeginList("Items", TAG_Compound);
- AddItemGrid(a_Entity->GetContents());
- m_Writer.EndList();
- m_Writer.EndCompound();
-}
+ void AddBedEntity(cBedEntity * a_Entity)
+ {
+ mWriter.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Bed");
+ mWriter.AddInt("color", a_Entity->GetColor());
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddDispenserEntity(cDispenserEntity * a_Entity)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Entity, "Trap");
- m_Writer.BeginList("Items", TAG_Compound);
- AddItemGrid(a_Entity->GetContents());
- m_Writer.EndList();
- m_Writer.EndCompound();
-}
+ void AddBrewingstandEntity(cBrewingstandEntity * a_Brewingstand)
+ {
+ mWriter.BeginCompound("");
+ AddBasicTileEntity(a_Brewingstand, "Brewingstand");
+ mWriter.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Brewingstand->GetContents());
+ mWriter.EndList();
+ mWriter.AddShort("BrewTime", a_Brewingstand->GetTimeBrewed());
+ mWriter.AddShort("Fuel", a_Brewingstand->GetRemainingFuel());
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddDropperEntity(cDropperEntity * a_Entity)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Entity, "Dropper");
- m_Writer.BeginList("Items", TAG_Compound);
- AddItemGrid(a_Entity->GetContents());
- m_Writer.EndList();
- m_Writer.EndCompound();
-}
+ void AddChestEntity(cChestEntity * a_Entity, BLOCKTYPE a_ChestType)
+ {
+ mWriter.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Chest");
+ mWriter.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Entity->GetContents());
+ mWriter.EndList();
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddFurnaceEntity(cFurnaceEntity * a_Furnace)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Furnace, "Furnace");
- m_Writer.BeginList("Items", TAG_Compound);
- AddItemGrid(a_Furnace->GetContents());
- m_Writer.EndList();
- m_Writer.AddShort("BurnTime", static_cast<Int16>(a_Furnace->GetFuelBurnTimeLeft()));
- m_Writer.AddShort("CookTime", static_cast<Int16>(a_Furnace->GetTimeCooked()));
- m_Writer.EndCompound();
-}
+ void AddDispenserEntity(cDispenserEntity * a_Entity)
+ {
+ mWriter.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Trap");
+ mWriter.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Entity->GetContents());
+ mWriter.EndList();
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddHopperEntity(cHopperEntity * a_Entity)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Entity, "Hopper");
- m_Writer.BeginList("Items", TAG_Compound);
- AddItemGrid(a_Entity->GetContents());
- m_Writer.EndList();
- m_Writer.EndCompound();
-}
+ void AddDropperEntity(cDropperEntity * a_Entity)
+ {
+ mWriter.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Dropper");
+ mWriter.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Entity->GetContents());
+ mWriter.EndList();
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddJukeboxEntity(cJukeboxEntity * a_Jukebox)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Jukebox, "RecordPlayer");
- m_Writer.AddInt("Record", a_Jukebox->GetRecord());
- m_Writer.EndCompound();
-}
+ void AddFurnaceEntity(cFurnaceEntity * a_Furnace)
+ {
+ mWriter.BeginCompound("");
+ AddBasicTileEntity(a_Furnace, "Furnace");
+ mWriter.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Furnace->GetContents());
+ mWriter.EndList();
+ mWriter.AddShort("BurnTime", static_cast<Int16>(a_Furnace->GetFuelBurnTimeLeft()));
+ mWriter.AddShort("CookTime", static_cast<Int16>(a_Furnace->GetTimeCooked()));
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddMobSpawnerEntity(cMobSpawnerEntity * a_MobSpawner)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_MobSpawner, "MobSpawner");
- m_Writer.AddShort("Entity", static_cast<short>(a_MobSpawner->GetEntity()));
- m_Writer.AddString("EntityId", cMonster::MobTypeToVanillaName(a_MobSpawner->GetEntity()));
- m_Writer.AddShort("Delay", a_MobSpawner->GetSpawnDelay());
- m_Writer.EndCompound();
-}
+ void AddHopperEntity(cHopperEntity * a_Entity)
+ {
+ mWriter.BeginCompound("");
+ AddBasicTileEntity(a_Entity, "Hopper");
+ mWriter.BeginList("Items", TAG_Compound);
+ AddItemGrid(a_Entity->GetContents());
+ mWriter.EndList();
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddNoteEntity(cNoteEntity * a_Note)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_Note, "Music");
- m_Writer.AddByte("note", static_cast<Byte>(a_Note->GetPitch()));
- m_Writer.EndCompound();
-}
+ void AddJukeboxEntity(cJukeboxEntity * a_Jukebox)
+ {
+ mWriter.BeginCompound("");
+ AddBasicTileEntity(a_Jukebox, "RecordPlayer");
+ mWriter.AddInt("Record", a_Jukebox->GetRecord());
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddCommandBlockEntity(cCommandBlockEntity * a_CmdBlock)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_CmdBlock, "Control");
- m_Writer.AddString("Command", a_CmdBlock->GetCommand());
- m_Writer.AddInt ("SuccessCount", a_CmdBlock->GetResult());
- m_Writer.AddString("LastOutput", a_CmdBlock->GetLastOutput());
- m_Writer.AddByte ("TrackOutput", 1); // TODO 2014-01-18 xdot: Figure out what TrackOutput is and save it.
- m_Writer.EndCompound();
-}
+ void AddMobSpawnerEntity(cMobSpawnerEntity * a_MobSpawner)
+ {
+ mWriter.BeginCompound("");
+ AddBasicTileEntity(a_MobSpawner, "MobSpawner");
+ mWriter.AddShort("Entity", static_cast<short>(a_MobSpawner->GetEntity()));
+ mWriter.AddString("EntityId", cMonster::MobTypeToVanillaName(a_MobSpawner->GetEntity()));
+ mWriter.AddShort("Delay", a_MobSpawner->GetSpawnDelay());
+ mWriter.EndCompound();
+ }
-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));
- m_Writer.EndCompound();
-}
+ void AddNoteEntity(cNoteEntity * a_Note)
+ {
+ mWriter.BeginCompound("");
+ AddBasicTileEntity(a_Note, "Music");
+ mWriter.AddByte("note", static_cast<Byte>(a_Note->GetPitch()));
+ mWriter.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);
-
- // The new Block Entity format for a Mob Head. See: https://minecraft.gamepedia.com/Head#Block_entity
- m_Writer.BeginCompound("Owner");
- m_Writer.AddString("Id", a_MobHead->GetOwnerUUID().ToShortString());
- m_Writer.AddString("Name", a_MobHead->GetOwnerName());
- m_Writer.BeginCompound("Properties");
- m_Writer.BeginList("textures", TAG_Compound);
- m_Writer.BeginCompound("");
- m_Writer.AddString("Signature", a_MobHead->GetOwnerTextureSignature());
- m_Writer.AddString("Value", a_MobHead->GetOwnerTexture());
- m_Writer.EndCompound();
- m_Writer.EndList();
- m_Writer.EndCompound();
- m_Writer.EndCompound();
- m_Writer.EndCompound();
-}
+ void AddCommandBlockEntity(cCommandBlockEntity * a_CmdBlock)
+ {
+ mWriter.BeginCompound("");
+ AddBasicTileEntity(a_CmdBlock, "Control");
+ mWriter.AddString("Command", a_CmdBlock->GetCommand());
+ mWriter.AddInt ("SuccessCount", a_CmdBlock->GetResult());
+ mWriter.AddString("LastOutput", a_CmdBlock->GetLastOutput());
+ mWriter.AddByte ("TrackOutput", 1); // TODO 2014-01-18 xdot: Figure out what TrackOutput is and save it.
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddFlowerPotEntity(cFlowerPotEntity * a_FlowerPot)
-{
- m_Writer.BeginCompound("");
- AddBasicTileEntity(a_FlowerPot, "FlowerPot");
- m_Writer.AddInt ("Item", static_cast<Int32>(a_FlowerPot->GetItem().m_ItemType));
- m_Writer.AddInt ("Data", static_cast<Int32>(a_FlowerPot->GetItem().m_ItemDamage));
- m_Writer.EndCompound();
-}
+ void AddSignEntity(cSignEntity * a_Sign)
+ {
+ mWriter.BeginCompound("");
+ AddBasicTileEntity(a_Sign, "Sign");
+ mWriter.AddString("Text1", a_Sign->GetLine(0));
+ mWriter.AddString("Text2", a_Sign->GetLine(1));
+ mWriter.AddString("Text3", a_Sign->GetLine(2));
+ mWriter.AddString("Text4", a_Sign->GetLine(3));
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddBasicEntity(cEntity * a_Entity, const AString & a_ClassName)
-{
- m_Writer.AddString("id", a_ClassName);
- m_Writer.BeginList("Pos", TAG_Double);
- m_Writer.AddDouble("", a_Entity->GetPosX());
- m_Writer.AddDouble("", a_Entity->GetPosY());
- m_Writer.AddDouble("", a_Entity->GetPosZ());
- m_Writer.EndList();
- m_Writer.BeginList("Motion", TAG_Double);
- m_Writer.AddDouble("", a_Entity->GetSpeedX());
- m_Writer.AddDouble("", a_Entity->GetSpeedY());
- m_Writer.AddDouble("", a_Entity->GetSpeedZ());
- m_Writer.EndList();
- m_Writer.BeginList("Rotation", TAG_Double);
- m_Writer.AddDouble("", a_Entity->GetYaw());
- m_Writer.AddDouble("", a_Entity->GetPitch());
- m_Writer.EndList();
- m_Writer.AddFloat("Health", a_Entity->GetHealth());
-}
+ void AddMobHeadEntity(cMobHeadEntity * a_MobHead)
+ {
+ mWriter.BeginCompound("");
+ AddBasicTileEntity(a_MobHead, "Skull");
+ mWriter.AddByte ("SkullType", a_MobHead->GetType() & 0xFF);
+ mWriter.AddByte ("Rot", a_MobHead->GetRotation() & 0xFF);
+
+ // The new Block Entity format for a Mob Head. See: https://minecraft.gamepedia.com/Head#Block_entity
+ mWriter.BeginCompound("Owner");
+ mWriter.AddString("Id", a_MobHead->GetOwnerUUID().ToShortString());
+ mWriter.AddString("Name", a_MobHead->GetOwnerName());
+ mWriter.BeginCompound("Properties");
+ mWriter.BeginList("textures", TAG_Compound);
+ mWriter.BeginCompound("");
+ mWriter.AddString("Signature", a_MobHead->GetOwnerTextureSignature());
+ mWriter.AddString("Value", a_MobHead->GetOwnerTexture());
+ mWriter.EndCompound();
+ mWriter.EndList();
+ mWriter.EndCompound();
+ mWriter.EndCompound();
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddBoatEntity(cBoat * a_Boat)
-{
- m_Writer.BeginCompound("");
- AddBasicEntity(a_Boat, "Boat");
- m_Writer.AddString("Type", cBoat::MaterialToString(a_Boat->GetMaterial()));
- m_Writer.EndCompound();
-}
+ void AddFlowerPotEntity(cFlowerPotEntity * a_FlowerPot)
+ {
+ mWriter.BeginCompound("");
+ AddBasicTileEntity(a_FlowerPot, "FlowerPot");
+ mWriter.AddInt ("Item", static_cast<Int32>(a_FlowerPot->GetItem().m_ItemType));
+ mWriter.AddInt ("Data", static_cast<Int32>(a_FlowerPot->GetItem().m_ItemDamage));
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddEnderCrystalEntity(cEnderCrystal * a_EnderCrystal)
-{
- m_Writer.BeginCompound("");
- AddBasicEntity(a_EnderCrystal, "EnderCrystal");
- m_Writer.EndCompound();
-}
+ void AddBasicEntity(cEntity * a_Entity, const AString & a_ClassName)
+ {
+ mWriter.AddString("id", a_ClassName);
+ mWriter.BeginList("Pos", TAG_Double);
+ mWriter.AddDouble("", a_Entity->GetPosX());
+ mWriter.AddDouble("", a_Entity->GetPosY());
+ mWriter.AddDouble("", a_Entity->GetPosZ());
+ mWriter.EndList();
+ mWriter.BeginList("Motion", TAG_Double);
+ mWriter.AddDouble("", a_Entity->GetSpeedX());
+ mWriter.AddDouble("", a_Entity->GetSpeedY());
+ mWriter.AddDouble("", a_Entity->GetSpeedZ());
+ mWriter.EndList();
+ mWriter.BeginList("Rotation", TAG_Double);
+ mWriter.AddDouble("", a_Entity->GetYaw());
+ mWriter.AddDouble("", a_Entity->GetPitch());
+ mWriter.EndList();
+ mWriter.AddFloat("Health", a_Entity->GetHealth());
+ }
-void cNBTChunkSerializer::AddFallingBlockEntity(cFallingBlock * a_FallingBlock)
-{
- m_Writer.BeginCompound("");
- AddBasicEntity(a_FallingBlock, "FallingSand");
- m_Writer.AddInt("TileID", a_FallingBlock->GetBlockType());
- m_Writer.AddByte("Data", a_FallingBlock->GetBlockMeta());
- m_Writer.AddByte("Time", 1); // Unused in Cuberite, Vanilla said to need nonzero
- m_Writer.AddByte("DropItem", 1);
- m_Writer.AddByte("HurtEntities", a_FallingBlock->GetBlockType() == E_BLOCK_ANVIL);
- m_Writer.EndCompound();
-}
+ void AddBoatEntity(cBoat * a_Boat)
+ {
+ mWriter.BeginCompound("");
+ AddBasicEntity(a_Boat, "Boat");
+ mWriter.AddString("Type", cBoat::MaterialToString(a_Boat->GetMaterial()));
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddMinecartEntity(cMinecart * a_Minecart)
-{
- m_Writer.BeginCompound("");
- switch (a_Minecart->GetPayload())
- {
- case cMinecart::mpChest:
- {
- AddBasicEntity(a_Minecart, "MinecartChest");
- // Add chest contents into the Items tag:
- AddMinecartChestContents(static_cast<cMinecartWithChest *>(a_Minecart));
- break;
- }
- case cMinecart::mpFurnace:
- {
- AddBasicEntity(a_Minecart, "MinecartFurnace");
- // TODO: Add "Push" and "Fuel" tags
- break;
- }
- case cMinecart::mpHopper:
- {
- AddBasicEntity(a_Minecart, "MinecartHopper");
- // TODO: Add hopper contents?
- break;
- }
- case cMinecart::mpTNT:
- {
- AddBasicEntity(a_Minecart, "MinecartTNT");
- break;
- }
- case cMinecart::mpNone:
- {
- AddBasicEntity(a_Minecart, "MinecartRideable");
- break;
- }
- } // switch (Payload)
- m_Writer.EndCompound();
-}
+ void AddEnderCrystalEntity(cEnderCrystal * a_EnderCrystal)
+ {
+ mWriter.BeginCompound("");
+ AddBasicEntity(a_EnderCrystal, "EnderCrystal");
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddMonsterEntity(cMonster * a_Monster)
-{
- const char * EntityClass = nullptr;
- switch (a_Monster->GetMobType())
+ void AddFallingBlockEntity(cFallingBlock * a_FallingBlock)
{
- case mtBat: EntityClass = "Bat"; break;
- case mtBlaze: EntityClass = "Blaze"; break;
- case mtCaveSpider: EntityClass = "CaveSpider"; break;
- case mtChicken: EntityClass = "Chicken"; break;
- case mtCow: EntityClass = "Cow"; break;
- case mtCreeper: EntityClass = "Creeper"; break;
- case mtEnderDragon: EntityClass = "EnderDragon"; break;
- case mtEnderman: EntityClass = "Enderman"; break;
- case mtGhast: EntityClass = "Ghast"; break;
- case mtGiant: EntityClass = "Giant"; break;
- case mtGuardian: EntityClass = "Guardian"; break;
- case mtHorse: EntityClass = "Horse"; break;
- case mtIronGolem: EntityClass = "VillagerGolem"; break;
- case mtMagmaCube: EntityClass = "LavaSlime"; break;
- case mtMooshroom: EntityClass = "MushroomCow"; break;
- case mtOcelot: EntityClass = "Ozelot"; break;
- case mtPig: EntityClass = "Pig"; break;
- case mtRabbit: EntityClass = "Rabbit"; break;
- case mtSheep: EntityClass = "Sheep"; break;
- case mtSilverfish: EntityClass = "Silverfish"; break;
- case mtSkeleton: EntityClass = "Skeleton"; break;
- case mtSlime: EntityClass = "Slime"; break;
- case mtSnowGolem: EntityClass = "SnowMan"; break;
- case mtSpider: EntityClass = "Spider"; break;
- case mtSquid: EntityClass = "Squid"; break;
- case mtVillager: EntityClass = "Villager"; break;
- case mtWitch: EntityClass = "Witch"; break;
- case mtWither: EntityClass = "WitherBoss"; break;
- case mtWolf: EntityClass = "Wolf"; break;
- case mtZombie: EntityClass = "Zombie"; break;
- case mtZombiePigman: EntityClass = "PigZombie"; break;
- default:
- {
- ASSERT(!"Unhandled monster type");
- return;
- }
- } // switch (payload)
-
- 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", (a_Monster->CanPickUpLoot())? 1 : 0);
- m_Writer.AddString("CustomName", a_Monster->GetCustomName());
- m_Writer.AddByte("CustomNameVisible", static_cast<Byte>(a_Monster->IsCustomNameAlwaysVisible()));
-
- // Mob was leashed
- if (a_Monster->IsLeashed() || (a_Monster->GetLeashToPos() != nullptr))
- {
- m_Writer.AddByte("Leashed", 1);
+ mWriter.BeginCompound("");
+ AddBasicEntity(a_FallingBlock, "FallingSand");
+ mWriter.AddInt("TileID", a_FallingBlock->GetBlockType());
+ mWriter.AddByte("Data", a_FallingBlock->GetBlockMeta());
+ mWriter.AddByte("Time", 1); // Unused in Cuberite, Vanilla said to need nonzero
+ mWriter.AddByte("DropItem", 1);
+ mWriter.AddByte("HurtEntities", a_FallingBlock->GetBlockType() == E_BLOCK_ANVIL);
+ mWriter.EndCompound();
+ }
- const Vector3d * LeashedToPos = nullptr;
- if (a_Monster->GetLeashToPos() != nullptr)
- {
- LeashedToPos = a_Monster->GetLeashToPos();
- }
- else if (a_Monster->GetLeashedTo()->IsLeashKnot())
- {
- LeashedToPos = & a_Monster->GetLeashedTo()->GetPosition();
- }
- if (LeashedToPos != nullptr)
- {
- m_Writer.BeginCompound("Leash");
- m_Writer.AddDouble("X", LeashedToPos->x);
- m_Writer.AddDouble("Y", LeashedToPos->y);
- m_Writer.AddDouble("Z", LeashedToPos->z);
- m_Writer.EndCompound();
- }
- }
- switch (a_Monster->GetMobType())
- {
- case mtBat:
- {
- m_Writer.AddByte("BatFlags", static_cast<const cBat *>(a_Monster)->IsHanging());
- break;
- }
- case mtCreeper:
- {
- const cCreeper *Creeper = static_cast<const cCreeper *>(a_Monster);
- m_Writer.AddByte("powered", Creeper->IsCharged());
- m_Writer.AddByte("ignited", Creeper->IsBlowing());
- break;
- }
- case mtEnderman:
- {
- const cEnderman *Enderman = static_cast<const cEnderman *>(a_Monster);
- m_Writer.AddShort("carried", static_cast<Int16>(Enderman->GetCarriedBlock()));
- m_Writer.AddShort("carriedData", static_cast<Int16>(Enderman->GetCarriedMeta()));
- break;
- }
- case mtHorse:
- {
- const cHorse *Horse = static_cast<const cHorse *>(a_Monster);
- m_Writer.AddByte("ChestedHorse", Horse->IsChested()? 1 : 0);
- m_Writer.AddByte("EatingHaystack", Horse->IsEating()? 1 : 0);
- m_Writer.AddByte("Tame", Horse->IsTame()? 1: 0);
- m_Writer.AddInt ("Type", Horse->GetHorseType());
- m_Writer.AddInt ("Color", Horse->GetHorseColor());
- m_Writer.AddInt ("Style", Horse->GetHorseStyle());
- m_Writer.AddInt ("ArmorType", Horse->GetHorseArmour());
- m_Writer.AddByte("Saddle", Horse->IsSaddled()? 1 : 0);
- m_Writer.AddInt ("Age", Horse->GetAge());
- break;
- }
- case mtMagmaCube:
- {
- m_Writer.AddInt("Size", static_cast<const cMagmaCube *>(a_Monster)->GetSize());
- break;
- }
- case mtOcelot:
+
+ void AddMinecartEntity(cMinecart * a_Minecart)
+ {
+ mWriter.BeginCompound("");
+
+ switch (a_Minecart->GetPayload())
{
- const auto *Ocelot = static_cast<const cOcelot *>(a_Monster);
- if (!Ocelot->GetOwnerName().empty())
+ case cMinecart::mpChest:
{
- m_Writer.AddString("Owner", Ocelot->GetOwnerName());
+ AddBasicEntity(a_Minecart, "MinecartChest");
+ // Add chest contents into the Items tag:
+ AddMinecartChestContents(static_cast<cMinecartWithChest *>(a_Minecart));
+ break;
}
- if (!Ocelot->GetOwnerUUID().IsNil())
+ case cMinecart::mpFurnace:
{
- m_Writer.AddString("OwnerUUID", Ocelot->GetOwnerUUID().ToShortString());
+ AddBasicEntity(a_Minecart, "MinecartFurnace");
+ // TODO: Add "Push" and "Fuel" tags
+ break;
}
- m_Writer.AddByte("Sitting", Ocelot->IsSitting() ? 1 : 0);
- m_Writer.AddInt("CatType", Ocelot->GetOcelotType());
- m_Writer.AddInt("Age", Ocelot->GetAge());
- break;
- }
- case mtPig:
- {
- m_Writer.AddInt("Age", static_cast<const cPig *>(a_Monster)->GetAge());
- break;
- }
- case mtRabbit:
- {
- const cRabbit * Rabbit = static_cast<const cRabbit *>(a_Monster);
- m_Writer.AddInt("RabbitType", static_cast<Int32>(Rabbit->GetRabbitType()));
- m_Writer.AddInt("MoreCarrotTicks", Rabbit->GetMoreCarrotTicks());
- m_Writer.AddInt("Age", Rabbit->GetAge());
- break;
- }
- case mtSheep:
- {
- const cSheep *Sheep = static_cast<const cSheep *>(a_Monster);
- m_Writer.AddByte("Sheared", Sheep->IsSheared()? 1 : 0);
- m_Writer.AddByte("Color", static_cast<Byte>(Sheep->GetFurColor()));
- m_Writer.AddInt ("Age", Sheep->GetAge());
- break;
- }
- case mtSlime:
- {
- m_Writer.AddInt("Size", static_cast<const cSlime *>(a_Monster)->GetSize());
- break;
- }
- case mtSkeleton:
- {
- m_Writer.AddByte("SkeletonType", (static_cast<const cSkeleton *>(a_Monster)->IsWither() ? 1 : 0));
- break;
- }
- case mtVillager:
- {
- const cVillager *Villager = static_cast<const cVillager *>(a_Monster);
- m_Writer.AddInt("Profession", Villager->GetVilType());
- m_Writer.AddInt("Age", Villager->GetAge());
- break;
- }
- case mtWither:
- {
- m_Writer.AddInt("Invul", static_cast<Int32>(static_cast<const cWither *>(a_Monster)->GetWitherInvulnerableTicks()));
- break;
- }
- case mtWolf:
- {
- const cWolf *Wolf = static_cast<const cWolf *>(a_Monster);
- if (!Wolf->GetOwnerName().empty())
+ case cMinecart::mpHopper:
{
- m_Writer.AddString("Owner", Wolf->GetOwnerName());
+ AddBasicEntity(a_Minecart, "MinecartHopper");
+ // TODO: Add hopper contents?
+ break;
}
- if (!Wolf->GetOwnerUUID().IsNil())
+ case cMinecart::mpTNT:
{
- m_Writer.AddString("OwnerUUID", Wolf->GetOwnerUUID().ToShortString());
+ AddBasicEntity(a_Minecart, "MinecartTNT");
+ break;
}
- m_Writer.AddByte("Sitting", Wolf->IsSitting() ? 1 : 0);
- m_Writer.AddByte("Angry", Wolf->IsAngry() ? 1 : 0);
- m_Writer.AddByte("CollarColor", static_cast<Byte>(Wolf->GetCollarColor()));
- m_Writer.AddInt ("Age", Wolf->GetAge());
- break;
- }
- case mtZombie:
- {
- const cZombie *Zombie = static_cast<const cZombie *>(a_Monster);
- m_Writer.AddByte("IsVillager", Zombie->IsVillagerZombie() ? 1 : 0);
- m_Writer.AddByte("IsConverting", Zombie->IsConverting() ? 1 : 0);
- m_Writer.AddInt ("Age", Zombie->GetAge());
- break;
- }
- case mtZombiePigman:
- {
- m_Writer.AddInt("Age", static_cast<const cZombiePigman *>(a_Monster)->GetAge());
- break;
- }
- case mtBlaze:
- case mtCaveSpider:
- case mtChicken:
- case mtCow:
- case mtEnderDragon:
- case mtGhast:
- case mtGiant:
- case mtGuardian:
- case mtIronGolem:
- case mtMooshroom:
- case mtSilverfish:
- case mtSnowGolem:
- case mtSpider:
- case mtSquid:
- case mtWitch:
- {
- // Other mobs have no special tags.
- break;
- }
- case mtInvalidType:
- {
- ASSERT(!"cNBTChunkSerializer::AddMonsterEntity: Recieved mob of invalid type");
- break;
- }
- }
- m_Writer.EndCompound();
-}
-
-
-
-
-
-void cNBTChunkSerializer::AddPickupEntity(cPickup * a_Pickup)
-{
- m_Writer.BeginCompound("");
- AddBasicEntity(a_Pickup, "Item");
- AddItem(a_Pickup->GetItem(), -1, "Item");
- m_Writer.AddShort("Age", static_cast<Int16>(a_Pickup->GetAge()));
- m_Writer.EndCompound();
-}
+ case cMinecart::mpNone:
+ {
+ AddBasicEntity(a_Minecart, "MinecartRideable");
+ break;
+ }
+ } // switch (Payload)
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddProjectileEntity(cProjectileEntity * a_Projectile)
-{
- m_Writer.BeginCompound("");
- AddBasicEntity(a_Projectile, a_Projectile->GetMCAClassName());
- m_Writer.AddByte("inGround", a_Projectile->IsInGround() ? 1 : 0);
- switch (a_Projectile->GetProjectileKind())
+ void AddMonsterEntity(cMonster * a_Monster)
+ {
+ const char * EntityClass = nullptr;
+ switch (a_Monster->GetMobType())
{
- case cProjectileEntity::pkArrow:
- {
- cArrowEntity * Arrow = static_cast<cArrowEntity *>(a_Projectile);
-
- m_Writer.AddShort("xTile", static_cast<Int16>(Arrow->GetBlockHit().x));
- m_Writer.AddShort("yTile", static_cast<Int16>(Arrow->GetBlockHit().y));
- m_Writer.AddShort("zTile", static_cast<Int16>(Arrow->GetBlockHit().z));
- m_Writer.AddByte("pickup", Arrow->GetPickupState());
- m_Writer.AddDouble("damage", Arrow->GetDamageCoeff());
- break;
- }
- case cProjectileEntity::pkSplashPotion:
- {
- cSplashPotionEntity * Potion = static_cast<cSplashPotionEntity *>(a_Projectile);
-
- m_Writer.AddInt("EffectType", static_cast<Int16>(Potion->GetEntityEffectType()));
- m_Writer.AddInt("EffectDuration", static_cast<Int16>(Potion->GetEntityEffect().GetDuration()));
- m_Writer.AddShort("EffectIntensity", Potion->GetEntityEffect().GetIntensity());
- m_Writer.AddDouble("EffectDistanceModifier", Potion->GetEntityEffect().GetDistanceModifier());
- m_Writer.AddInt("PotionName", Potion->GetPotionColor());
- break;
- }
- case cProjectileEntity::pkGhastFireball:
- {
- m_Writer.AddInt("ExplosionPower", 1);
- break;
- }
- case cProjectileEntity::pkFireCharge:
- case cProjectileEntity::pkWitherSkull:
- case cProjectileEntity::pkEnderPearl:
- {
- break;
- }
+ case mtBat: EntityClass = "Bat"; break;
+ case mtBlaze: EntityClass = "Blaze"; break;
+ case mtCaveSpider: EntityClass = "CaveSpider"; break;
+ case mtChicken: EntityClass = "Chicken"; break;
+ case mtCow: EntityClass = "Cow"; break;
+ case mtCreeper: EntityClass = "Creeper"; break;
+ case mtEnderDragon: EntityClass = "EnderDragon"; break;
+ case mtEnderman: EntityClass = "Enderman"; break;
+ case mtGhast: EntityClass = "Ghast"; break;
+ case mtGiant: EntityClass = "Giant"; break;
+ case mtGuardian: EntityClass = "Guardian"; break;
+ case mtHorse: EntityClass = "Horse"; break;
+ case mtIronGolem: EntityClass = "VillagerGolem"; break;
+ case mtMagmaCube: EntityClass = "LavaSlime"; break;
+ case mtMooshroom: EntityClass = "MushroomCow"; break;
+ case mtOcelot: EntityClass = "Ozelot"; break;
+ case mtPig: EntityClass = "Pig"; break;
+ case mtRabbit: EntityClass = "Rabbit"; break;
+ case mtSheep: EntityClass = "Sheep"; break;
+ case mtSilverfish: EntityClass = "Silverfish"; break;
+ case mtSkeleton: EntityClass = "Skeleton"; break;
+ case mtSlime: EntityClass = "Slime"; break;
+ case mtSnowGolem: EntityClass = "SnowMan"; break;
+ case mtSpider: EntityClass = "Spider"; break;
+ case mtSquid: EntityClass = "Squid"; break;
+ case mtVillager: EntityClass = "Villager"; break;
+ case mtWitch: EntityClass = "Witch"; break;
+ case mtWither: EntityClass = "WitherBoss"; break;
+ case mtWolf: EntityClass = "Wolf"; break;
+ case mtZombie: EntityClass = "Zombie"; break;
+ case mtZombiePigman: EntityClass = "PigZombie"; break;
default:
{
- ASSERT(!"Unsaved projectile entity!");
+ ASSERT(!"Unhandled monster type");
+ return;
}
- } // switch (ProjectileKind)
+ } // switch (payload)
+
+ mWriter.BeginCompound("");
+ AddBasicEntity(a_Monster, EntityClass);
+ mWriter.BeginList("DropChances", TAG_Float);
+ mWriter.AddFloat("", a_Monster->GetDropChanceWeapon());
+ mWriter.AddFloat("", a_Monster->GetDropChanceHelmet());
+ mWriter.AddFloat("", a_Monster->GetDropChanceChestplate());
+ mWriter.AddFloat("", a_Monster->GetDropChanceLeggings());
+ mWriter.AddFloat("", a_Monster->GetDropChanceBoots());
+ mWriter.EndList();
+ mWriter.AddByte("CanPickUpLoot", (a_Monster->CanPickUpLoot())? 1 : 0);
+ mWriter.AddString("CustomName", a_Monster->GetCustomName());
+ mWriter.AddByte("CustomNameVisible", static_cast<Byte>(a_Monster->IsCustomNameAlwaysVisible()));
+
+ // Mob was leashed
+ if (a_Monster->IsLeashed() || (a_Monster->GetLeashToPos() != nullptr))
+ {
+ mWriter.AddByte("Leashed", 1);
- if (!a_Projectile->GetCreatorName().empty())
- {
- m_Writer.AddString("ownerName", a_Projectile->GetCreatorName());
- }
- m_Writer.EndCompound();
-}
+ const Vector3d * LeashedToPos = nullptr;
+ if (a_Monster->GetLeashToPos() != nullptr)
+ {
+ LeashedToPos = a_Monster->GetLeashToPos();
+ }
+ else if (a_Monster->GetLeashedTo()->IsLeashKnot())
+ {
+ LeashedToPos = & a_Monster->GetLeashedTo()->GetPosition();
+ }
+ if (LeashedToPos != nullptr)
+ {
+ mWriter.BeginCompound("Leash");
+ mWriter.AddDouble("X", LeashedToPos->x);
+ mWriter.AddDouble("Y", LeashedToPos->y);
+ mWriter.AddDouble("Z", LeashedToPos->z);
+ mWriter.EndCompound();
+ }
+ }
+ switch (a_Monster->GetMobType())
+ {
+ case mtBat:
+ {
+ mWriter.AddByte("BatFlags", static_cast<const cBat *>(a_Monster)->IsHanging());
+ break;
+ }
+ case mtCreeper:
+ {
+ const cCreeper *Creeper = static_cast<const cCreeper *>(a_Monster);
+ mWriter.AddByte("powered", Creeper->IsCharged());
+ mWriter.AddByte("ignited", Creeper->IsBlowing());
+ break;
+ }
+ case mtEnderman:
+ {
+ const cEnderman *Enderman = static_cast<const cEnderman *>(a_Monster);
+ mWriter.AddShort("carried", static_cast<Int16>(Enderman->GetCarriedBlock()));
+ mWriter.AddShort("carriedData", static_cast<Int16>(Enderman->GetCarriedMeta()));
+ break;
+ }
+ case mtHorse:
+ {
+ const cHorse *Horse = static_cast<const cHorse *>(a_Monster);
+ mWriter.AddByte("ChestedHorse", Horse->IsChested()? 1 : 0);
+ mWriter.AddByte("EatingHaystack", Horse->IsEating()? 1 : 0);
+ mWriter.AddByte("Tame", Horse->IsTame()? 1: 0);
+ mWriter.AddInt ("Type", Horse->GetHorseType());
+ mWriter.AddInt ("Color", Horse->GetHorseColor());
+ mWriter.AddInt ("Style", Horse->GetHorseStyle());
+ mWriter.AddInt ("ArmorType", Horse->GetHorseArmour());
+ mWriter.AddByte("Saddle", Horse->IsSaddled()? 1 : 0);
+ mWriter.AddInt ("Age", Horse->GetAge());
+ break;
+ }
+ case mtMagmaCube:
+ {
+ mWriter.AddInt("Size", static_cast<const cMagmaCube *>(a_Monster)->GetSize());
+ break;
+ }
+ case mtOcelot:
+ {
+ const auto *Ocelot = static_cast<const cOcelot *>(a_Monster);
+ if (!Ocelot->GetOwnerName().empty())
+ {
+ mWriter.AddString("Owner", Ocelot->GetOwnerName());
+ }
+ if (!Ocelot->GetOwnerUUID().IsNil())
+ {
+ mWriter.AddString("OwnerUUID", Ocelot->GetOwnerUUID().ToShortString());
+ }
+ mWriter.AddByte("Sitting", Ocelot->IsSitting() ? 1 : 0);
+ mWriter.AddInt("CatType", Ocelot->GetOcelotType());
+ mWriter.AddInt("Age", Ocelot->GetAge());
+ break;
+ }
+ case mtPig:
+ {
+ mWriter.AddInt("Age", static_cast<const cPig *>(a_Monster)->GetAge());
+ break;
+ }
+ case mtRabbit:
+ {
+ const cRabbit * Rabbit = static_cast<const cRabbit *>(a_Monster);
+ mWriter.AddInt("RabbitType", static_cast<Int32>(Rabbit->GetRabbitType()));
+ mWriter.AddInt("MoreCarrotTicks", Rabbit->GetMoreCarrotTicks());
+ mWriter.AddInt("Age", Rabbit->GetAge());
+ break;
+ }
+ case mtSheep:
+ {
+ const cSheep *Sheep = static_cast<const cSheep *>(a_Monster);
+ mWriter.AddByte("Sheared", Sheep->IsSheared()? 1 : 0);
+ mWriter.AddByte("Color", static_cast<Byte>(Sheep->GetFurColor()));
+ mWriter.AddInt ("Age", Sheep->GetAge());
+ break;
+ }
+ case mtSlime:
+ {
+ mWriter.AddInt("Size", static_cast<const cSlime *>(a_Monster)->GetSize());
+ break;
+ }
+ case mtSkeleton:
+ {
+ mWriter.AddByte("SkeletonType", (static_cast<const cSkeleton *>(a_Monster)->IsWither() ? 1 : 0));
+ break;
+ }
+ case mtVillager:
+ {
+ const cVillager *Villager = static_cast<const cVillager *>(a_Monster);
+ mWriter.AddInt("Profession", Villager->GetVilType());
+ mWriter.AddInt("Age", Villager->GetAge());
+ break;
+ }
+ case mtWither:
+ {
+ mWriter.AddInt("Invul", static_cast<Int32>(static_cast<const cWither *>(a_Monster)->GetWitherInvulnerableTicks()));
+ break;
+ }
+ case mtWolf:
+ {
+ const cWolf *Wolf = static_cast<const cWolf *>(a_Monster);
+ if (!Wolf->GetOwnerName().empty())
+ {
+ mWriter.AddString("Owner", Wolf->GetOwnerName());
+ }
+ if (!Wolf->GetOwnerUUID().IsNil())
+ {
+ mWriter.AddString("OwnerUUID", Wolf->GetOwnerUUID().ToShortString());
+ }
+ mWriter.AddByte("Sitting", Wolf->IsSitting() ? 1 : 0);
+ mWriter.AddByte("Angry", Wolf->IsAngry() ? 1 : 0);
+ mWriter.AddByte("CollarColor", static_cast<Byte>(Wolf->GetCollarColor()));
+ mWriter.AddInt ("Age", Wolf->GetAge());
+ break;
+ }
+ case mtZombie:
+ {
+ const cZombie *Zombie = static_cast<const cZombie *>(a_Monster);
+ mWriter.AddByte("IsVillager", Zombie->IsVillagerZombie() ? 1 : 0);
+ mWriter.AddByte("IsConverting", Zombie->IsConverting() ? 1 : 0);
+ mWriter.AddInt ("Age", Zombie->GetAge());
+ break;
+ }
+ case mtZombiePigman:
+ {
+ mWriter.AddInt("Age", static_cast<const cZombiePigman *>(a_Monster)->GetAge());
+ break;
+ }
+ case mtBlaze:
+ case mtCaveSpider:
+ case mtChicken:
+ case mtCow:
+ case mtEnderDragon:
+ case mtGhast:
+ case mtGiant:
+ case mtGuardian:
+ case mtIronGolem:
+ case mtMooshroom:
+ case mtSilverfish:
+ case mtSnowGolem:
+ case mtSpider:
+ case mtSquid:
+ case mtWitch:
+ {
+ // Other mobs have no special tags.
+ break;
+ }
+ case mtInvalidType:
+ {
+ ASSERT(!"NBTChunkSerializer::SerializerCollector::AddMonsterEntity: Recieved mob of invalid type");
+ break;
+ }
+ }
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddHangingEntity(cHangingEntity * a_Hanging)
-{
- m_Writer.AddInt("TileX", FloorC(a_Hanging->GetPosX()));
- m_Writer.AddInt("TileY", FloorC(a_Hanging->GetPosY()));
- m_Writer.AddInt("TileZ", FloorC(a_Hanging->GetPosZ()));
- m_Writer.AddByte("Facing", a_Hanging->GetProtocolFacing());
-}
+ void AddPickupEntity(cPickup * a_Pickup)
+ {
+ mWriter.BeginCompound("");
+ AddBasicEntity(a_Pickup, "Item");
+ AddItem(a_Pickup->GetItem(), -1, "Item");
+ mWriter.AddShort("Age", static_cast<Int16>(a_Pickup->GetAge()));
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddTNTEntity(cTNTEntity * a_TNT)
-{
- m_Writer.BeginCompound("");
- AddBasicEntity(a_TNT, "PrimedTnt");
- m_Writer.AddByte("Fuse", static_cast<unsigned char>(a_TNT->GetFuseTicks()));
- m_Writer.EndCompound();
-}
+ void AddProjectileEntity(cProjectileEntity * a_Projectile)
+ {
+ mWriter.BeginCompound("");
+ AddBasicEntity(a_Projectile, a_Projectile->GetMCAClassName());
+ mWriter.AddByte("inGround", a_Projectile->IsInGround() ? 1 : 0);
+ switch (a_Projectile->GetProjectileKind())
+ {
+ case cProjectileEntity::pkArrow:
+ {
+ cArrowEntity * Arrow = static_cast<cArrowEntity *>(a_Projectile);
+
+ mWriter.AddShort("xTile", static_cast<Int16>(Arrow->GetBlockHit().x));
+ mWriter.AddShort("yTile", static_cast<Int16>(Arrow->GetBlockHit().y));
+ mWriter.AddShort("zTile", static_cast<Int16>(Arrow->GetBlockHit().z));
+ mWriter.AddByte("pickup", Arrow->GetPickupState());
+ mWriter.AddDouble("damage", Arrow->GetDamageCoeff());
+ break;
+ }
+ case cProjectileEntity::pkSplashPotion:
+ {
+ cSplashPotionEntity * Potion = static_cast<cSplashPotionEntity *>(a_Projectile);
+
+ mWriter.AddInt("EffectType", static_cast<Int16>(Potion->GetEntityEffectType()));
+ mWriter.AddInt("EffectDuration", static_cast<Int16>(Potion->GetEntityEffect().GetDuration()));
+ mWriter.AddShort("EffectIntensity", Potion->GetEntityEffect().GetIntensity());
+ mWriter.AddDouble("EffectDistanceModifier", Potion->GetEntityEffect().GetDistanceModifier());
+ mWriter.AddInt("PotionName", Potion->GetPotionColor());
+ break;
+ }
+ case cProjectileEntity::pkGhastFireball:
+ {
+ mWriter.AddInt("ExplosionPower", 1);
+ break;
+ }
+ case cProjectileEntity::pkFireCharge:
+ case cProjectileEntity::pkWitherSkull:
+ case cProjectileEntity::pkEnderPearl:
+ {
+ break;
+ }
+ default:
+ {
+ ASSERT(!"Unsaved projectile entity!");
+ }
+ } // switch (ProjectileKind)
-void cNBTChunkSerializer::AddExpOrbEntity(cExpOrb * a_ExpOrb)
-{
- m_Writer.BeginCompound("");
- AddBasicEntity(a_ExpOrb, "XPOrb");
- m_Writer.AddShort("Age", static_cast<Int16>(a_ExpOrb->GetAge()));
- m_Writer.AddShort("Value", static_cast<Int16>(a_ExpOrb->GetReward()));
- m_Writer.EndCompound();
-}
+ if (!a_Projectile->GetCreatorName().empty())
+ {
+ mWriter.AddString("ownerName", a_Projectile->GetCreatorName());
+ }
+ mWriter.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", static_cast<Byte>(a_ItemFrame->GetItemRotation()));
- m_Writer.AddFloat("ItemDropChance", 1.0F);
- m_Writer.EndCompound();
-}
+ void AddHangingEntity(cHangingEntity * a_Hanging)
+ {
+ mWriter.AddInt("TileX", FloorC(a_Hanging->GetPosX()));
+ mWriter.AddInt("TileY", FloorC(a_Hanging->GetPosY()));
+ mWriter.AddInt("TileZ", FloorC(a_Hanging->GetPosZ()));
+ mWriter.AddByte("Facing", a_Hanging->GetProtocolFacing());
+ }
-void cNBTChunkSerializer::AddLeashKnotEntity(cLeashKnot * a_LeashKnot)
-{
- m_Writer.BeginCompound("");
- AddBasicEntity(a_LeashKnot, "LeashKnot");
- AddHangingEntity(a_LeashKnot);
- m_Writer.EndCompound();
-}
+ void AddTNTEntity(cTNTEntity * a_TNT)
+ {
+ mWriter.BeginCompound("");
+ AddBasicEntity(a_TNT, "PrimedTnt");
+ mWriter.AddByte("Fuse", static_cast<unsigned char>(a_TNT->GetFuseTicks()));
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddPaintingEntity(cPainting * a_Painting)
-{
- m_Writer.BeginCompound("");
- AddBasicEntity(a_Painting, "Painting");
- AddHangingEntity(a_Painting);
- m_Writer.AddString("Motive", a_Painting->GetName());
- m_Writer.EndCompound();
-}
+ void AddExpOrbEntity(cExpOrb * a_ExpOrb)
+ {
+ mWriter.BeginCompound("");
+ AddBasicEntity(a_ExpOrb, "XPOrb");
+ mWriter.AddShort("Age", static_cast<Int16>(a_ExpOrb->GetAge()));
+ mWriter.AddShort("Value", static_cast<Int16>(a_ExpOrb->GetReward()));
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::AddMinecartChestContents(cMinecartWithChest * a_Minecart)
-{
- m_Writer.BeginList("Items", TAG_Compound);
- for (int i = 0; i < cMinecartWithChest::ContentsHeight * cMinecartWithChest::ContentsWidth; i++)
- {
- const cItem & Item = a_Minecart->GetSlot(i);
- if (Item.IsEmpty())
- {
- continue;
- }
- AddItem(Item, i);
- }
- m_Writer.EndList();
-}
+ void AddItemFrameEntity(cItemFrame * a_ItemFrame)
+ {
+ mWriter.BeginCompound("");
+ AddBasicEntity(a_ItemFrame, "ItemFrame");
+ AddHangingEntity(a_ItemFrame);
+ AddItem(a_ItemFrame->GetItem(), -1, "Item");
+ mWriter.AddByte("ItemRotation", static_cast<Byte>(a_ItemFrame->GetItemRotation()));
+ mWriter.AddFloat("ItemDropChance", 1.0F);
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::LightIsValid(bool a_IsLightValid)
-{
- m_IsLightValid = a_IsLightValid;
-}
+ void AddLeashKnotEntity(cLeashKnot * a_LeashKnot)
+ {
+ mWriter.BeginCompound("");
+ AddBasicEntity(a_LeashKnot, "LeashKnot");
+ AddHangingEntity(a_LeashKnot);
+ mWriter.EndCompound();
+ }
-void cNBTChunkSerializer::HeightMap(const cChunkDef::HeightMap * a_HeightMap)
-{
- for (int RelX = 0; RelX < cChunkDef::Width; RelX++)
+ void AddPaintingEntity(cPainting * a_Painting)
{
- for (int RelZ = 0; RelZ < cChunkDef::Width; RelZ++)
- {
- int Height = cChunkDef::GetHeight(*a_HeightMap, RelX, RelZ);
- m_VanillaHeightMap[(RelZ << 4) | RelX] = Height;
- }
+ mWriter.BeginCompound("");
+ AddBasicEntity(a_Painting, "Painting");
+ AddHangingEntity(a_Painting);
+ mWriter.AddString("Motive", a_Painting->GetName());
+ mWriter.EndCompound();
}
-}
-void cNBTChunkSerializer::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap)
-{
- memcpy(m_Biomes, a_BiomeMap, sizeof(m_Biomes));
- for (size_t i = 0; i < ARRAYCOUNT(m_Biomes); i++)
+ void AddMinecartChestContents(cMinecartWithChest * a_Minecart)
{
- if ((*a_BiomeMap)[i] < 255)
- {
- // Normal MC biome, copy as-is:
- m_VanillaBiomes[i] = static_cast<Byte>((*a_BiomeMap)[i]);
- }
- else
- {
- // TODO: MCS-specific biome, need to map to some basic MC biome:
- ASSERT(!"Unimplemented MCS-specific biome");
- return;
- }
- } // for i - m_BiomeMap[]
- m_BiomesAreValid = true;
-}
+ mWriter.BeginList("Items", TAG_Compound);
+ for (int i = 0; i < cMinecartWithChest::ContentsHeight * cMinecartWithChest::ContentsWidth; i++)
+ {
+ const cItem & Item = a_Minecart->GetSlot(i);
+ if (Item.IsEmpty())
+ {
+ continue;
+ }
+ AddItem(Item, i);
+ }
+ mWriter.EndList();
+ }
+}; // SerializerCollector
+
+////////////////////////////////////////////////////////////////////////////////
+// NBTChunkSerializer:
-void cNBTChunkSerializer::Entity(cEntity * a_Entity)
+bool NBTChunkSerializer::serialize(const cWorld & aWorld, cChunkCoords aCoords, cFastNBTWriter & aWriter)
{
- // Add entity into NBT:
- if (m_IsTagOpen)
+ SerializerCollector serializer(aWriter);
+ aWriter.BeginCompound("Level");
+ aWriter.AddInt("xPos", aCoords.m_ChunkX);
+ aWriter.AddInt("zPos", aCoords.m_ChunkZ);
+ if (!aWorld.GetChunkData(aCoords, serializer))
{
- if (!m_HasHadEntity)
- {
- m_Writer.EndList();
- m_Writer.BeginList("Entities", TAG_Compound);
- }
+ aWriter.EndCompound(); // "Level"
+ return false;
}
- else
+ serializer.Finish(); // Close NBT tags
+
+ // Save biomes, both MCS (IntArray) and MC-vanilla (ByteArray):
+ if (serializer.mBiomesAreValid)
{
- m_Writer.BeginList("Entities", TAG_Compound);
+ aWriter.AddByteArray("Biomes", reinterpret_cast<const char *>(serializer.mVanillaBiomes), ARRAYCOUNT(serializer.mVanillaBiomes));
+ aWriter.AddIntArray ("MCSBiomes", reinterpret_cast<const int *>(serializer.mBiomes), ARRAYCOUNT(serializer.mBiomes));
}
- m_IsTagOpen = true;
- m_HasHadEntity = true;
- switch (a_Entity->GetEntityType())
+ // Save heightmap (Vanilla require this):
+ aWriter.AddIntArray("HeightMap", reinterpret_cast<const int *>(serializer.mVanillaHeightMap), ARRAYCOUNT(serializer.mVanillaHeightMap));
+
+ // Save blockdata:
+ aWriter.BeginList("Sections", TAG_Compound);
+ for (size_t Y = 0; Y != cChunkData::NumSections; ++Y)
{
- case cEntity::etBoat: AddBoatEntity (static_cast<cBoat *> (a_Entity)); break;
- case cEntity::etEnderCrystal: AddEnderCrystalEntity(static_cast<cEnderCrystal *> (a_Entity)); break;
- case cEntity::etFallingBlock: AddFallingBlockEntity(static_cast<cFallingBlock *> (a_Entity)); break;
- case cEntity::etMinecart: AddMinecartEntity (static_cast<cMinecart *> (a_Entity)); break;
- case cEntity::etMonster: AddMonsterEntity (static_cast<cMonster *> (a_Entity)); break;
- case cEntity::etPickup: AddPickupEntity (static_cast<cPickup *> (a_Entity)); break;
- case cEntity::etProjectile: AddProjectileEntity (static_cast<cProjectileEntity *>(a_Entity)); break;
- case cEntity::etTNT: AddTNTEntity (static_cast<cTNTEntity *> (a_Entity)); break;
- case cEntity::etExpOrb: AddExpOrbEntity (static_cast<cExpOrb *> (a_Entity)); break;
- case cEntity::etItemFrame: AddItemFrameEntity (static_cast<cItemFrame *> (a_Entity)); break;
- case cEntity::etLeashKnot: AddLeashKnotEntity (static_cast<cLeashKnot *> (a_Entity)); break;
- case cEntity::etPainting: AddPaintingEntity (static_cast<cPainting *> (a_Entity)); break;
- case cEntity::etPlayer: return; // Players aren't saved into the world
- case cEntity::etFloater: return; // Floaters aren't saved either
- default:
+ auto section = serializer.m_Data.GetSection(Y);
+ if (section == nullptr)
{
- ASSERT(!"Unhandled entity type is being saved");
- break;
+ continue;
}
- }
-}
+ aWriter.BeginCompound("");
+ aWriter.AddByteArray("Blocks", reinterpret_cast<const char *>(section->m_BlockTypes), ARRAYCOUNT(section->m_BlockTypes));
+ aWriter.AddByteArray("Data", reinterpret_cast<const char *>(section->m_BlockMetas), ARRAYCOUNT(section->m_BlockMetas));
+ #ifdef DEBUG_SKYLIGHT
+ aWriter.AddByteArray("BlockLight", reinterpret_cast<const char *>(section->m_BlockSkyLight), ARRAYCOUNT(section->m_BlockSkyLight));
+ #else
+ aWriter.AddByteArray("BlockLight", reinterpret_cast<const char *>(section->m_BlockLight), ARRAYCOUNT(section->m_BlockLight));
+ #endif
-
-
-void cNBTChunkSerializer::BlockEntity(cBlockEntity * a_Entity)
-{
- if (m_IsTagOpen)
- {
- if (!m_HasHadBlockEntity)
- {
- m_Writer.EndList();
- m_Writer.BeginList("TileEntities", TAG_Compound);
- }
+ aWriter.AddByteArray("SkyLight", reinterpret_cast<const char *>(section->m_BlockSkyLight), ARRAYCOUNT(section->m_BlockSkyLight));
+ aWriter.AddByte("Y", static_cast<unsigned char>(Y));
+ aWriter.EndCompound();
}
- else
- {
- m_Writer.BeginList("TileEntities", TAG_Compound);
- }
- m_IsTagOpen = true;
+ aWriter.EndList(); // "Sections"
- // Add tile-entity into NBT:
- switch (a_Entity->GetBlockType())
+ // Store the information that the lighting is valid.
+ // For compatibility reason, the default is "invalid" (missing) - this means older data is re-lighted upon loading.
+ if (serializer.mIsLightValid)
{
- case E_BLOCK_BEACON: AddBeaconEntity (static_cast<cBeaconEntity *> (a_Entity)); break;
- case E_BLOCK_BED: AddBedEntity (static_cast<cBedEntity *> (a_Entity)); break;
- case E_BLOCK_BREWING_STAND: AddBrewingstandEntity(static_cast<cBrewingstandEntity *>(a_Entity)); break;
- case E_BLOCK_CHEST: AddChestEntity (static_cast<cChestEntity *> (a_Entity), a_Entity->GetBlockType()); break;
- case E_BLOCK_COMMAND_BLOCK: AddCommandBlockEntity(static_cast<cCommandBlockEntity *>(a_Entity)); break;
- case E_BLOCK_DISPENSER: AddDispenserEntity (static_cast<cDispenserEntity *> (a_Entity)); break;
- case E_BLOCK_DROPPER: AddDropperEntity (static_cast<cDropperEntity *> (a_Entity)); break;
- case E_BLOCK_ENDER_CHEST: /* No data to be saved */ break;
- case E_BLOCK_FLOWER_POT: AddFlowerPotEntity (static_cast<cFlowerPotEntity *> (a_Entity)); break;
- case E_BLOCK_FURNACE: AddFurnaceEntity (static_cast<cFurnaceEntity *> (a_Entity)); break;
- case E_BLOCK_HEAD: AddMobHeadEntity (static_cast<cMobHeadEntity *> (a_Entity)); break;
- case E_BLOCK_HOPPER: AddHopperEntity (static_cast<cHopperEntity *> (a_Entity)); break;
- case E_BLOCK_JUKEBOX: AddJukeboxEntity (static_cast<cJukeboxEntity *> (a_Entity)); break;
- case E_BLOCK_LIT_FURNACE: AddFurnaceEntity (static_cast<cFurnaceEntity *> (a_Entity)); break;
- case E_BLOCK_MOB_SPAWNER: AddMobSpawnerEntity (static_cast<cMobSpawnerEntity *> (a_Entity)); break;
- case E_BLOCK_NOTE_BLOCK: AddNoteEntity (static_cast<cNoteEntity *> (a_Entity)); break;
- case E_BLOCK_SIGN_POST: AddSignEntity (static_cast<cSignEntity *> (a_Entity)); break;
- case E_BLOCK_TRAPPED_CHEST: AddChestEntity (static_cast<cChestEntity *> (a_Entity), a_Entity->GetBlockType()); break;
- case E_BLOCK_WALLSIGN: AddSignEntity (static_cast<cSignEntity *> (a_Entity)); break;
-
- default:
- {
- ASSERT(!"Unhandled block entity saved into Anvil");
- }
+ aWriter.AddByte("MCSIsLightValid", 1);
}
- m_HasHadBlockEntity = true;
-}
-
+ // Save the world age to the chunk data. Required by vanilla and mcedit.
+ aWriter.AddLong("LastUpdate", aWorld.GetWorldAge());
+ // Store the flag that the chunk has all the ores, trees, dungeons etc. Cuberite chunks are always complete.
+ aWriter.AddByte("TerrainPopulated", 1);
+ aWriter.EndCompound(); // "Level"
+ return true;
+}
diff --git a/src/WorldStorage/NBTChunkSerializer.h b/src/WorldStorage/NBTChunkSerializer.h
index a7cb1de1e..b14a58c40 100644
--- a/src/WorldStorage/NBTChunkSerializer.h
+++ b/src/WorldStorage/NBTChunkSerializer.h
@@ -1,139 +1,27 @@
-
// NBTChunkSerializer.h
-// Declares the cNBTChunkSerializer class that is used for saving individual chunks into NBT format used by Anvil
-
-
-
-
-
#pragma once
-#include "../ChunkDataCallback.h"
-
// fwd:
class cFastNBTWriter;
-class cEntity;
-class cBlockEntity;
-class cBoat;
-class cBeaconEntity;
-class cBedEntity;
-class cBrewingstandEntity;
-class cChestEntity;
-class cCommandBlockEntity;
-class cDispenserEntity;
-class cDropperEntity;
-class cEnderCrystal;
-class cFurnaceEntity;
-class cHopperEntity;
-class cJukeboxEntity;
-class cNoteEntity;
-class cSignEntity;
-class cMobHeadEntity;
-class cMobSpawnerEntity;
-class cFlowerPotEntity;
-class cFallingBlock;
-class cMinecart;
-class cMinecartWithChest;
-class cMonster;
-class cPickup;
-class cItemGrid;
-class cProjectileEntity;
-class cTNTEntity;
-class cExpOrb;
-class cHangingEntity;
-class cItemFrame;
-class cLeashKnot;
-class cPainting;
+class cWorld;
-class cNBTChunkSerializer :
- public cChunkDataCopyCollector
+/** Saves the chunk data into a NBT format, used by the Anvil storage.
+The Writer is expected to be set up so that the serializer can write the chunk's top level "Level" NBT tag immediately.
+Provides a single static entry point that does all the work, through a hidden worker class in the CPP file. */
+class NBTChunkSerializer
{
public:
- cChunkDef::BiomeMap m_Biomes;
- unsigned char m_VanillaBiomes[cChunkDef::Width * cChunkDef::Width];
- int m_VanillaHeightMap[cChunkDef::Width * cChunkDef::Width];
- bool m_BiomesAreValid;
-
-
- cNBTChunkSerializer(cFastNBTWriter & a_Writer);
-
- /** Close NBT tags that we've opened */
- void Finish(void);
-
- bool IsLightValid(void) const { return m_IsLightValid; }
-
-protected:
-
- /* From cChunkDataCopyCollector we inherit:
- - cChunkData m_Data */
-
- cFastNBTWriter & m_Writer;
-
- bool m_IsTagOpen; // True if a tag has been opened in the callbacks and not yet closed.
- bool m_HasHadEntity; // True if any Entity has already been received and processed
- bool m_HasHadBlockEntity; // True if any BlockEntity has already been received and processed
- bool m_IsLightValid; // True if the chunk lighting is valid
-
-
- /** Writes an item into the writer, if slot >= 0, adds the Slot tag. The compound is named as requested. */
- void AddItem(const cItem & a_Item, int a_Slot, const AString & a_CompoundName = "");
-
- /** Writes an item grid into the writer; begins the stored slot numbers with a_BeginSlotNum. Note that it doesn't begin nor end the list tag */
- void AddItemGrid(const cItemGrid & a_Grid, int a_BeginSlotNum = 0);
-
- // Block entities:
- void AddBasicTileEntity (cBlockEntity * a_Entity, const char * a_EntityTypeID);
- void AddBeaconEntity (cBeaconEntity * a_Entity);
- void AddBedEntity (cBedEntity * a_Entity);
- void AddBrewingstandEntity(cBrewingstandEntity * a_Brewingstand);
- void AddChestEntity (cChestEntity * a_Entity, BLOCKTYPE a_ChestType);
- void AddDispenserEntity (cDispenserEntity * a_Entity);
- void AddDropperEntity (cDropperEntity * a_Entity);
- void AddFurnaceEntity (cFurnaceEntity * a_Furnace);
- void AddHopperEntity (cHopperEntity * a_Entity);
- void AddJukeboxEntity (cJukeboxEntity * a_Jukebox);
- void AddMobSpawnerEntity (cMobSpawnerEntity * a_MobSpawner);
- 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 AddLeashKnotEntity (cLeashKnot * a_LeashKnot);
- void AddPaintingEntity (cPainting * a_Painting);
-
- void AddMinecartChestContents(cMinecartWithChest * a_Minecart);
-
- // cChunkDataSeparateCollector overrides:
- virtual void LightIsValid(bool a_IsLightValid) override;
- virtual void HeightMap(const cChunkDef::HeightMap * a_HeightMap) override;
- virtual void BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) override;
- virtual void Entity(cEntity * a_Entity) override;
- virtual void BlockEntity(cBlockEntity * a_Entity) override;
-} ; // class cNBTChunkSerializer
-
-
-
+ /** Serializes the chunk into the specified writer.
+ Returns true on success, false on failure (chunk not present etc.) */
+ static bool serialize(const cWorld & aWorld, cChunkCoords aCoords, cFastNBTWriter & aWriter);
+};
diff --git a/src/WorldStorage/WSSAnvil.cpp b/src/WorldStorage/WSSAnvil.cpp
index d2f3284f5..8c6c9fb67 100755
--- a/src/WorldStorage/WSSAnvil.cpp
+++ b/src/WorldStorage/WSSAnvil.cpp
@@ -491,68 +491,11 @@ void cWSSAnvil::CopyNBTData(const cParsedNBT & a_NBT, int a_Tag, const AString &
bool cWSSAnvil::SaveChunkToNBT(const cChunkCoords & a_Chunk, cFastNBTWriter & a_Writer)
{
- a_Writer.BeginCompound("Level");
- a_Writer.AddInt("xPos", a_Chunk.m_ChunkX);
- a_Writer.AddInt("zPos", a_Chunk.m_ChunkZ);
-
- cNBTChunkSerializer Serializer(a_Writer);
- if (!m_World->GetChunkData(a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ, Serializer))
+ if (!NBTChunkSerializer::serialize(*m_World, a_Chunk, a_Writer))
{
- LOGWARNING("Cannot get chunk [%d, %d] data for NBT saving", a_Chunk.m_ChunkX, a_Chunk.m_ChunkZ);
+ LOGWARNING("Failed to save chunk %s.", a_Chunk.ToString());
return false;
}
- Serializer.Finish(); // Close NBT tags
-
- // Save biomes, both MCS (IntArray) and MC-vanilla (ByteArray):
- if (Serializer.m_BiomesAreValid)
- {
- a_Writer.AddByteArray("Biomes", reinterpret_cast<const char *>(Serializer.m_VanillaBiomes), ARRAYCOUNT(Serializer.m_VanillaBiomes));
- a_Writer.AddIntArray ("MCSBiomes", reinterpret_cast<const int *>(Serializer.m_Biomes), ARRAYCOUNT(Serializer.m_Biomes));
- }
-
- // Save heightmap (Vanilla require this):
- a_Writer.AddIntArray("HeightMap", reinterpret_cast<const int *>(Serializer.m_VanillaHeightMap), ARRAYCOUNT(Serializer.m_VanillaHeightMap));
-
- // Save blockdata:
- a_Writer.BeginList("Sections", TAG_Compound);
- for (size_t Y = 0; Y != cChunkData::NumSections; ++Y)
- {
- auto Section = Serializer.m_Data.GetSection(Y);
- if (Section == nullptr)
- {
- continue;
- }
-
- a_Writer.BeginCompound("");
- a_Writer.AddByteArray("Blocks", reinterpret_cast<const char *>(Section->m_BlockTypes), ARRAYCOUNT(Section->m_BlockTypes));
- a_Writer.AddByteArray("Data", reinterpret_cast<const char *>(Section->m_BlockMetas), ARRAYCOUNT(Section->m_BlockMetas));
-
- #ifdef DEBUG_SKYLIGHT
- a_Writer.AddByteArray("BlockLight", reinterpret_cast<const char *>(Section->m_BlockSkyLight), ARRAYCOUNT(Section->m_BlockSkyLight));
- #else
- a_Writer.AddByteArray("BlockLight", reinterpret_cast<const char *>(Section->m_BlockLight), ARRAYCOUNT(Section->m_BlockLight));
- #endif
-
- a_Writer.AddByteArray("SkyLight", reinterpret_cast<const char *>(Section->m_BlockSkyLight), ARRAYCOUNT(Section->m_BlockSkyLight));
- a_Writer.AddByte("Y", static_cast<unsigned char>(Y));
- a_Writer.EndCompound();
- }
- a_Writer.EndList(); // "Sections"
-
- // Store the information that the lighting is valid.
- // For compatibility reason, the default is "invalid" (missing) - this means older data is re-lighted upon loading.
- if (Serializer.IsLightValid())
- {
- a_Writer.AddByte("MCSIsLightValid", 1);
- }
-
- // Save the world age to the chunk data. Required by vanilla and mcedit.
- a_Writer.AddLong("LastUpdate", m_World->GetWorldAge());
-
- // Store the flag that the chunk has all the ores, trees, dungeons etc. MCS chunks are always complete.
- a_Writer.AddByte("TerrainPopulated", 1);
-
- a_Writer.EndCompound(); // "Level"
return true;
}