diff options
-rw-r--r-- | MCServer/crafting.txt | 2 | ||||
-rw-r--r-- | src/Bindings/AllToLua.pkg | 1 | ||||
-rw-r--r-- | src/ClientHandle.cpp | 27 | ||||
-rw-r--r-- | src/ClientHandle.h | 4 | ||||
-rw-r--r-- | src/Entities/Player.cpp | 3 | ||||
-rw-r--r-- | src/Globals.h | 10 | ||||
-rw-r--r-- | src/Inventory.cpp | 25 | ||||
-rw-r--r-- | src/Inventory.h | 3 | ||||
-rw-r--r-- | src/Items/ItemEmptyMap.h | 62 | ||||
-rw-r--r-- | src/Items/ItemHandler.cpp | 4 | ||||
-rw-r--r-- | src/Items/ItemHandler.h | 8 | ||||
-rw-r--r-- | src/Items/ItemMap.h | 43 | ||||
-rw-r--r-- | src/Map.cpp | 494 | ||||
-rw-r--r-- | src/Map.h | 209 | ||||
-rw-r--r-- | src/Protocol/Protocol.h | 4 | ||||
-rw-r--r-- | src/Protocol/Protocol125.cpp | 52 | ||||
-rw-r--r-- | src/Protocol/Protocol125.h | 3 | ||||
-rw-r--r-- | src/Protocol/Protocol17x.cpp | 55 | ||||
-rw-r--r-- | src/Protocol/Protocol17x.h | 3 | ||||
-rw-r--r-- | src/Protocol/ProtocolRecognizer.cpp | 30 | ||||
-rw-r--r-- | src/Protocol/ProtocolRecognizer.h | 3 | ||||
-rw-r--r-- | src/World.cpp | 7 | ||||
-rw-r--r-- | src/World.h | 1 | ||||
-rw-r--r-- | src/WorldStorage/MapSerializer.cpp | 276 | ||||
-rw-r--r-- | src/WorldStorage/MapSerializer.h | 82 |
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; + +}; + + + + |