summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--MCServer/crafting.txt2
-rw-r--r--src/Bindings/AllToLua.pkg1
-rw-r--r--src/ClientHandle.cpp27
-rw-r--r--src/ClientHandle.h4
-rw-r--r--src/Entities/Player.cpp3
-rw-r--r--src/Globals.h10
-rw-r--r--src/Inventory.cpp25
-rw-r--r--src/Inventory.h3
-rw-r--r--src/Items/ItemEmptyMap.h62
-rw-r--r--src/Items/ItemHandler.cpp4
-rw-r--r--src/Items/ItemHandler.h8
-rw-r--r--src/Items/ItemMap.h43
-rw-r--r--src/Map.cpp494
-rw-r--r--src/Map.h209
-rw-r--r--src/Protocol/Protocol.h4
-rw-r--r--src/Protocol/Protocol125.cpp52
-rw-r--r--src/Protocol/Protocol125.h3
-rw-r--r--src/Protocol/Protocol17x.cpp55
-rw-r--r--src/Protocol/Protocol17x.h3
-rw-r--r--src/Protocol/ProtocolRecognizer.cpp30
-rw-r--r--src/Protocol/ProtocolRecognizer.h3
-rw-r--r--src/World.cpp7
-rw-r--r--src/World.h1
-rw-r--r--src/WorldStorage/MapSerializer.cpp276
-rw-r--r--src/WorldStorage/MapSerializer.h82
25 files changed, 1410 insertions, 1 deletions
diff --git a/MCServer/crafting.txt b/MCServer/crafting.txt
index fe9a465d0..92abe24cb 100644
--- a/MCServer/crafting.txt
+++ b/MCServer/crafting.txt
@@ -156,7 +156,7 @@ Lighter = IronIngot, 1:1 | Flint, 2:2
Lighter = IronIngot, 2:1 | Flint, 1:2
Bucket = IronIngot, 1:1, 2:2, 3:1
Compass = IronIngot, 2:1, 1:2, 3:2, 2:3 | RedstoneDust, 2:2
-Map = Paper, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Compass, 2:2
+EmptyMap = Paper, 1:1, 1:2, 1:3, 2:1, 2:3, 3:1, 3:2, 3:3 | Compass, 2:2
Watch = GoldIngot, 2:1, 1:2, 3:2, 2:3 | RedstoneDust, 2:2
FishingRod = Stick, 1:3, 2:2, 3:1 | String, 3:2, 3:3
FishingRod = Stick, 3:3, 2:2, 1:1 | String, 1:2, 1:3
diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg
index 1f08c66dc..ef61f55f0 100644
--- a/src/Bindings/AllToLua.pkg
+++ b/src/Bindings/AllToLua.pkg
@@ -48,6 +48,7 @@ $cfile "../ItemGrid.h"
$cfile "../BlockEntities/BlockEntity.h"
$cfile "../BlockEntities/BlockEntityWithItems.h"
$cfile "../BlockEntities/ChestEntity.h"
+$cfile "../BlockEntities/CommandBlockEntity.h"
$cfile "../BlockEntities/DropSpenserEntity.h"
$cfile "../BlockEntities/DispenserEntity.h"
$cfile "../BlockEntities/DropperEntity.h"
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 84286fc41..a6a74bf4a 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -2074,6 +2074,33 @@ void cClientHandle::SendInventorySlot(char a_WindowID, short a_SlotNum, const cI
+void cClientHandle::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length)
+{
+ m_Protocol->SendMapColumn(a_ID, a_X, a_Y, a_Colors, a_Length);
+}
+
+
+
+
+
+void cClientHandle::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators)
+{
+ m_Protocol->SendMapDecorators(a_ID, a_Decorators);
+}
+
+
+
+
+
+void cClientHandle::SendMapInfo(int a_ID, unsigned int a_Scale)
+{
+ m_Protocol->SendMapInfo(a_ID, a_Scale);
+}
+
+
+
+
+
void cClientHandle::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount)
{
m_Protocol->SendParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmmount);
diff --git a/src/ClientHandle.h b/src/ClientHandle.h
index aefca7233..8911164bf 100644
--- a/src/ClientHandle.h
+++ b/src/ClientHandle.h
@@ -17,6 +17,7 @@
#include "ChunkDef.h"
#include "ByteBuffer.h"
#include "Scoreboard.h"
+#include "Map.h"
@@ -112,6 +113,9 @@ public:
void SendGameMode (eGameMode a_GameMode);
void SendHealth (void);
void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item);
+ void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length);
+ void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators);
+ void SendMapInfo (int a_ID, unsigned int a_Scale);
void SendPaintingSpawn (const cPainting & a_Painting);
void SendPickupSpawn (const cPickup & a_Pickup);
void SendEntityAnimation (const cEntity & a_Entity, char a_Animation);
diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp
index 70ddb3c98..0152bfc5b 100644
--- a/src/Entities/Player.cpp
+++ b/src/Entities/Player.cpp
@@ -254,6 +254,9 @@ void cPlayer::Tick(float a_Dt, cChunk & a_Chunk)
HandleFloater();
}
+ // Update items (e.g. Maps)
+ m_Inventory.UpdateItems();
+
// Send Player List (Once per m_LastPlayerListTime/1000 ms)
cTimer t1;
if (m_LastPlayerListTime + cPlayer::PLAYER_LIST_TIME_MS <= t1.GetNowTime())
diff --git a/src/Globals.h b/src/Globals.h
index 1e90d83e9..7ee045130 100644
--- a/src/Globals.h
+++ b/src/Globals.h
@@ -235,6 +235,16 @@ public:
+/** Clamp a_X to the specified range. */
+template <typename T>
+T Clamp(T a_X, T a_Min, T a_Max)
+{
+ return std::min(std::max(a_X, a_Min), a_Max);
+}
+
+
+
+
// Common headers (part 2, with macros):
#include "ChunkDef.h"
diff --git a/src/Inventory.cpp b/src/Inventory.cpp
index 0e1cedc85..7f434adfd 100644
--- a/src/Inventory.cpp
+++ b/src/Inventory.cpp
@@ -515,6 +515,31 @@ bool cInventory::AddToBar( cItem & a_Item, const int a_Offset, const int a_Size,
+void cInventory::UpdateItems(void)
+{
+ const cItem & Slot = GetEquippedItem();
+
+ if (Slot.IsEmpty())
+ {
+ return;
+ }
+
+ switch (Slot.m_ItemType)
+ {
+ case E_ITEM_MAP:
+ {
+ ItemHandler(Slot.m_ItemType)->OnUpdate(m_Owner.GetWorld(), &m_Owner, Slot);
+ break;
+ }
+
+ default: break;
+ }
+}
+
+
+
+
+
void cInventory::SaveToJson(Json::Value & a_Value)
{
// The JSON originally included the 4 crafting slots and the result, so we have to put empty items there, too:
diff --git a/src/Inventory.h b/src/Inventory.h
index 3c6a19de8..fd2089a13 100644
--- a/src/Inventory.h
+++ b/src/Inventory.h
@@ -150,6 +150,9 @@ public:
/// Sends the slot contents to the owner
void SendSlot(int a_SlotNum);
+ /// Update items (e.g. Maps)
+ void UpdateItems(void);
+
/// Converts an armor slot number into the ID for the EntityEquipment packet
static int ArmorSlotNumToEntityEquipmentID(short a_ArmorSlotNum);
diff --git a/src/Items/ItemEmptyMap.h b/src/Items/ItemEmptyMap.h
new file mode 100644
index 000000000..b06cf9d13
--- /dev/null
+++ b/src/Items/ItemEmptyMap.h
@@ -0,0 +1,62 @@
+
+// ItemEmptyMap.h
+
+
+
+
+
+#pragma once
+
+#include "../Entities/Entity.h"
+#include "../Item.h"
+
+
+
+
+
+class cItemEmptyMapHandler :
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+ static const unsigned int DEFAULT_SCALE = 0;
+
+public:
+ cItemEmptyMapHandler() :
+ super(E_ITEM_EMPTY_MAP)
+ {
+ }
+
+ virtual bool OnItemUse(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_Dir) override
+ {
+ UNUSED(a_Item);
+ UNUSED(a_BlockX);
+ UNUSED(a_BlockZ);
+ UNUSED(a_Dir);
+
+ // The map center is fixed at the central point of the 8x8 block of chunks you are standing in when you right-click it.
+
+ const int RegionWidth = cChunkDef::Width * 8 * pow(2, DEFAULT_SCALE);
+
+ int CenterX = round(a_Player->GetPosX() / (float) RegionWidth) * RegionWidth;
+ int CenterZ = round(a_Player->GetPosZ() / (float) RegionWidth) * RegionWidth;
+
+ cMap * NewMap = a_World->CreateMap(CenterX, CenterZ, DEFAULT_SCALE);
+
+ // Remove empty map from inventory
+ if (!a_Player->GetInventory().RemoveOneEquippedItem())
+ {
+ ASSERT(!"Inventory mismatch");
+ return true;
+ }
+
+ if (NewMap == NULL)
+ {
+ return true;
+ }
+
+ a_Player->GetInventory().AddItem(cItem(E_ITEM_MAP, 1, NewMap->GetID()), true, true);
+
+ return true;
+ }
+} ;
diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp
index e9bb616a6..c10d13edc 100644
--- a/src/Items/ItemHandler.cpp
+++ b/src/Items/ItemHandler.cpp
@@ -18,6 +18,7 @@
#include "ItemComparator.h"
#include "ItemDoor.h"
#include "ItemDye.h"
+#include "ItemEmptyMap.h"
#include "ItemFishingRod.h"
#include "ItemFlowerPot.h"
#include "ItemFood.h"
@@ -25,6 +26,7 @@
#include "ItemHoe.h"
#include "ItemLeaves.h"
#include "ItemLighter.h"
+#include "ItemMap.h"
#include "ItemMinecart.h"
#include "ItemNetherWart.h"
#include "ItemPainting.h"
@@ -103,11 +105,13 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType)
case E_ITEM_COMPARATOR: return new cItemComparatorHandler(a_ItemType);
case E_ITEM_DYE: return new cItemDyeHandler(a_ItemType);
case E_ITEM_EGG: return new cItemEggHandler();
+ case E_ITEM_EMPTY_MAP: return new cItemEmptyMapHandler();
case E_ITEM_ENDER_PEARL: return new cItemEnderPearlHandler();
case E_ITEM_FIREWORK_ROCKET: return new cItemFireworkHandler();
case E_ITEM_FISHING_ROD: return new cItemFishingRodHandler(a_ItemType);
case E_ITEM_FLINT_AND_STEEL: return new cItemLighterHandler(a_ItemType);
case E_ITEM_FLOWER_POT: return new cItemFlowerPotHandler(a_ItemType);
+ case E_ITEM_MAP: return new cItemMapHandler();
case E_ITEM_ITEM_FRAME: return new cItemItemFrameHandler(a_ItemType);
case E_ITEM_NETHER_WART: return new cItemNetherWartHandler(a_ItemType);
case E_ITEM_PAINTING: return new cItemPaintingHandler(a_ItemType);
diff --git a/src/Items/ItemHandler.h b/src/Items/ItemHandler.h
index 1a6bb044f..ef3f37a7a 100644
--- a/src/Items/ItemHandler.h
+++ b/src/Items/ItemHandler.h
@@ -32,6 +32,14 @@ public:
UNUSED(a_BlockZ);
UNUSED(a_BlockFace);
}
+
+ /// Called every tick while the item is on the player's inventory (Used by maps) - For now, called only for equipped items
+ virtual void OnUpdate(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item)
+ {
+ UNUSED(a_World);
+ UNUSED(a_Player);
+ UNUSED(a_Item);
+ }
/// Called while the player diggs a block using this item
virtual bool OnDiggingBlock(cWorld * a_World, cPlayer * a_Player, const cItem & a_HeldItem, int a_BlockX, int a_BlockY, int a_BlockZ, eBlockFace a_BlockFace);
diff --git a/src/Items/ItemMap.h b/src/Items/ItemMap.h
new file mode 100644
index 000000000..9bb16b189
--- /dev/null
+++ b/src/Items/ItemMap.h
@@ -0,0 +1,43 @@
+
+// ItemMap.h
+
+
+
+
+
+#pragma once
+
+#include "../Entities/Entity.h"
+#include "../Item.h"
+
+
+
+
+
+class cItemMapHandler :
+ public cItemHandler
+{
+ typedef cItemHandler super;
+
+ static const unsigned int DEFAULT_RADIUS = 128;
+
+public:
+ cItemMapHandler() :
+ super(E_ITEM_MAP)
+ {
+ }
+
+ virtual void OnUpdate(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item)
+ {
+ cMap * Map = a_World->GetMapData(a_Item.m_ItemDamage);
+
+ if (Map == NULL)
+ {
+ return;
+ }
+
+ Map->UpdateRadius(*a_Player, DEFAULT_RADIUS);
+
+ Map->UpdateClient(a_Player);
+ }
+} ;
diff --git a/src/Map.cpp b/src/Map.cpp
new file mode 100644
index 000000000..cb5472a22
--- /dev/null
+++ b/src/Map.cpp
@@ -0,0 +1,494 @@
+
+// Map.cpp
+
+#include "Globals.h"
+
+#include "Map.h"
+
+#include "ClientHandle.h"
+#include "World.h"
+#include "Chunk.h"
+#include "Entities/Player.h"
+#include "FastRandom.h"
+
+
+
+
+
+cMapDecorator::cMapDecorator(cMap * a_Map, eType a_Type, int a_X, int a_Z, int a_Rot)
+ : m_Map(a_Map)
+ , m_Type(a_Type)
+ , m_PixelX(a_X)
+ , m_PixelZ(a_Z)
+ , m_Rot(a_Rot)
+ , m_Player(NULL)
+{
+}
+
+
+
+
+
+cMapDecorator::cMapDecorator(cMap * a_Map, cPlayer * a_Player)
+ : m_Map(a_Map)
+ , m_Type(E_TYPE_PLAYER)
+ , m_Player(a_Player)
+{
+ Update();
+}
+
+
+
+
+
+void cMapDecorator::Update(void)
+{
+ if (m_Player != NULL)
+ {
+ ASSERT(m_Map != NULL);
+ unsigned int PixelWidth = m_Map->GetPixelWidth();
+
+ int InsideWidth = (m_Map->GetWidth() / 2) - 1;
+ int InsideHeight = (m_Map->GetHeight() / 2) - 1;
+
+ int PixelX = (m_Player->GetPosX() - m_Map->GetCenterX()) / PixelWidth;
+ int PixelZ = (m_Player->GetPosZ() - m_Map->GetCenterZ()) / PixelWidth;
+
+ // Center of pixel
+ m_PixelX = (2 * PixelX) + 1;
+ m_PixelZ = (2 * PixelZ) + 1;
+
+ if ((PixelX > -InsideWidth) && (PixelX <= InsideWidth) && (PixelZ > -InsideHeight) && (PixelZ <= InsideHeight))
+ {
+ double Yaw = m_Player->GetYaw();
+
+ if (m_Map->GetDimension() == dimNether)
+ {
+ cFastRandom Random;
+
+ Int64 WorldAge = m_Player->GetWorld()->GetWorldAge();
+
+ // TODO 2014-02-19 xdot: Refine
+ m_Rot = Random.NextInt(16, WorldAge);
+ }
+ else
+ {
+ m_Rot = (Yaw * 16) / 360;
+ }
+
+ m_Type = E_TYPE_PLAYER;
+ }
+ else
+ {
+ if ((abs(PixelX) > 320.0) || (abs(PixelZ) > 320.0))
+ {
+ // TODO 2014-02-18 xdot: Remove decorator
+ }
+
+ m_Rot = 0;
+
+ m_Type = E_TYPE_PLAYER_OUTSIDE;
+
+ // Move to border
+ if (PixelX <= -InsideWidth)
+ {
+ m_PixelX = (2 * -InsideWidth) + 1;
+ }
+ if (PixelZ <= -InsideHeight)
+ {
+ m_PixelZ = (2 * -InsideHeight) + 1;
+ }
+ if (PixelX > InsideWidth)
+ {
+ m_PixelX = (2 * InsideWidth) + 1;
+ }
+ if (PixelZ > InsideHeight)
+ {
+ m_PixelZ = (2 * InsideHeight) + 1;
+ }
+ }
+ }
+}
+
+
+
+
+
+cMap::cMap(unsigned int a_ID, cWorld * a_World)
+ : m_ID(a_ID)
+ , m_Width(cChunkDef::Width * 8)
+ , m_Height(cChunkDef::Width * 8)
+ , m_Scale(3)
+ , m_CenterX(0)
+ , m_CenterZ(0)
+ , m_World(a_World)
+{
+ m_Data.assign(m_Width * m_Height, 0);
+
+ Printf(m_Name, "map_%i", m_ID);
+}
+
+
+
+
+
+cMap::cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, unsigned int a_Scale)
+ : m_ID(a_ID)
+ , m_Width(cChunkDef::Width * 8)
+ , m_Height(cChunkDef::Width * 8)
+ , m_Scale(a_Scale)
+ , m_CenterX(a_CenterX)
+ , m_CenterZ(a_CenterZ)
+ , m_World(a_World)
+{
+ m_Data.assign(m_Width * m_Height, 0);
+
+ Printf(m_Name, "map_%i", m_ID);
+}
+
+
+
+
+
+void cMap::UpdateRadius(int a_PixelX, int a_PixelZ, unsigned int a_Radius)
+{
+ int PixelRadius = a_Radius / GetPixelWidth();
+
+ unsigned int StartX = Clamp(a_PixelX - PixelRadius, 0, (int)m_Width);
+ unsigned int StartZ = Clamp(a_PixelZ - PixelRadius, 0, (int)m_Height);
+
+ unsigned int EndX = Clamp(a_PixelX + PixelRadius, 0, (int)m_Width);
+ unsigned int EndZ = Clamp(a_PixelZ + PixelRadius, 0, (int)m_Height);
+
+ for (unsigned int X = StartX; X < EndX; ++X)
+ {
+ for (unsigned int Z = StartZ; Z < EndZ; ++Z)
+ {
+ int dX = X - a_PixelX;
+ int dZ = Z - a_PixelZ;
+
+ if ((dX * dX) + (dZ * dZ) < (PixelRadius * PixelRadius))
+ {
+ UpdatePixel(X, Z);
+ }
+ }
+ }
+}
+
+
+
+
+
+void cMap::UpdateRadius(cPlayer & a_Player, unsigned int a_Radius)
+{
+ unsigned int PixelWidth = GetPixelWidth();
+
+ int PixelX = (a_Player.GetPosX() - m_CenterX) / PixelWidth + (m_Width / 2);
+ int PixelZ = (a_Player.GetPosZ() - m_CenterZ) / PixelWidth + (m_Height / 2);
+
+ UpdateRadius(PixelX, PixelZ, a_Radius);
+}
+
+
+
+
+
+bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Z)
+{
+ /*
+ unsigned int PixelWidth = GetPixelWidth();
+
+ int BlockX = m_CenterX + ((a_X - m_Width) * PixelWidth);
+ int BlockZ = m_CenterZ + ((a_Y - m_Height) * PixelWidth);
+
+ int ChunkX, ChunkY, ChunkZ;
+ m_World->BlockToChunk(BlockX, 0, BlockZ, ChunkX, ChunkY, ChunkZ);
+
+ int RelX = BlockX - (ChunkX * cChunkDef::Width);
+ int RelZ = BlockZ - (ChunkZ * cChunkDef::Width);
+
+ class cCalculatePixelCb :
+ public cChunkCallback
+ {
+ cMap * m_Map;
+
+ int m_RelX, m_RelZ;
+
+ ColorID m_PixelData;
+
+ public:
+ cCalculatePixelCb(cMap * a_Map, int a_RelX, int a_RelZ)
+ : m_Map(a_Map), m_RelX(a_RelX), m_RelZ(a_RelZ), m_PixelData(4) {}
+
+ virtual bool Item(cChunk * a_Chunk) override
+ {
+ if (a_Chunk == NULL)
+ {
+ return false;
+ }
+
+ unsigned int PixelWidth = m_Map->GetPixelWidth();
+
+ for (unsigned int X = m_RelX; X < m_RelX + PixelWidth; ++X)
+ {
+ for (unsigned int Z = m_RelZ; Z < m_RelZ + PixelWidth; ++Z)
+ {
+ int Height = a_Chunk->GetHeight(X, Z);
+
+ if (Height > 0)
+ {
+ // TODO
+ }
+ }
+ }
+
+ m_PixelData = 8; // Debug
+
+ return false;
+ }
+
+ ColorID GetPixelData(void) const
+ {
+ return m_PixelData;
+ }
+ } CalculatePixelCb(this, RelX, RelZ);
+
+ ASSERT(m_World != NULL);
+ m_World->DoWithChunk(ChunkX, ChunkZ, CalculatePixelCb);
+ */
+
+ m_Data[a_Z + (a_X * m_Height)] = 4;
+
+ return true;
+}
+
+
+
+
+
+void cMap::UpdateDecorators(void)
+{
+ for (cMapDecoratorList::iterator it = m_Decorators.begin(); it != m_Decorators.end(); ++it)
+ {
+ it->Update();
+ }
+}
+
+
+
+
+
+void cMap::UpdateClient(cPlayer * a_Player)
+{
+ ASSERT(a_Player != NULL);
+ cClientHandle * Handle = a_Player->GetClientHandle();
+
+ if (Handle == NULL)
+ {
+ return;
+ }
+
+ Int64 WorldAge = a_Player->GetWorld()->GetWorldAge();
+
+ // Remove invalid clients
+ for (cMapClientList::iterator it = m_Clients.begin(); it != m_Clients.end();)
+ {
+ // Check if client is active
+ if (it->m_LastUpdate < WorldAge - 5)
+ {
+ // Remove associated decorators
+ for (cMapDecoratorList::iterator it2 = m_Decorators.begin(); it2 != m_Decorators.end();)
+ {
+ if (it2->GetPlayer()->GetClientHandle() == Handle)
+ {
+ // Erase decorator
+ cMapDecoratorList::iterator temp = it2;
+ ++it2;
+ m_Decorators.erase(temp);
+ }
+ else
+ {
+ ++it2;
+ }
+ }
+
+ // Erase client
+ cMapClientList::iterator temp = it;
+ ++it;
+ m_Clients.erase(temp);
+ }
+ else
+ {
+ ++it;
+ }
+ }
+
+ // Linear search for client state
+ for (cMapClientList::iterator it = m_Clients.begin(); it != m_Clients.end(); ++it)
+ {
+ if (it->m_Handle == Handle)
+ {
+ it->m_LastUpdate = WorldAge;
+
+ if (it->m_SendInfo)
+ {
+ Handle->SendMapInfo(m_ID, m_Scale);
+
+ it->m_SendInfo = false;
+
+ return;
+ }
+
+ ++it->m_NextDecoratorUpdate;
+
+ if (it->m_NextDecoratorUpdate >= 4)
+ {
+ // TODO 2014-02-19 xdot
+ // This is dangerous as the player object may have been destroyed before the decorator is erased from the list
+ UpdateDecorators();
+
+ Handle->SendMapDecorators(m_ID, m_Decorators);
+
+ it->m_NextDecoratorUpdate = 0;
+ }
+ else
+ {
+ ++it->m_DataUpdate;
+
+ unsigned int Y = (it->m_DataUpdate * 11) % m_Width;
+
+ const Byte * Colors = &m_Data[Y * m_Height];
+
+ Handle->SendMapColumn(m_ID, Y, 0, Colors, m_Height);
+ }
+
+ return;
+ }
+ }
+
+ // New player, construct a new client state
+ cMapClient MapClient;
+
+ MapClient.m_LastUpdate = WorldAge;
+ MapClient.m_SendInfo = true;
+ MapClient.m_Handle = a_Player->GetClientHandle();
+
+ m_Clients.push_back(MapClient);
+
+ // Insert new decorator
+ cMapDecorator PlayerDecorator(this, a_Player);
+
+ m_Decorators.push_back(PlayerDecorator);
+}
+
+
+
+
+
+void cMap::EraseData(void)
+{
+ m_Data.assign(m_Width * m_Height, 0);
+}
+
+
+
+
+
+eDimension cMap::GetDimension(void) const
+{
+ ASSERT(m_World != NULL);
+ return m_World->GetDimension();
+}
+
+
+
+
+
+
+void cMap::Resize(unsigned int a_Width, unsigned int a_Height)
+{
+ if ((m_Width == a_Width) && (m_Height == a_Height))
+ {
+ return;
+ }
+
+ m_Width = a_Width;
+ m_Height = a_Height;
+
+ m_Data.assign(m_Width * m_Height, 0);
+}
+
+
+
+
+
+void cMap::SetPosition(int a_CenterX, int a_CenterZ)
+{
+ if ((m_CenterX == a_CenterX) && (m_CenterZ == a_CenterZ))
+ {
+ return;
+ }
+
+ m_CenterX = a_CenterX;
+ m_CenterZ = a_CenterZ;
+}
+
+
+
+
+
+void cMap::SetScale(unsigned int a_Scale)
+{
+ if (m_Scale == a_Scale)
+ {
+ return;
+ }
+
+ m_Scale = a_Scale;
+
+ for (cMapClientList::iterator it = m_Clients.begin(); it != m_Clients.end(); ++it)
+ {
+ it->m_SendInfo = true;
+ }
+}
+
+
+
+
+
+void cMap::SendTo(cClientHandle & a_Client)
+{
+ a_Client.SendMapInfo(m_ID, m_Scale);
+
+ for (unsigned int i = 0; i < m_Width; ++i)
+ {
+ const Byte* Colors = &m_Data[i * m_Height];
+
+ a_Client.SendMapColumn(m_ID, i, 0, Colors, m_Height);
+ }
+
+ a_Client.SendMapDecorators(m_ID, m_Decorators);
+}
+
+
+
+
+
+unsigned int cMap::GetNumPixels(void) const
+{
+ return m_Width * m_Height;
+}
+
+
+
+
+
+unsigned int cMap::GetPixelWidth(void) const
+{
+ return pow(2, m_Scale);
+}
+
+
+
+
+
diff --git a/src/Map.h b/src/Map.h
new file mode 100644
index 000000000..3cf9977ab
--- /dev/null
+++ b/src/Map.h
@@ -0,0 +1,209 @@
+
+// Map.h
+
+// Implementation of in-game coloured maps
+
+
+
+
+
+#pragma once
+
+
+
+
+
+#include "BlockID.h"
+
+
+
+
+
+class cClientHandle;
+class cWorld;
+class cPlayer;
+class cMap;
+
+
+
+
+
+/** Encapsulates a map decorator. */
+class cMapDecorator
+{
+public:
+
+ enum eType
+ {
+ E_TYPE_PLAYER = 0x00,
+ E_TYPE_ITEM_FRAME = 0x01,
+
+ /** Player outside of the boundaries of the map. */
+ E_TYPE_PLAYER_OUTSIDE = 0x06
+ };
+
+
+public:
+
+ /** Constructs a map decorator fixed at the specified pixel coordinates. (DEBUG) */
+ cMapDecorator(cMap * a_Map, eType a_Type, int a_X, int a_Z, int a_Rot);
+
+ /** Constructs a map decorator that tracks a player. */
+ cMapDecorator(cMap * a_Map, cPlayer * a_Player);
+
+ /** Updates the decorator. */
+ void Update(void);
+
+ unsigned int GetPixelX(void) const { return m_PixelX; }
+ unsigned int GetPixelZ(void) const { return m_PixelZ; }
+
+ int GetRot(void) const { return m_Rot; }
+
+ eType GetType(void) const { return m_Type; }
+
+ cPlayer * GetPlayer(void) { return m_Player; }
+
+
+protected:
+
+ cMap * m_Map;
+
+ eType m_Type;
+
+ unsigned int m_PixelX;
+ unsigned int m_PixelZ;
+
+ unsigned int m_Rot;
+
+ cPlayer * m_Player;
+
+};
+
+typedef std::list<cMapDecorator> cMapDecoratorList;
+
+
+
+
+
+// tolua_begin
+
+/** Encapsulates an in-game world map. */
+class cMap
+{
+public:
+
+ typedef Byte ColorID;
+
+ // tolua_end
+
+ typedef std::vector<ColorID> cColorList;
+
+ /** Encapsulates the state of a map client. */
+ struct cMapClient
+ {
+ cClientHandle * m_Handle;
+
+ /** Whether the map scale was modified and needs to be resent. */
+ bool m_SendInfo;
+
+ /** Ticks since last decorator update. */
+ unsigned int m_NextDecoratorUpdate;
+
+ /** Number of pixel data updates. */
+ Int64 m_DataUpdate;
+
+ Int64 m_LastUpdate;
+ };
+
+ typedef std::list<cMapClient> cMapClientList;
+
+
+public:
+
+ /** Construct an empty map. */
+ cMap(unsigned int a_ID, cWorld * a_World);
+
+ /** Construct an empty map at the specified coordinates. */
+ cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, unsigned int a_Scale = 3);
+
+ /** Send this map to the specified client. WARNING: Slow */
+ void SendTo(cClientHandle & a_Client);
+
+ /** Update a circular region with the specified radius and center (in pixels). */
+ void UpdateRadius(int a_PixelX, int a_PixelZ, unsigned int a_Radius);
+
+ /** Update a circular region around the specified player. */
+ void UpdateRadius(cPlayer & a_Player, unsigned int a_Radius);
+
+ /** Send next update packet to the specified player and remove invalid decorators/clients. */
+ void UpdateClient(cPlayer * a_Player);
+
+ // tolua_begin
+
+ /** Erase pixel data */
+ void EraseData(void);
+
+ void Resize(unsigned int a_Width, unsigned int a_Height);
+
+ void SetPosition(int a_CenterX, int a_CenterZ);
+
+ void SetScale(unsigned int a_Scale);
+
+ unsigned int GetWidth (void) const { return m_Width; }
+ unsigned int GetHeight(void) const { return m_Height; }
+
+ unsigned int GetScale(void) const { return m_Scale; }
+
+ int GetCenterX(void) const { return m_CenterX; }
+ int GetCenterZ(void) const { return m_CenterZ; }
+
+ unsigned int GetID(void) const { return m_ID; }
+
+ cWorld * GetWorld(void) { return m_World; }
+
+ AString GetName(void) { return m_Name; }
+
+ eDimension GetDimension(void) const;
+
+ const cColorList & GetData(void) const { return m_Data; }
+
+ unsigned int GetNumPixels(void) const;
+
+ unsigned int GetPixelWidth(void) const;
+
+ // tolua_end
+
+
+private:
+
+ /** Update the associated decorators. */
+ void UpdateDecorators(void);
+
+ /** Update the specified pixel. */
+ bool UpdatePixel(unsigned int a_X, unsigned int a_Z);
+
+ unsigned int m_ID;
+
+ unsigned int m_Width;
+ unsigned int m_Height;
+
+ /** The zoom level, 2^scale square blocks per pixel */
+ unsigned int m_Scale;
+
+ int m_CenterX;
+ int m_CenterZ;
+
+ /** Column-major array of colours */
+ cColorList m_Data;
+
+ cWorld * m_World;
+
+ cMapDecoratorList m_Decorators;
+
+ cMapClientList m_Clients;
+
+ AString m_Name;
+
+ friend class cMapSerializer;
+
+};
diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h
index 46b627254..b5560f7c1 100644
--- a/src/Protocol/Protocol.h
+++ b/src/Protocol/Protocol.h
@@ -13,6 +13,7 @@
#include "../Defines.h"
#include "../Endianness.h"
#include "../Scoreboard.h"
+#include "../Map.h"
@@ -82,6 +83,9 @@ public:
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) = 0;
virtual void SendKeepAlive (int a_PingID) = 0;
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) = 0;
+ virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) = 0;
+ virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) = 0;
+ virtual void SendMapInfo (int a_ID, unsigned int a_Scale) = 0;
virtual void SendPaintingSpawn (const cPainting & a_Painting) = 0;
virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0;
virtual void SendPlayerAbilities (void) = 0;
diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp
index 7020699d1..3980350f5 100644
--- a/src/Protocol/Protocol125.cpp
+++ b/src/Protocol/Protocol125.cpp
@@ -97,6 +97,7 @@ enum
PACKET_WINDOW_PROPERTY = 0x69,
PACKET_CREATIVE_INVENTORY_ACTION = 0x6B,
PACKET_UPDATE_SIGN = 0x82,
+ PACKET_ITEM_DATA = 0x83,
PACKET_PLAYER_LIST_ITEM = 0xC9,
PACKET_PLAYER_ABILITIES = 0xca,
PACKET_PLUGIN_MESSAGE = 0xfa,
@@ -614,6 +615,57 @@ void cProtocol125::SendLogin(const cPlayer & a_Player, const cWorld & a_World)
+void cProtocol125::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length)
+{
+ cCSLock Lock(m_CSPacket);
+
+ WriteByte (PACKET_ITEM_DATA);
+ WriteShort(E_ITEM_MAP);
+ WriteShort(a_ID);
+ WriteShort(3 + a_Length);
+
+ WriteByte(0);
+ WriteByte(a_X);
+ WriteByte(a_Y);
+
+ for (unsigned int i = 0; i < a_Length; ++i)
+ {
+ WriteByte(a_Colors[i]);
+ }
+
+ Flush();
+}
+
+
+
+
+
+void cProtocol125::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators)
+{
+ cCSLock Lock(m_CSPacket);
+
+ WriteByte (PACKET_ITEM_DATA);
+ WriteShort(E_ITEM_MAP);
+ WriteShort(a_ID);
+ WriteShort(1 + (3 * a_Decorators.size()));
+
+ WriteByte(1);
+
+ for (cMapDecoratorList::const_iterator it = a_Decorators.begin(); it != a_Decorators.end(); ++it)
+ {
+ WriteByte((it->GetType() << 4) | (it->GetRot() & 0xf));
+ WriteByte(it->GetPixelX());
+ WriteByte(it->GetPixelZ());
+ }
+
+ Flush();
+}
+
+
+
+
+
+
void cProtocol125::SendPickupSpawn(const cPickup & a_Pickup)
{
cCSLock Lock(m_CSPacket);
diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h
index 54551ea5f..1d1484a60 100644
--- a/src/Protocol/Protocol125.h
+++ b/src/Protocol/Protocol125.h
@@ -55,6 +55,9 @@ public:
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
virtual void SendKeepAlive (int a_PingID) override;
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override;
+ virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override;
+ virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override {} // This protocol doesn't support such message
virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override;
virtual void SendPaintingSpawn (const cPainting & a_Painting) override {};
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp
index aaf8830cd..992023464 100644
--- a/src/Protocol/Protocol17x.cpp
+++ b/src/Protocol/Protocol17x.cpp
@@ -587,6 +587,61 @@ void cProtocol172::SendPaintingSpawn(const cPainting & a_Painting)
+void cProtocol172::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length)
+{
+ cPacketizer Pkt(*this, 0x34);
+ Pkt.WriteVarInt(a_ID);
+ Pkt.WriteShort (3 + a_Length);
+
+ Pkt.WriteByte(0);
+ Pkt.WriteByte(a_X);
+ Pkt.WriteByte(a_Y);
+
+ for (unsigned int i = 0; i < a_Length; ++i)
+ {
+ Pkt.WriteByte(a_Colors[i]);
+ }
+}
+
+
+
+
+
+void cProtocol172::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators)
+{
+ cPacketizer Pkt(*this, 0x34);
+ Pkt.WriteVarInt(a_ID);
+ Pkt.WriteShort (1 + (3 * a_Decorators.size()));
+
+ Pkt.WriteByte(1);
+
+ for (cMapDecoratorList::const_iterator it = a_Decorators.begin(); it != a_Decorators.end(); ++it)
+ {
+ Pkt.WriteByte((it->GetType() << 4) | (it->GetRot() & 0xf));
+ Pkt.WriteByte(it->GetPixelX());
+ Pkt.WriteByte(it->GetPixelZ());
+ }
+}
+
+
+
+
+
+void cProtocol172::SendMapInfo(int a_ID, unsigned int a_Scale)
+{
+ cPacketizer Pkt(*this, 0x34);
+ Pkt.WriteVarInt(a_ID);
+ Pkt.WriteShort (2);
+
+ Pkt.WriteByte(2);
+ Pkt.WriteByte(a_Scale);
+}
+
+
+
+
+
+
void cProtocol172::SendPickupSpawn(const cPickup & a_Pickup)
{
{
diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h
index ae3577867..113501568 100644
--- a/src/Protocol/Protocol17x.h
+++ b/src/Protocol/Protocol17x.h
@@ -87,6 +87,9 @@ public:
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
virtual void SendKeepAlive (int a_PingID) override;
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override;
+ virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override;
+ virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override;
virtual void SendPaintingSpawn (const cPainting & a_Painting) override;
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
virtual void SendPlayerAbilities (void) override;
diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp
index b658dc9db..84b052146 100644
--- a/src/Protocol/ProtocolRecognizer.cpp
+++ b/src/Protocol/ProtocolRecognizer.cpp
@@ -396,6 +396,36 @@ void cProtocolRecognizer::SendLogin(const cPlayer & a_Player, const cWorld & a_W
+void cProtocolRecognizer::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendMapColumn(a_ID, a_X, a_Y, a_Colors, a_Length);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendMapDecorators(int a_ID, const cMapDecoratorList & a_Decorators)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendMapDecorators(a_ID, a_Decorators);
+}
+
+
+
+
+
+void cProtocolRecognizer::SendMapInfo(int a_ID, unsigned int a_Scale)
+{
+ ASSERT(m_Protocol != NULL);
+ m_Protocol->SendMapInfo(a_ID, a_Scale);
+}
+
+
+
+
+
void cProtocolRecognizer::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount)
{
ASSERT(m_Protocol != NULL);
diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h
index abbb22827..6aaafedeb 100644
--- a/src/Protocol/ProtocolRecognizer.h
+++ b/src/Protocol/ProtocolRecognizer.h
@@ -90,6 +90,9 @@ public:
virtual void SendInventorySlot (char a_WindowID, short a_SlotNum, const cItem & a_Item) override;
virtual void SendKeepAlive (int a_PingID) override;
virtual void SendLogin (const cPlayer & a_Player, const cWorld & a_World) override;
+ virtual void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length) override;
+ virtual void SendMapDecorators (int a_ID, const cMapDecoratorList & a_Decorators) override;
+ virtual void SendMapInfo (int a_ID, unsigned int a_Scale) override;
virtual void SendParticleEffect (const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmmount) override;
virtual void SendPaintingSpawn (const cPainting & a_Painting) override;
virtual void SendPickupSpawn (const cPickup & a_Pickup) override;
diff --git a/src/World.cpp b/src/World.cpp
index 7d8bdd95f..c1e0731c1 100644
--- a/src/World.cpp
+++ b/src/World.cpp
@@ -11,7 +11,9 @@
#include "ChunkMap.h"
#include "Generating/ChunkDesc.h"
#include "OSSupport/Timer.h"
+
#include "WorldStorage/ScoreboardSerializer.h"
+#include "WorldStorage/MapSerializer.h"
// Entities (except mobs):
#include "Entities/ExpOrb.h"
@@ -261,6 +263,8 @@ cWorld::cWorld(const AString & a_WorldName) :
// Load the scoreboard
cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard);
Serializer.Load();
+
+ LoadMapData();
}
@@ -284,6 +288,8 @@ cWorld::~cWorld()
cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard);
Serializer.Save();
+ SaveMapData();
+
delete m_ChunkMap;
}
@@ -3023,6 +3029,7 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c
+
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// cWorld::cTaskSaveAllChunks:
diff --git a/src/World.h b/src/World.h
index 5c18c5d23..92fc66c8c 100644
--- a/src/World.h
+++ b/src/World.h
@@ -24,6 +24,7 @@
#include "Entities/ProjectileEntity.h"
#include "ForEachChunkProvider.h"
#include "Scoreboard.h"
+#include "Map.h"
#include "Blocks/WorldInterface.h"
#include "Blocks/BroadcastInterface.h"
diff --git a/src/WorldStorage/MapSerializer.cpp b/src/WorldStorage/MapSerializer.cpp
new file mode 100644
index 000000000..0bbe71a60
--- /dev/null
+++ b/src/WorldStorage/MapSerializer.cpp
@@ -0,0 +1,276 @@
+
+// MapSerializer.cpp
+
+
+#include "Globals.h"
+#include "MapSerializer.h"
+#include "../StringCompression.h"
+#include "zlib/zlib.h"
+#include "FastNBT.h"
+
+#include "../Map.h"
+#include "../World.h"
+
+
+
+
+
+cMapSerializer::cMapSerializer(const AString& a_WorldName, cMap * a_Map)
+ : m_Map(a_Map)
+{
+ AString DataPath;
+ Printf(DataPath, "%s/data", a_WorldName.c_str());
+
+ Printf(m_Path, "%s/map_%i.dat", DataPath.c_str(), a_Map->GetID());
+
+ cFile::CreateFolder(FILE_IO_PREFIX + DataPath);
+}
+
+
+
+
+
+bool cMapSerializer::Load(void)
+{
+ AString Data = cFile::ReadWholeFile(FILE_IO_PREFIX + m_Path);
+ if (Data.empty())
+ {
+ return false;
+ }
+
+ AString Uncompressed;
+ int res = UncompressStringGZIP(Data.data(), Data.size(), Uncompressed);
+
+ if (res != Z_OK)
+ {
+ return false;
+ }
+
+ // Parse the NBT data:
+ cParsedNBT NBT(Uncompressed.data(), Uncompressed.size());
+ if (!NBT.IsValid())
+ {
+ // NBT Parsing failed
+ return false;
+ }
+
+ return LoadMapFromNBT(NBT);
+}
+
+
+
+
+
+bool cMapSerializer::Save(void)
+{
+ cFastNBTWriter Writer;
+
+ SaveMapToNBT(Writer);
+
+ Writer.Finish();
+
+ #ifdef _DEBUG
+ cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size());
+ ASSERT(TestParse.IsValid());
+ #endif // _DEBUG
+
+ cFile File;
+ if (!File.Open(FILE_IO_PREFIX + m_Path, cFile::fmWrite))
+ {
+ return false;
+ }
+
+ AString Compressed;
+ int res = CompressStringGZIP(Writer.GetResult().data(), Writer.GetResult().size(), Compressed);
+
+ if (res != Z_OK)
+ {
+ return false;
+ }
+
+ File.Write(Compressed.data(), Compressed.size());
+ File.Close();
+
+ return true;
+}
+
+
+
+
+
+void cMapSerializer::SaveMapToNBT(cFastNBTWriter & a_Writer)
+{
+ a_Writer.BeginCompound("data");
+
+ a_Writer.AddByte("scale", m_Map->GetScale());
+ a_Writer.AddByte("dimension", (int) m_Map->GetDimension());
+
+ a_Writer.AddShort("width", m_Map->GetWidth());
+ a_Writer.AddShort("height", m_Map->GetHeight());
+
+ a_Writer.AddInt("xCenter", m_Map->GetCenterX());
+ a_Writer.AddInt("zCenter", m_Map->GetCenterZ());
+
+ const cMap::cColorList & Data = m_Map->GetData();
+ a_Writer.AddByteArray("colors", (char *) Data.data(), Data.size());
+
+ a_Writer.EndCompound();
+}
+
+
+
+
+
+bool cMapSerializer::LoadMapFromNBT(const cParsedNBT & a_NBT)
+{
+ int Data = a_NBT.FindChildByName(0, "data");
+ if (Data < 0)
+ {
+ return false;
+ }
+
+ int CurrLine = a_NBT.FindChildByName(Data, "scale");
+ if (CurrLine >= 0)
+ {
+ unsigned int Scale = a_NBT.GetByte(CurrLine);
+ m_Map->SetScale(Scale);
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "dimension");
+ if (CurrLine >= 0)
+ {
+ eDimension Dimension = (eDimension) a_NBT.GetByte(CurrLine);
+
+ ASSERT(Dimension == m_Map->m_World->GetDimension());
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "width");
+ if (CurrLine >= 0)
+ {
+ unsigned int Width = a_NBT.GetShort(CurrLine);
+ m_Map->m_Width = Width;
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "height");
+ if (CurrLine >= 0)
+ {
+ unsigned int Height = a_NBT.GetShort(CurrLine);
+ m_Map->m_Height = Height;
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "xCenter");
+ if (CurrLine >= 0)
+ {
+ int CenterX = a_NBT.GetInt(CurrLine);
+ m_Map->m_CenterX = CenterX;
+ }
+
+ CurrLine = a_NBT.FindChildByName(Data, "zCenter");
+ if (CurrLine >= 0)
+ {
+ int CenterZ = a_NBT.GetInt(CurrLine);
+ m_Map->m_CenterZ = CenterZ;
+ }
+
+ unsigned int NumPixels = m_Map->GetNumPixels();
+ m_Map->m_Data.resize(NumPixels);
+
+ CurrLine = a_NBT.FindChildByName(Data, "colors");
+ if ((CurrLine >= 0) && (a_NBT.GetType(CurrLine) == TAG_ByteArray))
+ {
+ memcpy(m_Map->m_Data.data(), a_NBT.GetData(CurrLine), NumPixels);
+ }
+
+ return true;
+}
+
+
+
+
+
+cIDCountSerializer::cIDCountSerializer(const AString & a_WorldName) : m_MapCount(0)
+{
+ AString DataPath;
+ Printf(DataPath, "%s/data", a_WorldName.c_str());
+
+ Printf(m_Path, "%s/idcounts.dat", DataPath.c_str());
+
+ cFile::CreateFolder(FILE_IO_PREFIX + DataPath);
+}
+
+
+
+
+
+bool cIDCountSerializer::Load(void)
+{
+ AString Data = cFile::ReadWholeFile(FILE_IO_PREFIX + m_Path);
+ if (Data.empty())
+ {
+ return false;
+ }
+
+ // NOTE: idcounts.dat is not compressed (raw format)
+
+ // Parse the NBT data:
+ cParsedNBT NBT(Data.data(), Data.size());
+ if (!NBT.IsValid())
+ {
+ // NBT Parsing failed
+ return false;
+ }
+
+ int CurrLine = NBT.FindChildByName(0, "map");
+ if (CurrLine >= 0)
+ {
+ m_MapCount = (int)NBT.GetShort(CurrLine) + 1;
+ }
+ else
+ {
+ m_MapCount = 0;
+ }
+
+ return true;
+}
+
+
+
+
+
+bool cIDCountSerializer::Save(void)
+{
+ cFastNBTWriter Writer;
+
+ if (m_MapCount > 0)
+ {
+ Writer.AddShort("map", m_MapCount - 1);
+ }
+
+ Writer.Finish();
+
+ #ifdef _DEBUG
+ cParsedNBT TestParse(Writer.GetResult().data(), Writer.GetResult().size());
+ ASSERT(TestParse.IsValid());
+ #endif // _DEBUG
+
+ cFile File;
+ if (!File.Open(FILE_IO_PREFIX + m_Path, cFile::fmWrite))
+ {
+ return false;
+ }
+
+ // NOTE: idcounts.dat is not compressed (raw format)
+
+ File.Write(Writer.GetResult().data(), Writer.GetResult().size());
+ File.Close();
+
+ return true;
+}
+
+
+
+
+
+
+
+
diff --git a/src/WorldStorage/MapSerializer.h b/src/WorldStorage/MapSerializer.h
new file mode 100644
index 000000000..85fe917f5
--- /dev/null
+++ b/src/WorldStorage/MapSerializer.h
@@ -0,0 +1,82 @@
+
+// MapSerializer.h
+
+// Declares the cMapSerializer class that is used for saving maps into NBT format used by Anvil
+
+
+
+
+
+#pragma once
+
+
+
+
+
+// fwd:
+class cFastNBTWriter;
+class cParsedNBT;
+class cMap;
+
+
+
+
+/** Utility class used to serialize maps. */
+class cMapSerializer
+{
+public:
+
+ cMapSerializer(const AString& a_WorldName, cMap * a_Map);
+
+ /** Try to load the scoreboard */
+ bool Load(void);
+
+ /** Try to save the scoreboard */
+ bool Save(void);
+
+
+private:
+
+ void SaveMapToNBT(cFastNBTWriter & a_Writer);
+
+ bool LoadMapFromNBT(const cParsedNBT & a_NBT);
+
+ cMap * m_Map;
+
+ AString m_Path;
+
+
+} ;
+
+
+
+
+/** Utility class used to serialize item ID counts. */
+class cIDCountSerializer
+{
+public:
+
+ cIDCountSerializer(const AString & a_WorldName);
+
+ /** Try to load the ID counts */
+ bool Load(void);
+
+ /** Try to save the ID counts */
+ bool Save(void);
+
+ inline unsigned int GetMapCount(void) const { return m_MapCount; }
+
+ inline void SetMapCount(unsigned int a_MapCount) { m_MapCount = a_MapCount; }
+
+
+private:
+
+ AString m_Path;
+
+ unsigned int m_MapCount;
+
+};
+
+
+
+