From 92e85cc96030285bba74837759925866c1be7235 Mon Sep 17 00:00:00 2001 From: andrew Date: Thu, 13 Feb 2014 17:13:09 +0200 Subject: [PATCH 01/16] Implementation of in-game maps --- src/ClientHandle.cpp | 18 +++ src/ClientHandle.h | 2 + src/Map.cpp | 149 ++++++++++++++++++++++ src/Map.h | 95 ++++++++++++++ src/Protocol/Protocol.h | 3 + src/Protocol/Protocol125.cpp | 27 ++++ src/Protocol/Protocol125.h | 2 + src/Protocol/Protocol17x.cpp | 35 ++++++ src/Protocol/Protocol17x.h | 2 + src/Protocol/ProtocolRecognizer.cpp | 20 +++ src/Protocol/ProtocolRecognizer.h | 2 + src/WorldStorage/MapSerializer.cpp | 189 ++++++++++++++++++++++++++++ src/WorldStorage/MapSerializer.h | 52 ++++++++ 13 files changed, 596 insertions(+) create mode 100644 src/Map.cpp create mode 100644 src/Map.h create mode 100644 src/WorldStorage/MapSerializer.cpp create mode 100644 src/WorldStorage/MapSerializer.h diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 1b3ebc3d4..8e44a61fd 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -2057,6 +2057,24 @@ 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::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 d9a86d983..b1f13954b 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -109,6 +109,8 @@ 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) override; + void SendMapInfo (int a_ID, unsigned int a_Scale) override; void SendPickupSpawn (const cPickup & a_Pickup); void SendEntityAnimation (const cEntity & a_Entity, char a_Animation); 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); diff --git a/src/Map.cpp b/src/Map.cpp new file mode 100644 index 000000000..f1b690698 --- /dev/null +++ b/src/Map.cpp @@ -0,0 +1,149 @@ + +// Map.cpp + +#include "Globals.h" + +#include "Map.h" + +#include "ClientHandle.h" +#include "World.h" + + + + + +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(128) + , m_Height(128) + , m_Scale(a_Scale) + , m_CenterX(a_CenterX) + , m_CenterZ(a_CenterZ) + , m_World(a_World) +{ + m_Data.assign(m_Width * m_Height, 0); + + UpdateMap(); +} + + + + + +void cMap::UpdateMap(void) +{ + // ASSERT(m_World != NULL); + + // TODO + + for (unsigned int X = 0; X < m_Width; ++X) + { + for (unsigned int Y = 0; Y < m_Height; ++Y) + { + // Debug + m_Data[Y + X * m_Height] = rand() % 100; + } + } +} + + + + + +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); + + UpdateMap(); +} + + + + + +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; + + UpdateMap(); +} + + + + + +void cMap::SetScale(unsigned int a_Scale) +{ + if (m_Scale == a_Scale) + { + return; + } + + m_Scale = a_Scale; + + UpdateMap(); +} + + + + + +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); + } +} + + + + + +unsigned int cMap::GetNumPixels(void) const +{ + return m_Width * m_Height; +} + + + + + +unsigned int cMap::GetNumBlocksPerPixel(void) const +{ + return pow(2, m_Scale); +} + + + + + diff --git a/src/Map.h b/src/Map.h new file mode 100644 index 000000000..80bbd4ab4 --- /dev/null +++ b/src/Map.h @@ -0,0 +1,95 @@ + +// Map.h + +// Implementation of in-game coloured maps + + + + + +#pragma once + + + + + +#include "BlockID.h" + + + + + +class cClientHandle; +class cWorld; + + + + + +class cMap +{ +public: + + typedef Byte ColorID; + + typedef std::vector cColorList; + + +public: + + cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, unsigned int a_Scale = 3); + + /** Update the map (Query the world) */ + void UpdateMap(void); + + /** Send this map to the specified client. */ + void SendTo(cClientHandle & a_Client); + + 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; } + + eDimension GetDimension(void) const; + + const cColorList & GetData(void) const { return m_Data; } + + unsigned int GetNumPixels(void) const; + + unsigned int GetNumBlocksPerPixel(void) const; + + +private: + + 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; + + friend class cMapSerializer; + +}; diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 791082537..5f89799e1 100644 --- a/src/Protocol/Protocol.h +++ b/src/Protocol/Protocol.h @@ -28,6 +28,7 @@ class cWorld; class cMonster; class cChunkDataSerializer; class cFallingBlock; +class cMap; @@ -79,6 +80,8 @@ 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 SendMapInfo (int a_ID, unsigned int a_Scale) = 0; virtual void SendPickupSpawn (const cPickup & a_Pickup) = 0; virtual void SendPlayerAbilities (void) = 0; virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) = 0; diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp index 73d21161c..edb22fae6 100644 --- a/src/Protocol/Protocol125.cpp +++ b/src/Protocol/Protocol125.cpp @@ -95,6 +95,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, @@ -576,6 +577,32 @@ 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::SendPickupSpawn(const cPickup & a_Pickup) { cCSLock Lock(m_CSPacket); diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h index cd15ab518..467aee002 100644 --- a/src/Protocol/Protocol125.h +++ b/src/Protocol/Protocol125.h @@ -54,6 +54,8 @@ 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 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 SendPickupSpawn (const cPickup & a_Pickup) override; virtual void SendPlayerAbilities (void) override {} // This protocol doesn't support such message diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 7eaf106cf..4acc61586 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -496,6 +496,41 @@ void cProtocol172::SendLogin(const cPlayer & a_Player, const cWorld & a_World) +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::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 6a75e41c8..0e50db45d 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -76,6 +76,8 @@ 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 SendMapInfo (int a_ID, unsigned int a_Scale) override; virtual void SendPickupSpawn (const cPickup & a_Pickup) override; virtual void SendPlayerAbilities (void) override; virtual void SendEntityAnimation (const cEntity & a_Entity, char a_Animation) override; diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 32409c2aa..447fa516b 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -386,6 +386,26 @@ 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::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 f58c66d10..3c37d5138 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -89,6 +89,8 @@ 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 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 SendPickupSpawn (const cPickup & a_Pickup) override; virtual void SendPlayerAbilities (void) override; diff --git a/src/WorldStorage/MapSerializer.cpp b/src/WorldStorage/MapSerializer.cpp new file mode 100644 index 000000000..ea0d3ec47 --- /dev/null +++ b/src/WorldStorage/MapSerializer.cpp @@ -0,0 +1,189 @@ + +// MapSerializer.cpp + + +#include "Globals.h" +#include "MapSerializer.h" +#include "../StringCompression.h" +#include "zlib/zlib.h" +#include "FastNBT.h" + +#include "../Map.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()); + + // Potential bug - The internal representation may change + 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->m_Scale = Scale; + } + + CurrLine = a_NBT.FindChildByName(Data, "dimension"); + if (CurrLine >= 0) + { + eDimension Dimension = (eDimension) a_NBT.GetByte(CurrLine); + + // ASSERT(Dimension == 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); + + // TODO xdot: Parse the byte array. + + return true; +} + + + + + + + + diff --git a/src/WorldStorage/MapSerializer.h b/src/WorldStorage/MapSerializer.h new file mode 100644 index 000000000..71791a2fb --- /dev/null +++ b/src/WorldStorage/MapSerializer.h @@ -0,0 +1,52 @@ + +// 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; + + + + +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; + + +} ; + + + + From 32b465b8e1e1a6fa9e966a1376209f292331d4ae Mon Sep 17 00:00:00 2001 From: andrew Date: Thu, 13 Feb 2014 21:36:24 +0200 Subject: [PATCH 02/16] IDCount Serialization --- src/ClientHandle.h | 4 +- src/Map.cpp | 18 +++++++ src/Map.h | 3 ++ src/World.cpp | 54 ++++++++++++++++++++ src/World.h | 11 +++++ src/WorldStorage/MapSerializer.cpp | 79 +++++++++++++++++++++++++++++- src/WorldStorage/MapSerializer.h | 25 ++++++++++ 7 files changed, 191 insertions(+), 3 deletions(-) diff --git a/src/ClientHandle.h b/src/ClientHandle.h index b1f13954b..a714cf8b9 100644 --- a/src/ClientHandle.h +++ b/src/ClientHandle.h @@ -109,8 +109,8 @@ 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) override; - void SendMapInfo (int a_ID, unsigned int a_Scale) override; + void SendMapColumn (int a_ID, int a_X, int a_Y, const Byte * a_Colors, unsigned int a_Length); + void SendMapInfo (int a_ID, unsigned int a_Scale); void SendPickupSpawn (const cPickup & a_Pickup); void SendEntityAnimation (const cEntity & a_Entity, char a_Animation); 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); diff --git a/src/Map.cpp b/src/Map.cpp index f1b690698..f99b01752 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -12,6 +12,24 @@ +cMap::cMap(unsigned int a_ID, cWorld * a_World) + : m_ID(a_ID) + , m_Width(128) + , m_Height(128) + , m_Scale(3) + , m_CenterX(0) + , m_CenterZ(0) + , m_World(a_World) +{ + m_Data.assign(m_Width * m_Height, 0); + + // Do not update map +} + + + + + 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(128) diff --git a/src/Map.h b/src/Map.h index 80bbd4ab4..dbb15afdd 100644 --- a/src/Map.h +++ b/src/Map.h @@ -37,6 +37,9 @@ public: public: + /// Construct an empty map + cMap(unsigned int a_ID, cWorld * a_World); + cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, unsigned int a_Scale = 3); /** Update the map (Query the world) */ diff --git a/src/World.cpp b/src/World.cpp index f8c1091f0..a308778df 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; } @@ -2945,6 +2951,54 @@ cFluidSimulator * cWorld::InitializeFluidSimulator(cIniFile & a_IniFile, const c + +void cWorld::LoadMapData(void) +{ + cIDCountSerializer IDSerializer(GetName()); + + IDSerializer.Load(); + + unsigned int MapCount = IDSerializer.GetMapCount(); + + m_MapData.clear(); + + for (unsigned int i = 0; i < MapCount; ++i) + { + cMap Map(i, this); + + cMapSerializer Serializer(GetName(), &Map); + + Serializer.Load(); + + m_MapData.push_back(Map); + } +} + + + + + +void cWorld::SaveMapData(void) +{ + cIDCountSerializer IDSerializer(GetName()); + + IDSerializer.SetMapCount(m_MapData.size()); + + IDSerializer.Save(); + + for (cMapList::iterator it = m_MapData.begin(); it != m_MapData.end(); ++it) + { + cMap & Map = *it; + + cMapSerializer Serializer(GetName(), &Map); + + Serializer.Save(); + } +} + + + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cWorld::cTaskSaveAllChunks: diff --git a/src/World.h b/src/World.h index fa83fe73e..02e56a247 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" @@ -811,6 +812,10 @@ private: cChunkGenerator m_Generator; cScoreboard m_Scoreboard; + + typedef std::vector cMapList; + + cMapList m_MapData; /** The callbacks that the ChunkGenerator uses to store new chunks and interface to plugins */ cChunkGeneratorCallbacks m_GeneratorCallbacks; @@ -876,6 +881,12 @@ private: /** Creates a new redstone simulator.*/ cRedstoneSimulator * InitializeRedstoneSimulator(cIniFile & a_IniFile); + + /** Loads the map data from the disk */ + void LoadMapData(void); + + /** Saves the map data to the disk */ + void SaveMapData(void); }; // tolua_export diff --git a/src/WorldStorage/MapSerializer.cpp b/src/WorldStorage/MapSerializer.cpp index ea0d3ec47..aab4c7816 100644 --- a/src/WorldStorage/MapSerializer.cpp +++ b/src/WorldStorage/MapSerializer.cpp @@ -9,6 +9,7 @@ #include "FastNBT.h" #include "../Map.h" +#include "../World.h" @@ -141,7 +142,7 @@ bool cMapSerializer::LoadMapFromNBT(const cParsedNBT & a_NBT) { eDimension Dimension = (eDimension) a_NBT.GetByte(CurrLine); - // ASSERT(Dimension == m_World.GetDimension()); + ASSERT(Dimension == m_Map->m_World->GetDimension()); } CurrLine = a_NBT.FindChildByName(Data, "width"); @@ -184,6 +185,82 @@ bool cMapSerializer::LoadMapFromNBT(const cParsedNBT & a_NBT) +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); + } + + return true; +} + + + + + +bool cIDCountSerializer::Save(void) +{ + cFastNBTWriter Writer; + + Writer.AddShort("map", m_MapCount); + + 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 index 71791a2fb..d9da107bc 100644 --- a/src/WorldStorage/MapSerializer.h +++ b/src/WorldStorage/MapSerializer.h @@ -50,3 +50,28 @@ private: +class cIDCountSerializer +{ +public: + + cIDCountSerializer(const AString & a_WorldName); + + bool Load(void); + + 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; +}; + + + + From 5b92b877bcc0c5072dbea98b6c54106f954aa758 Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 14 Feb 2014 16:21:16 +0200 Subject: [PATCH 03/16] Send map when selected --- src/Bindings/AllToLua.pkg | 1 + src/ClientHandle.cpp | 13 ++++++++ src/Map.cpp | 53 ++++++++++++++++++------------ src/Map.h | 20 ++++++++--- src/World.cpp | 45 +++++++++++++++++++++++-- src/World.h | 6 ++++ src/WorldStorage/MapSerializer.cpp | 9 +++-- 7 files changed, 117 insertions(+), 30 deletions(-) diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index f65aed9bb..335acff95 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -47,6 +47,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 8e44a61fd..a2cbaefff 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -1190,6 +1190,19 @@ void cClientHandle::HandleSlotSelected(short a_SlotNum) { m_Player->GetInventory().SetEquippedSlotNum(a_SlotNum); m_Player->GetWorld()->BroadcastEntityEquipment(*m_Player, 0, m_Player->GetInventory().GetEquippedItem(), this); + + const cItem & Item = m_Player->GetInventory().GetEquippedItem(); + if (Item.m_ItemType == E_ITEM_MAP) + { + // TODO 2014-02-14 xdot: Do not hardcode this. + cMap * Map = m_Player->GetWorld()->GetMapData(Item.m_ItemDamage); + + if (Map != NULL) + { + // TODO 2014-02-14 xdot: Optimization - Do not send the whole map. + Map->SendTo(*this); + } + } } diff --git a/src/Map.cpp b/src/Map.cpp index f99b01752..4acd9512c 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -7,6 +7,7 @@ #include "ClientHandle.h" #include "World.h" +#include "Chunk.h" @@ -22,8 +23,6 @@ cMap::cMap(unsigned int a_ID, cWorld * a_World) , m_World(a_World) { m_Data.assign(m_Width * m_Height, 0); - - // Do not update map } @@ -41,19 +40,6 @@ cMap::cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, un { m_Data.assign(m_Width * m_Height, 0); - UpdateMap(); -} - - - - - -void cMap::UpdateMap(void) -{ - // ASSERT(m_World != NULL); - - // TODO - for (unsigned int X = 0; X < m_Width; ++X) { for (unsigned int Y = 0; Y < m_Height; ++Y) @@ -68,6 +54,37 @@ void cMap::UpdateMap(void) +bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Y) +{ + ASSERT(m_World != NULL); + + cChunk * Chunk = NULL; + + if (Chunk == NULL) + { + return false; + } + + int Height = Chunk->GetHeight(a_X, a_Y); + + // TODO + + return true; +} + + + + + +void cMap::EraseData(void) +{ + m_Data.assign(m_Width * m_Height, 0); +} + + + + + eDimension cMap::GetDimension(void) const { ASSERT(m_World != NULL); @@ -90,8 +107,6 @@ void cMap::Resize(unsigned int a_Width, unsigned int a_Height) m_Height = a_Height; m_Data.assign(m_Width * m_Height, 0); - - UpdateMap(); } @@ -107,8 +122,6 @@ void cMap::SetPosition(int a_CenterX, int a_CenterZ) m_CenterX = a_CenterX; m_CenterZ = a_CenterZ; - - UpdateMap(); } @@ -123,8 +136,6 @@ void cMap::SetScale(unsigned int a_Scale) } m_Scale = a_Scale; - - UpdateMap(); } diff --git a/src/Map.h b/src/Map.h index dbb15afdd..c443445de 100644 --- a/src/Map.h +++ b/src/Map.h @@ -26,28 +26,33 @@ class cWorld; +// tolua_begin class cMap { public: typedef Byte ColorID; + // tolua_end + typedef std::vector cColorList; public: - /// Construct an empty map + /** Construct an empty map. */ cMap(unsigned int a_ID, cWorld * a_World); cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, unsigned int a_Scale = 3); - /** Update the map (Query the world) */ - void UpdateMap(void); - /** Send this map to the specified client. */ void SendTo(cClientHandle & a_Client); + // 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); @@ -74,9 +79,16 @@ public: unsigned int GetNumBlocksPerPixel(void) const; + // tolua_end + private: + /** Update the specified pixel. */ + bool UpdatePixel(unsigned int a_X, unsigned int a_Y); + + void PixelToWorldCoords(unsigned int a_X, unsigned int a_Y, int & a_WorldX, int & a_WorldY); + unsigned int m_ID; unsigned int m_Width; diff --git a/src/World.cpp b/src/World.cpp index a308778df..2a3e53332 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -1554,6 +1554,42 @@ bool cWorld::WriteBlockArea(cBlockArea & a_Area, int a_MinBlockX, int a_MinBlock +cMap * cWorld::GetMapData(unsigned int a_ID) +{ + if (a_ID < m_MapData.size()) + { + return &m_MapData[a_ID]; + } + else + { + return NULL; + } +} + + + + + +cMap * cWorld::CreateMap(int a_CenterX, int a_CenterY, int a_Scale) +{ + if (m_MapData.size() >= 65536) + { + LOGD("cWorld::CreateMap - Too many maps in use"); + + return NULL; + } + + cMap Map(m_MapData.size(), a_CenterX, a_CenterY, this, a_Scale); + + m_MapData.push_back(Map); + + return &m_MapData[Map.GetID()]; +} + + + + + void cWorld::SpawnItemPickups(const cItems & a_Pickups, double a_BlockX, double a_BlockY, double a_BlockZ, double a_FlyAwaySpeed, bool IsPlayerCreated) { MTRand r1; @@ -2958,7 +2994,7 @@ void cWorld::LoadMapData(void) IDSerializer.Load(); - unsigned int MapCount = IDSerializer.GetMapCount(); + unsigned int MapCount = IDSerializer.GetMapCount() + 1; m_MapData.clear(); @@ -2980,9 +3016,14 @@ void cWorld::LoadMapData(void) void cWorld::SaveMapData(void) { + if (m_MapData.empty()) + { + return; + } + cIDCountSerializer IDSerializer(GetName()); - IDSerializer.SetMapCount(m_MapData.size()); + IDSerializer.SetMapCount(m_MapData.size() - 1); IDSerializer.Save(); diff --git a/src/World.h b/src/World.h index 02e56a247..a9b1ca2cb 100644 --- a/src/World.h +++ b/src/World.h @@ -552,6 +552,12 @@ public: bool ShouldUseChatPrefixes(void) const { return m_bUseChatPrefixes; } void SetShouldUseChatPrefixes(bool a_Flag) { m_bUseChatPrefixes = a_Flag; } + + /** Returns the map with the specified ID, NULL if out of range. */ + cMap * GetMapData(unsigned int a_ID); + + /** Creates a new map. Returns NULL on error */ + cMap * CreateMap(int a_CenterX, int a_CenterY, int a_Scale = 3); // tolua_end diff --git a/src/WorldStorage/MapSerializer.cpp b/src/WorldStorage/MapSerializer.cpp index aab4c7816..6dab19d4f 100644 --- a/src/WorldStorage/MapSerializer.cpp +++ b/src/WorldStorage/MapSerializer.cpp @@ -111,7 +111,6 @@ void cMapSerializer::SaveMapToNBT(cFastNBTWriter & a_Writer) a_Writer.AddInt("xCenter", m_Map->GetCenterX()); a_Writer.AddInt("zCenter", m_Map->GetCenterZ()); - // Potential bug - The internal representation may change const cMap::cColorList & Data = m_Map->GetData(); a_Writer.AddByteArray("colors", (char *) Data.data(), Data.size()); @@ -134,7 +133,7 @@ bool cMapSerializer::LoadMapFromNBT(const cParsedNBT & a_NBT) if (CurrLine >= 0) { unsigned int Scale = a_NBT.GetByte(CurrLine); - m_Map->m_Scale = Scale; + m_Map->SetScale(Scale); } CurrLine = a_NBT.FindChildByName(Data, "dimension"); @@ -176,7 +175,11 @@ bool cMapSerializer::LoadMapFromNBT(const cParsedNBT & a_NBT) unsigned int NumPixels = m_Map->GetNumPixels(); m_Map->m_Data.resize(NumPixels); - // TODO xdot: Parse the byte array. + 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; } From c7fb00085854ed76b8b8945968de0505b8fbe8a2 Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 14 Feb 2014 17:38:22 +0200 Subject: [PATCH 04/16] EmptyMap item handler --- MCServer/crafting.txt | 2 +- src/Items/ItemEmptyMap.h | 46 +++++++++++++++++++++++++++++++++++++++ src/Items/ItemHandler.cpp | 2 ++ src/Map.cpp | 8 +++---- 4 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 src/Items/ItemEmptyMap.h 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/Items/ItemEmptyMap.h b/src/Items/ItemEmptyMap.h new file mode 100644 index 000000000..5516033a0 --- /dev/null +++ b/src/Items/ItemEmptyMap.h @@ -0,0 +1,46 @@ + +// ItemEmptyMap.h + + + + + +#pragma once + +#include "../Entities/Entity.h" +#include "../Item.h" + + + + + +class cItemEmptyMapHandler : + public cItemHandler +{ + typedef cItemHandler super; + +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; + + int CenterX = round(a_Player->GetPosX() / (float) RegionWidth) * RegionWidth; + int CenterZ = round(a_Player->GetPosZ() / (float) RegionWidth) * RegionWidth; + + a_World->CreateMap(CenterX, CenterZ, 0); + + return true; + } +} ; diff --git a/src/Items/ItemHandler.cpp b/src/Items/ItemHandler.cpp index 19913ab24..755766d64 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" @@ -100,6 +101,7 @@ 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); diff --git a/src/Map.cpp b/src/Map.cpp index 4acd9512c..497cc9659 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -15,8 +15,8 @@ cMap::cMap(unsigned int a_ID, cWorld * a_World) : m_ID(a_ID) - , m_Width(128) - , m_Height(128) + , m_Width(cChunkDef::Width * 8) + , m_Height(cChunkDef::Width * 8) , m_Scale(3) , m_CenterX(0) , m_CenterZ(0) @@ -31,8 +31,8 @@ cMap::cMap(unsigned int a_ID, cWorld * a_World) 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(128) - , m_Height(128) + , m_Width(cChunkDef::Width * 8) + , m_Height(cChunkDef::Width * 8) , m_Scale(a_Scale) , m_CenterX(a_CenterX) , m_CenterZ(a_CenterZ) From cf96e69716e0ccd0657cf275720bb11b915361c4 Mon Sep 17 00:00:00 2001 From: andrew Date: Sat, 15 Feb 2014 20:06:47 +0200 Subject: [PATCH 05/16] cMap::UpdateRadius --- src/ClientHandle.cpp | 2 + src/Items/ItemEmptyMap.h | 6 ++- src/Map.cpp | 112 +++++++++++++++++++++++++++++++++++---- src/Map.h | 17 ++++-- 4 files changed, 121 insertions(+), 16 deletions(-) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index a2cbaefff..ff8775771 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -1199,6 +1199,8 @@ void cClientHandle::HandleSlotSelected(short a_SlotNum) if (Map != NULL) { + Map->UpdateRadius(*m_Player, 128); // Temporary + // TODO 2014-02-14 xdot: Optimization - Do not send the whole map. Map->SendTo(*this); } diff --git a/src/Items/ItemEmptyMap.h b/src/Items/ItemEmptyMap.h index 5516033a0..24d31151b 100644 --- a/src/Items/ItemEmptyMap.h +++ b/src/Items/ItemEmptyMap.h @@ -18,6 +18,8 @@ class cItemEmptyMapHandler : public cItemHandler { typedef cItemHandler super; + + static const unsigned int DEFAULT_SCALE = 0; public: cItemEmptyMapHandler() : @@ -34,12 +36,12 @@ public: // 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; + 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; - a_World->CreateMap(CenterX, CenterZ, 0); + a_World->CreateMap(CenterX, CenterZ, DEFAULT_SCALE); return true; } diff --git a/src/Map.cpp b/src/Map.cpp index 497cc9659..e0f991693 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -8,6 +8,7 @@ #include "ClientHandle.h" #include "World.h" #include "Chunk.h" +#include "Entities/Player.h" @@ -23,6 +24,8 @@ cMap::cMap(unsigned int a_ID, cWorld * a_World) , m_World(a_World) { m_Data.assign(m_Width * m_Height, 0); + + Printf(m_Name, "map_%i", m_ID); } @@ -40,12 +43,34 @@ cMap::cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, un { m_Data.assign(m_Width * m_Height, 0); - for (unsigned int X = 0; X < m_Width; ++X) + 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 = std::max(a_PixelX - PixelRadius, 0); + unsigned int StartZ = std::max(a_PixelZ - PixelRadius, 0); + + unsigned int EndX = std::min(a_PixelX + PixelRadius, (int)m_Width); + unsigned int EndZ = std::min(a_PixelZ + PixelRadius, (int)m_Height); + + for (unsigned int X = StartX; X < EndX; ++X) { - for (unsigned int Y = 0; Y < m_Height; ++Y) + for (unsigned int Z = StartZ; Z < EndZ; ++Z) { - // Debug - m_Data[Y + X * m_Height] = rand() % 100; + int dX = X - a_PixelX; + int dZ = Z - a_PixelZ; + + if ((dX * dX) + (dZ * dZ) < (PixelRadius * PixelRadius)) + { + UpdatePixel(X, Z); + } } } } @@ -54,20 +79,85 @@ cMap::cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, un +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_Y) { ASSERT(m_World != NULL); - cChunk * Chunk = NULL; + unsigned int PixelWidth = GetPixelWidth(); - if (Chunk == NULL) + 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 { - return false; - } + cMap * m_Map; - int Height = Chunk->GetHeight(a_X, a_Y); + int m_RelX, m_RelZ; - // TODO + 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(0) {} + + 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_Y + (a_X * m_Height)] = CalculatePixelCb.GetPixelData(); return true; } @@ -167,7 +257,7 @@ unsigned int cMap::GetNumPixels(void) const -unsigned int cMap::GetNumBlocksPerPixel(void) const +unsigned int cMap::GetPixelWidth(void) const { return pow(2, m_Scale); } diff --git a/src/Map.h b/src/Map.h index c443445de..4134d53a1 100644 --- a/src/Map.h +++ b/src/Map.h @@ -21,6 +21,7 @@ class cClientHandle; class cWorld; +class cPlayer; @@ -48,6 +49,11 @@ public: /** Send this map to the specified client. */ 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); + + void UpdateRadius(cPlayer & a_Player, unsigned int a_Radius); + // tolua_begin /** Erase pixel data */ @@ -71,13 +77,15 @@ public: 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 GetNumBlocksPerPixel(void) const; + unsigned int GetPixelWidth(void) const; // tolua_end @@ -87,8 +95,6 @@ private: /** Update the specified pixel. */ bool UpdatePixel(unsigned int a_X, unsigned int a_Y); - void PixelToWorldCoords(unsigned int a_X, unsigned int a_Y, int & a_WorldX, int & a_WorldY); - unsigned int m_ID; unsigned int m_Width; @@ -105,6 +111,11 @@ private: cWorld * m_World; + //typedef std::vector cPlayerList; + //cPlayerList m_TrackedPlayers; + + AString m_Name; + friend class cMapSerializer; }; From 3b24bc870bb39a8b8812ed307250e1188b9ff788 Mon Sep 17 00:00:00 2001 From: andrew Date: Mon, 17 Feb 2014 16:27:12 +0200 Subject: [PATCH 06/16] Map item handler; Fixed several bugs --- src/ClientHandle.cpp | 2 - src/Entities/Player.cpp | 3 ++ src/Inventory.cpp | 25 ++++++++++++ src/Inventory.h | 3 ++ src/Items/ItemEmptyMap.h | 16 +++++++- src/Items/ItemHandler.cpp | 2 + src/Items/ItemHandler.h | 8 ++++ src/Items/ItemMap.h | 43 ++++++++++++++++++++ src/Map.cpp | 63 ++++++++++++++++++++++++------ src/Map.h | 13 ++++-- src/World.cpp | 28 +++++++++---- src/WorldStorage/MapSerializer.cpp | 11 +++++- src/WorldStorage/MapSerializer.h | 7 +++- 13 files changed, 195 insertions(+), 29 deletions(-) create mode 100644 src/Items/ItemMap.h diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index ff8775771..a2cbaefff 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -1199,8 +1199,6 @@ void cClientHandle::HandleSlotSelected(short a_SlotNum) if (Map != NULL) { - Map->UpdateRadius(*m_Player, 128); // Temporary - // TODO 2014-02-14 xdot: Optimization - Do not send the whole map. Map->SendTo(*this); } diff --git a/src/Entities/Player.cpp b/src/Entities/Player.cpp index 286d43cf6..fdf8d4303 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/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 index 24d31151b..b06cf9d13 100644 --- a/src/Items/ItemEmptyMap.h +++ b/src/Items/ItemEmptyMap.h @@ -41,7 +41,21 @@ public: int CenterX = round(a_Player->GetPosX() / (float) RegionWidth) * RegionWidth; int CenterZ = round(a_Player->GetPosZ() / (float) RegionWidth) * RegionWidth; - a_World->CreateMap(CenterX, CenterZ, DEFAULT_SCALE); + 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 755766d64..cab8dec97 100644 --- a/src/Items/ItemHandler.cpp +++ b/src/Items/ItemHandler.cpp @@ -25,6 +25,7 @@ #include "ItemHoe.h" #include "ItemLeaves.h" #include "ItemLighter.h" +#include "ItemMap.h" #include "ItemMinecart.h" #include "ItemNetherWart.h" #include "ItemPickaxe.h" @@ -107,6 +108,7 @@ cItemHandler *cItemHandler::CreateItemHandler(int a_ItemType) 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_NETHER_WART: return new cItemNetherWartHandler(a_ItemType); case E_ITEM_REDSTONE_DUST: return new cItemRedstoneDustHandler(a_ItemType); case E_ITEM_REDSTONE_REPEATER: return new cItemRedstoneRepeaterHandler(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..c3e605547 --- /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->AddTrackedPlayer(a_Player); + + Map->UpdateRadius(*a_Player, DEFAULT_RADIUS); + } +} ; diff --git a/src/Map.cpp b/src/Map.cpp index e0f991693..e85c23d3a 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -50,15 +50,25 @@ cMap::cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, un +template +T Clamp(T a_X, T a_Min, T a_Max) +{ + return std::min(std::max(a_X, a_Min), a_Max); +} + + + + + void cMap::UpdateRadius(int a_PixelX, int a_PixelZ, unsigned int a_Radius) { int PixelRadius = a_Radius / GetPixelWidth(); - unsigned int StartX = std::max(a_PixelX - PixelRadius, 0); - unsigned int StartZ = std::max(a_PixelZ - PixelRadius, 0); + 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 = std::min(a_PixelX + PixelRadius, (int)m_Width); - unsigned int EndZ = std::min(a_PixelZ + PixelRadius, (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) { @@ -93,11 +103,9 @@ void cMap::UpdateRadius(cPlayer & a_Player, unsigned int a_Radius) -bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Y) +bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Z) { - ASSERT(m_World != NULL); - - unsigned int PixelWidth = GetPixelWidth(); + /*unsigned int PixelWidth = GetPixelWidth(); int BlockX = m_CenterX + ((a_X - m_Width) * PixelWidth); int BlockZ = m_CenterZ + ((a_Y - m_Height) * PixelWidth); @@ -119,7 +127,7 @@ bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Y) 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(0) {} + : m_Map(a_Map), m_RelX(a_RelX), m_RelZ(a_RelZ), m_PixelData(4) {} virtual bool Item(cChunk * a_Chunk) override { @@ -155,9 +163,9 @@ bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Y) } CalculatePixelCb(this, RelX, RelZ); ASSERT(m_World != NULL); - m_World->DoWithChunk(ChunkX, ChunkZ, CalculatePixelCb); + m_World->DoWithChunk(ChunkX, ChunkZ, CalculatePixelCb);*/ - m_Data[a_Y + (a_X * m_Height)] = CalculatePixelCb.GetPixelData(); + m_Data[a_Z + (a_X * m_Height)] = 4; return true; } @@ -166,6 +174,39 @@ bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Y) +void cMap::UpdateTrackedPlayers(void) +{ + cTrackedPlayerList NewList; + + for (cTrackedPlayerList::iterator it = m_TrackedPlayers.begin(); it != m_TrackedPlayers.end(); ++it) + { + cPlayer * Player = *it; + + UpdateRadius(*Player, DEFAULT_RADIUS); + + if (true) + { + NewList.insert(Player); + } + } + + std::swap(m_TrackedPlayers, NewList); +} + + + + + +void cMap::AddTrackedPlayer(cPlayer * a_Player) +{ + ASSERT(a_Player != NULL); + m_TrackedPlayers.insert(a_Player); +} + + + + + void cMap::EraseData(void) { m_Data.assign(m_Width * m_Height, 0); diff --git a/src/Map.h b/src/Map.h index 4134d53a1..805dfb845 100644 --- a/src/Map.h +++ b/src/Map.h @@ -38,6 +38,8 @@ public: typedef std::vector cColorList; + static const unsigned int DEFAULT_RADIUS = 128; + public: @@ -54,6 +56,10 @@ public: void UpdateRadius(cPlayer & a_Player, unsigned int a_Radius); + void UpdateTrackedPlayers(void); + + void AddTrackedPlayer(cPlayer * a_Player); + // tolua_begin /** Erase pixel data */ @@ -93,7 +99,7 @@ public: private: /** Update the specified pixel. */ - bool UpdatePixel(unsigned int a_X, unsigned int a_Y); + bool UpdatePixel(unsigned int a_X, unsigned int a_Z); unsigned int m_ID; @@ -111,8 +117,9 @@ private: cWorld * m_World; - //typedef std::vector cPlayerList; - //cPlayerList m_TrackedPlayers; + typedef std::set cTrackedPlayerList; + + cTrackedPlayerList m_TrackedPlayers; AString m_Name; diff --git a/src/World.cpp b/src/World.cpp index 2a3e53332..55c6fbb7a 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -1574,8 +1574,7 @@ cMap * cWorld::CreateMap(int a_CenterX, int a_CenterY, int a_Scale) { if (m_MapData.size() >= 65536) { - LOGD("cWorld::CreateMap - Too many maps in use"); - + LOGWARN("Could not craft map - Too many maps in use"); return NULL; } @@ -2992,9 +2991,12 @@ void cWorld::LoadMapData(void) { cIDCountSerializer IDSerializer(GetName()); - IDSerializer.Load(); + if (!IDSerializer.Load()) + { + return; + } - unsigned int MapCount = IDSerializer.GetMapCount() + 1; + unsigned int MapCount = IDSerializer.GetMapCount(); m_MapData.clear(); @@ -3004,7 +3006,10 @@ void cWorld::LoadMapData(void) cMapSerializer Serializer(GetName(), &Map); - Serializer.Load(); + if (!Serializer.Load()) + { + LOGWARN("Could not load map #%i", Map.GetID()); + } m_MapData.push_back(Map); } @@ -3023,9 +3028,13 @@ void cWorld::SaveMapData(void) cIDCountSerializer IDSerializer(GetName()); - IDSerializer.SetMapCount(m_MapData.size() - 1); + IDSerializer.SetMapCount(m_MapData.size()); - IDSerializer.Save(); + if (!IDSerializer.Save()) + { + LOGERROR("Could not save idcounts.dat"); + return; + } for (cMapList::iterator it = m_MapData.begin(); it != m_MapData.end(); ++it) { @@ -3033,7 +3042,10 @@ void cWorld::SaveMapData(void) cMapSerializer Serializer(GetName(), &Map); - Serializer.Save(); + if (!Serializer.Save()) + { + LOGWARN("Could not save map #%i", Map.GetID()); + } } } diff --git a/src/WorldStorage/MapSerializer.cpp b/src/WorldStorage/MapSerializer.cpp index 6dab19d4f..0bbe71a60 100644 --- a/src/WorldStorage/MapSerializer.cpp +++ b/src/WorldStorage/MapSerializer.cpp @@ -223,7 +223,11 @@ bool cIDCountSerializer::Load(void) int CurrLine = NBT.FindChildByName(0, "map"); if (CurrLine >= 0) { - m_MapCount = (int)NBT.GetShort(CurrLine); + m_MapCount = (int)NBT.GetShort(CurrLine) + 1; + } + else + { + m_MapCount = 0; } return true; @@ -237,7 +241,10 @@ bool cIDCountSerializer::Save(void) { cFastNBTWriter Writer; - Writer.AddShort("map", m_MapCount); + if (m_MapCount > 0) + { + Writer.AddShort("map", m_MapCount - 1); + } Writer.Finish(); diff --git a/src/WorldStorage/MapSerializer.h b/src/WorldStorage/MapSerializer.h index d9da107bc..296cc92c8 100644 --- a/src/WorldStorage/MapSerializer.h +++ b/src/WorldStorage/MapSerializer.h @@ -27,10 +27,10 @@ public: cMapSerializer(const AString& a_WorldName, cMap * a_Map); - /// Try to load the scoreboard + /** Try to load the scoreboard */ bool Load(void); - /// Try to save the scoreboard + /** Try to save the scoreboard */ bool Save(void); @@ -56,8 +56,10 @@ 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; } @@ -70,6 +72,7 @@ private: AString m_Path; unsigned int m_MapCount; + }; From 393ca0221dfdb6dabadcf293fea86a830453c938 Mon Sep 17 00:00:00 2001 From: andrew Date: Tue, 18 Feb 2014 20:50:08 +0200 Subject: [PATCH 07/16] Map decorators; Map clients --- src/ClientHandle.cpp | 22 ++- src/ClientHandle.h | 2 + src/Items/ItemMap.h | 4 +- src/Map.cpp | 238 +++++++++++++++++++++++++--- src/Map.h | 75 ++++++++- src/Protocol/Protocol.h | 3 +- src/Protocol/Protocol125.cpp | 25 +++ src/Protocol/Protocol125.h | 1 + src/Protocol/Protocol17x.cpp | 20 +++ src/Protocol/Protocol17x.h | 1 + src/Protocol/ProtocolRecognizer.cpp | 10 ++ src/Protocol/ProtocolRecognizer.h | 1 + 12 files changed, 354 insertions(+), 48 deletions(-) diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index a2cbaefff..efc5a9f64 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -1190,19 +1190,6 @@ void cClientHandle::HandleSlotSelected(short a_SlotNum) { m_Player->GetInventory().SetEquippedSlotNum(a_SlotNum); m_Player->GetWorld()->BroadcastEntityEquipment(*m_Player, 0, m_Player->GetInventory().GetEquippedItem(), this); - - const cItem & Item = m_Player->GetInventory().GetEquippedItem(); - if (Item.m_ItemType == E_ITEM_MAP) - { - // TODO 2014-02-14 xdot: Do not hardcode this. - cMap * Map = m_Player->GetWorld()->GetMapData(Item.m_ItemDamage); - - if (Map != NULL) - { - // TODO 2014-02-14 xdot: Optimization - Do not send the whole map. - Map->SendTo(*this); - } - } } @@ -2079,6 +2066,15 @@ void cClientHandle::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Col +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); diff --git a/src/ClientHandle.h b/src/ClientHandle.h index a714cf8b9..a613a76e8 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" @@ -110,6 +111,7 @@ public: 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 SendPickupSpawn (const cPickup & a_Pickup); void SendEntityAnimation (const cEntity & a_Entity, char a_Animation); diff --git a/src/Items/ItemMap.h b/src/Items/ItemMap.h index c3e605547..9bb16b189 100644 --- a/src/Items/ItemMap.h +++ b/src/Items/ItemMap.h @@ -36,8 +36,8 @@ public: return; } - // Map->AddTrackedPlayer(a_Player); - Map->UpdateRadius(*a_Player, DEFAULT_RADIUS); + + Map->UpdateClient(a_Player); } } ; diff --git a/src/Map.cpp b/src/Map.cpp index e85c23d3a..f32d232fa 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -14,6 +14,111 @@ +cMapDecorator::cMapDecorator(cMap * a_Map, eType a_Type, int a_X, int a_Z, unsigned 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(); +} + + + + + +template +T Clamp(T a_X, T a_Min, T a_Max) +{ + return std::min(std::max(a_X, a_Min), a_Max); +} + + + + + +void cMapDecorator::Update(void) +{ + ASSERT(m_Map != NULL); + unsigned int PixelWidth = m_Map->GetPixelWidth(); + + int InsideWidth = (m_Map->GetWidth() / 2) - 1; + int InsideHeight = (m_Map->GetHeight() / 2) - 1; + + if (m_Player) + { + 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; + + // 1px border + if ((PixelX > -InsideWidth) && (PixelX <= InsideWidth) && (PixelZ > -InsideHeight) && (PixelZ <= InsideHeight)) + { + double Yaw = m_Player->GetYaw(); + + m_Rot = (Yaw * 16) / 360; + + if (m_Map->GetDimension() == dimNether) + { + Int64 WorldAge = m_Player->GetWorld()->GetWorldAge(); + + // TODO 2014-02-18 xdot: Random rotations + } + + 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) @@ -50,16 +155,6 @@ cMap::cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, un -template -T Clamp(T a_X, T a_Min, T a_Max) -{ - return std::min(std::max(a_X, a_Min), a_Max); -} - - - - - void cMap::UpdateRadius(int a_PixelX, int a_PixelZ, unsigned int a_Radius) { int PixelRadius = a_Radius / GetPixelWidth(); @@ -174,33 +269,117 @@ bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Z) -void cMap::UpdateTrackedPlayers(void) +void cMap::UpdateDecorators(void) { - cTrackedPlayerList NewList; - - for (cTrackedPlayerList::iterator it = m_TrackedPlayers.begin(); it != m_TrackedPlayers.end(); ++it) + for (cMapDecoratorList::iterator it = m_Decorators.begin(); it != m_Decorators.end(); ++it) { - cPlayer * Player = *it; - - UpdateRadius(*Player, DEFAULT_RADIUS); - - if (true) - { - NewList.insert(Player); - } + it->Update(); } - - std::swap(m_TrackedPlayers, NewList); } -void cMap::AddTrackedPlayer(cPlayer * a_Player) +void cMap::UpdateClient(cPlayer * a_Player) { ASSERT(a_Player != NULL); - m_TrackedPlayers.insert(a_Player); + 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) + { + 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); } @@ -267,6 +446,11 @@ void cMap::SetScale(unsigned int a_Scale) } m_Scale = a_Scale; + + for (cMapClientList::iterator it = m_Clients.begin(); it != m_Clients.end(); ++it) + { + it->m_SendInfo = true; + } } @@ -283,6 +467,8 @@ void cMap::SendTo(cClientHandle & a_Client) a_Client.SendMapColumn(m_ID, i, 0, Colors, m_Height); } + + a_Client.SendMapDecorators(m_ID, m_Decorators); } diff --git a/src/Map.h b/src/Map.h index 805dfb845..76e459621 100644 --- a/src/Map.h +++ b/src/Map.h @@ -22,6 +22,55 @@ class cClientHandle; class cWorld; class cPlayer; +class cMap; + + + + + +class cMapDecorator +{ +public: + enum eType + { + E_TYPE_PLAYER = 0x00, + E_TYPE_ITEM_FRAME = 0x01, + E_TYPE_PLAYER_OUTSIDE = 0x06 + }; + + +public: + + cMapDecorator(cMap * a_Map, eType a_Type, int a_X, int a_Z, unsigned int a_Rot); + + cMapDecorator(cMap * a_Map, cPlayer * a_Player); + + void Update(void); + + unsigned int GetPixelX(void) const { return m_PixelX; } + unsigned int GetPixelZ(void) const { return m_PixelZ; } + unsigned 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 cMapDecoratorList; @@ -38,7 +87,19 @@ public: typedef std::vector cColorList; - static const unsigned int DEFAULT_RADIUS = 128; + struct cMapClient + { + cClientHandle * m_Handle; + + bool m_SendInfo; + + unsigned int m_NextDecoratorUpdate; + + Int64 m_DataUpdate; + Int64 m_LastUpdate; + }; + + typedef std::list cMapClientList; public: @@ -56,9 +117,8 @@ public: void UpdateRadius(cPlayer & a_Player, unsigned int a_Radius); - void UpdateTrackedPlayers(void); - - void AddTrackedPlayer(cPlayer * a_Player); + /** Send next update packet and remove invalid decorators */ + void UpdateClient(cPlayer * a_Player); // tolua_begin @@ -98,6 +158,9 @@ public: private: + /** Update the associated decorators. */ + void UpdateDecorators(void); + /** Update the specified pixel. */ bool UpdatePixel(unsigned int a_X, unsigned int a_Z); @@ -117,9 +180,9 @@ private: cWorld * m_World; - typedef std::set cTrackedPlayerList; + cMapDecoratorList m_Decorators; - cTrackedPlayerList m_TrackedPlayers; + cMapClientList m_Clients; AString m_Name; diff --git a/src/Protocol/Protocol.h b/src/Protocol/Protocol.h index 5f89799e1..4a1601487 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" @@ -28,7 +29,6 @@ class cWorld; class cMonster; class cChunkDataSerializer; class cFallingBlock; -class cMap; @@ -81,6 +81,7 @@ public: 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 SendPickupSpawn (const cPickup & a_Pickup) = 0; virtual void SendPlayerAbilities (void) = 0; diff --git a/src/Protocol/Protocol125.cpp b/src/Protocol/Protocol125.cpp index edb22fae6..220fa18cf 100644 --- a/src/Protocol/Protocol125.cpp +++ b/src/Protocol/Protocol125.cpp @@ -602,6 +602,31 @@ void cProtocol125::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colo +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) { diff --git a/src/Protocol/Protocol125.h b/src/Protocol/Protocol125.h index 467aee002..1eeb15120 100644 --- a/src/Protocol/Protocol125.h +++ b/src/Protocol/Protocol125.h @@ -55,6 +55,7 @@ public: 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 SendPickupSpawn (const cPickup & a_Pickup) override; diff --git a/src/Protocol/Protocol17x.cpp b/src/Protocol/Protocol17x.cpp index 4acc61586..38b4ed786 100644 --- a/src/Protocol/Protocol17x.cpp +++ b/src/Protocol/Protocol17x.cpp @@ -516,6 +516,26 @@ void cProtocol172::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * a_Colo +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); diff --git a/src/Protocol/Protocol17x.h b/src/Protocol/Protocol17x.h index 0e50db45d..4edf51d2f 100644 --- a/src/Protocol/Protocol17x.h +++ b/src/Protocol/Protocol17x.h @@ -77,6 +77,7 @@ public: 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 SendPickupSpawn (const cPickup & a_Pickup) override; virtual void SendPlayerAbilities (void) override; diff --git a/src/Protocol/ProtocolRecognizer.cpp b/src/Protocol/ProtocolRecognizer.cpp index 447fa516b..0a9369c0d 100644 --- a/src/Protocol/ProtocolRecognizer.cpp +++ b/src/Protocol/ProtocolRecognizer.cpp @@ -396,6 +396,16 @@ void cProtocolRecognizer::SendMapColumn(int a_ID, int a_X, int a_Y, const Byte * +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); diff --git a/src/Protocol/ProtocolRecognizer.h b/src/Protocol/ProtocolRecognizer.h index 3c37d5138..ff4549ff5 100644 --- a/src/Protocol/ProtocolRecognizer.h +++ b/src/Protocol/ProtocolRecognizer.h @@ -90,6 +90,7 @@ public: 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 SendPickupSpawn (const cPickup & a_Pickup) override; From 4a1ac5740869356880523dde83ec0b7804689d42 Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 19 Feb 2014 15:28:48 +0200 Subject: [PATCH 08/16] Documented cMap --- src/Map.cpp | 12 ++++++++---- src/Map.h | 24 +++++++++++++++++++++--- src/WorldStorage/MapSerializer.h | 2 ++ 3 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/Map.cpp b/src/Map.cpp index f32d232fa..4f8924af2 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -14,7 +14,7 @@ -cMapDecorator::cMapDecorator(cMap * a_Map, eType a_Type, int a_X, int a_Z, unsigned int a_Rot) +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) @@ -58,7 +58,7 @@ void cMapDecorator::Update(void) int InsideWidth = (m_Map->GetWidth() / 2) - 1; int InsideHeight = (m_Map->GetHeight() / 2) - 1; - if (m_Player) + if (m_Player != NULL) { int PixelX = (m_Player->GetPosX() - m_Map->GetCenterX()) / PixelWidth; int PixelZ = (m_Player->GetPosZ() - m_Map->GetCenterZ()) / PixelWidth; @@ -200,7 +200,8 @@ void cMap::UpdateRadius(cPlayer & a_Player, unsigned int a_Radius) bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Z) { - /*unsigned int PixelWidth = GetPixelWidth(); + /* + unsigned int PixelWidth = GetPixelWidth(); int BlockX = m_CenterX + ((a_X - m_Width) * PixelWidth); int BlockZ = m_CenterZ + ((a_Y - m_Height) * PixelWidth); @@ -258,7 +259,8 @@ bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Z) } CalculatePixelCb(this, RelX, RelZ); ASSERT(m_World != NULL); - m_World->DoWithChunk(ChunkX, ChunkZ, CalculatePixelCb);*/ + m_World->DoWithChunk(ChunkX, ChunkZ, CalculatePixelCb); + */ m_Data[a_Z + (a_X * m_Height)] = 4; @@ -346,6 +348,8 @@ void cMap::UpdateClient(cPlayer * a_Player) 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); diff --git a/src/Map.h b/src/Map.h index 76e459621..1a330e1c2 100644 --- a/src/Map.h +++ b/src/Map.h @@ -28,28 +28,36 @@ 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: - cMapDecorator(cMap * a_Map, eType a_Type, int a_X, int a_Z, unsigned int a_Rot); + /** 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 pixel coordinates of the decorator. */ void Update(void); unsigned int GetPixelX(void) const { return m_PixelX; } unsigned int GetPixelZ(void) const { return m_PixelZ; } - unsigned int GetRot(void) const { return m_Rot; } + + int GetRot(void) const { return m_Rot; } eType GetType(void) const { return m_Type; } @@ -68,6 +76,7 @@ protected: unsigned int m_Rot; cPlayer * m_Player; + }; typedef std::list cMapDecoratorList; @@ -77,6 +86,8 @@ typedef std::list cMapDecoratorList; // tolua_begin + +/** Encapsulates an in-game world map. */ class cMap { public: @@ -87,15 +98,20 @@ public: typedef std::vector 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; }; @@ -107,14 +123,16 @@ public: /** Construct an empty map. */ cMap(unsigned int a_ID, cWorld * a_World); + /** Constructs 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. */ + /** 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 and remove invalid decorators */ diff --git a/src/WorldStorage/MapSerializer.h b/src/WorldStorage/MapSerializer.h index 296cc92c8..85fe917f5 100644 --- a/src/WorldStorage/MapSerializer.h +++ b/src/WorldStorage/MapSerializer.h @@ -21,6 +21,7 @@ class cMap; +/** Utility class used to serialize maps. */ class cMapSerializer { public: @@ -50,6 +51,7 @@ private: +/** Utility class used to serialize item ID counts. */ class cIDCountSerializer { public: From 58a708825fa7e79c9dcbe6ad1bbbb2c0c3247edc Mon Sep 17 00:00:00 2001 From: andrew Date: Wed, 19 Feb 2014 20:57:14 +0200 Subject: [PATCH 09/16] cMapDecorator: Implemented random rotations --- src/Map.cpp | 25 +++++++++++++++---------- src/Map.h | 6 +++--- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/Map.cpp b/src/Map.cpp index 4f8924af2..a194dbd96 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -9,6 +9,7 @@ #include "World.h" #include "Chunk.h" #include "Entities/Player.h" +#include "FastRandom.h" @@ -52,14 +53,14 @@ T Clamp(T a_X, T a_Min, T a_Max) void cMapDecorator::Update(void) { - ASSERT(m_Map != NULL); - unsigned int PixelWidth = m_Map->GetPixelWidth(); - - int InsideWidth = (m_Map->GetWidth() / 2) - 1; - int InsideHeight = (m_Map->GetHeight() / 2) - 1; - 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; @@ -67,18 +68,22 @@ void cMapDecorator::Update(void) m_PixelX = (2 * PixelX) + 1; m_PixelZ = (2 * PixelZ) + 1; - // 1px border if ((PixelX > -InsideWidth) && (PixelX <= InsideWidth) && (PixelZ > -InsideHeight) && (PixelZ <= InsideHeight)) { double Yaw = m_Player->GetYaw(); - m_Rot = (Yaw * 16) / 360; - if (m_Map->GetDimension() == dimNether) { + cFastRandom Random; + Int64 WorldAge = m_Player->GetWorld()->GetWorldAge(); - // TODO 2014-02-18 xdot: Random rotations + // TODO 2014-02-19 xdot: Refine + m_Rot = Random.NextInt(16, WorldAge); + } + else + { + m_Rot = (Yaw * 16) / 360; } m_Type = E_TYPE_PLAYER; diff --git a/src/Map.h b/src/Map.h index 1a330e1c2..3cf9977ab 100644 --- a/src/Map.h +++ b/src/Map.h @@ -51,7 +51,7 @@ public: /** Constructs a map decorator that tracks a player. */ cMapDecorator(cMap * a_Map, cPlayer * a_Player); - /** Updates the pixel coordinates of the decorator. */ + /** Updates the decorator. */ void Update(void); unsigned int GetPixelX(void) const { return m_PixelX; } @@ -123,7 +123,7 @@ public: /** Construct an empty map. */ cMap(unsigned int a_ID, cWorld * a_World); - /** Constructs an empty map at the specified coordinates. */ + /** 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 */ @@ -135,7 +135,7 @@ public: /** Update a circular region around the specified player. */ void UpdateRadius(cPlayer & a_Player, unsigned int a_Radius); - /** Send next update packet and remove invalid decorators */ + /** Send next update packet to the specified player and remove invalid decorators/clients. */ void UpdateClient(cPlayer * a_Player); // tolua_begin From f201f4f176fc908e9ddebfed86d4c8ef5582556c Mon Sep 17 00:00:00 2001 From: andrew Date: Thu, 20 Feb 2014 16:38:37 +0200 Subject: [PATCH 10/16] Thread safe cMap manager --- src/Generating/StructGen.cpp | 9 -- src/Globals.h | 6 +- src/Map.h | 15 ++- src/MapManager.cpp | 178 +++++++++++++++++++++++++++++++++++ src/MapManager.h | 76 +++++++++++++++ src/World.cpp | 3 +- src/World.h | 5 +- 7 files changed, 275 insertions(+), 17 deletions(-) create mode 100644 src/MapManager.cpp create mode 100644 src/MapManager.h diff --git a/src/Generating/StructGen.cpp b/src/Generating/StructGen.cpp index 4efcf92f0..47945cc2b 100644 --- a/src/Generating/StructGen.cpp +++ b/src/Generating/StructGen.cpp @@ -51,15 +51,6 @@ const int NEST_SIZE_GRAVEL = 32; -template T Clamp(T a_Value, T a_Min, T a_Max) -{ - return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value); -} - - - - - /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // cStructGenTrees: diff --git a/src/Globals.h b/src/Globals.h index 7ee045130..e4737a98a 100644 --- a/src/Globals.h +++ b/src/Globals.h @@ -235,11 +235,11 @@ public: -/** Clamp a_X to the specified range. */ +/** Clamp X to the specified range. */ template -T Clamp(T a_X, T a_Min, T a_Max) +T Clamp(T a_Value, T a_Min, T a_Max) { - return std::min(std::max(a_X, a_Min), a_Max); + return (a_Value < a_Min) ? a_Min : ((a_Value > a_Max) ? a_Max : a_Value); } diff --git a/src/Map.h b/src/Map.h index 3cf9977ab..ce19c8d2e 100644 --- a/src/Map.h +++ b/src/Map.h @@ -28,7 +28,14 @@ class cMap; -/** Encapsulates a map decorator. */ +/** Encapsulates a map decorator. + * + * A map decorator represents an object drawn on the map that can move freely. + * (e.g. player trackers and item frame pointers) + * + * Excluding manually placed decorators, + * decorators are automatically managed (allocated and freed) by their parent cMap instance. + */ class cMapDecorator { public: @@ -98,7 +105,11 @@ public: typedef std::vector cColorList; - /** Encapsulates the state of a map client. */ + /** Encapsulates the state of a map client. + * + * In order to enhance performace, maps are streamed column-by-column to each client. + * This structure stores the state of the stream. + */ struct cMapClient { cClientHandle * m_Handle; diff --git a/src/MapManager.cpp b/src/MapManager.cpp new file mode 100644 index 000000000..2fc44ccc8 --- /dev/null +++ b/src/MapManager.cpp @@ -0,0 +1,178 @@ + +// MapManager.cpp + +#include "Globals.h" + +#include "MapManager.h" + +#include "World.h" +#include "WorldStorage/MapSerializer.h" + + + + + +cMapManager::cMapManager(cWorld * a_World) + : m_World(a_World) +{ + ASSERT(m_World != NULL); +} + + + + + +bool cMapManager::DoWithMap(unsigned int a_ID, cMapCallback & a_Callback) +{ + cCSLock Lock(m_CS); + cMap * Map = GetMapData(a_ID); + + if (Map == NULL) + { + return false; + } + else + { + a_Callback.Item(Map); + return true; + } +} + + + + + +bool cMapManager::ForEachMap(cMapCallback & a_Callback) +{ + cCSLock Lock(m_CS); + for (cMapList::iterator itr = m_MapData.begin(); itr != m_MapData.end(); ++itr) + { + cMap * Map = &(*itr); + if (a_Callback.Item(Map)) + { + return false; + } + } // for itr - m_MapData[] + return true; +} + + + + + +cMap * cMapManager::GetMapData(unsigned int a_ID) +{ + if (a_ID < m_MapData.size()) + { + return &m_MapData[a_ID]; + } + else + { + return NULL; + } +} + + + + + +cMap * cMapManager::CreateMap(int a_CenterX, int a_CenterY, int a_Scale) +{ + cCSLock Lock(m_CS); + + if (m_MapData.size() >= 65536) + { + LOGWARN("Could not craft map - Too many maps in use"); + return NULL; + } + + cMap Map(m_MapData.size(), a_CenterX, a_CenterY, m_World, a_Scale); + + m_MapData.push_back(Map); + + return &m_MapData[Map.GetID()]; +} + + + + + +unsigned int cMapManager::GetNumMaps(void) const +{ + return m_MapData.size(); +} + + + + + +void cMapManager::LoadMapData(void) +{ + cCSLock Lock(m_CS); + + cIDCountSerializer IDSerializer(m_World->GetName()); + + if (!IDSerializer.Load()) + { + return; + } + + unsigned int MapCount = IDSerializer.GetMapCount(); + + m_MapData.clear(); + + for (unsigned int i = 0; i < MapCount; ++i) + { + cMap Map(i, m_World); + + cMapSerializer Serializer(m_World->GetName(), &Map); + + if (!Serializer.Load()) + { + LOGWARN("Could not load map #%i", Map.GetID()); + } + + m_MapData.push_back(Map); + } +} + + + + + +void cMapManager::SaveMapData(void) +{ + cCSLock Lock(m_CS); + + if (m_MapData.empty()) + { + return; + } + + cIDCountSerializer IDSerializer(m_World->GetName()); + + IDSerializer.SetMapCount(m_MapData.size()); + + if (!IDSerializer.Save()) + { + LOGERROR("Could not save idcounts.dat"); + return; + } + + for (cMapList::iterator it = m_MapData.begin(); it != m_MapData.end(); ++it) + { + cMap & Map = *it; + + cMapSerializer Serializer(m_World->GetName(), &Map); + + if (!Serializer.Save()) + { + LOGWARN("Could not save map #%i", Map.GetID()); + } + } +} + + + + + diff --git a/src/MapManager.h b/src/MapManager.h new file mode 100644 index 000000000..05673c694 --- /dev/null +++ b/src/MapManager.h @@ -0,0 +1,76 @@ + +// MapManager.h + + + + + +#pragma once + + + + + +#include "Map.h" + + + + +typedef cItemCallback cMapCallback; + + + + + +/** Manages the in-game maps of a single world - Thread safe. */ +class cMapManager +{ +public: + + cMapManager(cWorld * a_World); + + /** Returns the map with the specified ID, NULL if out of range. + * + * WARNING: The returned map object is not thread safe. + */ + cMap * GetMapData(unsigned int a_ID); + + /** Creates a new map. Returns NULL on error */ + cMap * CreateMap(int a_CenterX, int a_CenterY, int a_Scale = 3); + + /** Calls the callback for the map with the specified ID. + * + * Returns true if the map was found and the callback called, false if map not found. + * Callback return ignored. + */ + bool DoWithMap(unsigned int a_ID, cMapCallback & a_Callback); + + /** Calls the callback for each map. + * + * Returns true if all maps processed, false if the callback aborted by returning true. + */ + bool ForEachMap(cMapCallback & a_Callback); + + unsigned int GetNumMaps(void) const; + + /** Loads the map data from the disk */ + void LoadMapData(void); + + /** Saves the map data to the disk */ + void SaveMapData(void); + + +private: + + typedef std::vector cMapList; + + cCriticalSection m_CS; + + cMapList m_MapData; + + cWorld * m_World; + +}; + + + diff --git a/src/World.cpp b/src/World.cpp index c1e0731c1..01fdae697 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -12,8 +12,8 @@ #include "Generating/ChunkDesc.h" #include "OSSupport/Timer.h" +// Serializers #include "WorldStorage/ScoreboardSerializer.h" -#include "WorldStorage/MapSerializer.h" // Entities (except mobs): #include "Entities/ExpOrb.h" @@ -233,6 +233,7 @@ void cWorld::cTickThread::Execute(void) // cWorld: cWorld::cWorld(const AString & a_WorldName) : + cMapManager(this), m_WorldName(a_WorldName), m_IniFileName(m_WorldName + "/world.ini"), m_StorageSchema("Default"), diff --git a/src/World.h b/src/World.h index 92fc66c8c..f05ea9b2a 100644 --- a/src/World.h +++ b/src/World.h @@ -24,7 +24,7 @@ #include "Entities/ProjectileEntity.h" #include "ForEachChunkProvider.h" #include "Scoreboard.h" -#include "Map.h" +#include "MapManager.h" #include "Blocks/WorldInterface.h" #include "Blocks/BroadcastInterface.h" @@ -71,7 +71,8 @@ typedef cItemCallback cMobHeadBlockCallback; class cWorld : public cForEachChunkProvider, public cWorldInterface, - public cBroadcastInterface + public cBroadcastInterface, + public cMapManager { public: From 8bf5d116fe4c7b2addeba2dac9a8b1fc93486444 Mon Sep 17 00:00:00 2001 From: andrew Date: Fri, 21 Feb 2014 15:26:33 +0200 Subject: [PATCH 11/16] Split cMap::UpdateClient --- src/Map.cpp | 136 +++++++++++++++++++++++++++++++--------------------- src/Map.h | 61 ++++++++++++++--------- 2 files changed, 119 insertions(+), 78 deletions(-) diff --git a/src/Map.cpp b/src/Map.cpp index cb5472a22..0028a1e94 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -278,28 +278,35 @@ void cMap::UpdateDecorators(void) -void cMap::UpdateClient(cPlayer * a_Player) +void cMap::AddPlayer(cPlayer * a_Player, cClientHandle * a_Handle, Int64 a_WorldAge) { - ASSERT(a_Player != NULL); - cClientHandle * Handle = a_Player->GetClientHandle(); + cMapClient MapClient; - if (Handle == NULL) - { - return; - } + MapClient.m_LastUpdate = a_WorldAge; + MapClient.m_SendInfo = true; + MapClient.m_Handle = a_Handle; - Int64 WorldAge = a_Player->GetWorld()->GetWorldAge(); + m_Clients.push_back(MapClient); - // Remove invalid clients + cMapDecorator PlayerDecorator(this, a_Player); + + m_Decorators.push_back(PlayerDecorator); +} + + + + + +void cMap::RemoveInactiveClients(Int64 a_WorldAge) +{ for (cMapClientList::iterator it = m_Clients.begin(); it != m_Clients.end();) { - // Check if client is active - if (it->m_LastUpdate < WorldAge - 5) + if (it->m_LastUpdate < a_WorldAge) { // Remove associated decorators for (cMapDecoratorList::iterator it2 = m_Decorators.begin(); it2 != m_Decorators.end();) { - if (it2->GetPlayer()->GetClientHandle() == Handle) + if (it2->GetPlayer()->GetClientHandle() == it->m_Handle) { // Erase decorator cMapDecoratorList::iterator temp = it2; @@ -322,6 +329,66 @@ void cMap::UpdateClient(cPlayer * a_Player) ++it; } } +} + + + + + +void cMap::StreamNext(cMapClient & a_Client) +{ + cClientHandle * Handle = a_Client.m_Handle; + + if (a_Client.m_SendInfo) + { + Handle->SendMapInfo(m_ID, m_Scale); + + a_Client.m_SendInfo = false; + + return; + } + + ++a_Client.m_NextDecoratorUpdate; + + if (a_Client.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); + + a_Client.m_NextDecoratorUpdate = 0; + } + else + { + ++a_Client.m_DataUpdate; + + unsigned int Y = (a_Client.m_DataUpdate * 11) % m_Width; + + const Byte * Colors = &m_Data[Y * m_Height]; + + Handle->SendMapColumn(m_ID, Y, 0, Colors, m_Height); + } +} + + + + + +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(); + + RemoveInactiveClients(WorldAge - 5); // Linear search for client state for (cMapClientList::iterator it = m_Clients.begin(); it != m_Clients.end(); ++it) @@ -330,55 +397,14 @@ void cMap::UpdateClient(cPlayer * a_Player) { 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); - } + StreamNext(*it); 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); + AddPlayer(a_Player, Handle, WorldAge); } diff --git a/src/Map.h b/src/Map.h index ce19c8d2e..01ffd19f5 100644 --- a/src/Map.h +++ b/src/Map.h @@ -105,29 +105,6 @@ public: typedef std::vector cColorList; - /** Encapsulates the state of a map client. - * - * In order to enhance performace, maps are streamed column-by-column to each client. - * This structure stores the state of the stream. - */ - 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 cMapClientList; - public: @@ -185,6 +162,32 @@ public: // tolua_end +protected: + + /** Encapsulates the state of a map client. + * + * In order to enhance performace, maps are streamed column-by-column to each client. + * This structure stores the state of the stream. + */ + 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 cMapClientList; + + private: /** Update the associated decorators. */ @@ -193,6 +196,15 @@ private: /** Update the specified pixel. */ bool UpdatePixel(unsigned int a_X, unsigned int a_Z); + /** Add a new map client. */ + void AddPlayer(cPlayer * a_Player, cClientHandle * a_Handle, Int64 a_WorldAge); + + /** Remove inactive or invalid clients. */ + void RemoveInactiveClients(Int64 a_WorldAge); + + /** Send next update packet to the specified client. */ + void StreamNext(cMapClient & a_Client); + unsigned int m_ID; unsigned int m_Width; @@ -218,3 +230,6 @@ private: friend class cMapSerializer; }; + + + From a96eea5e66191ee02c21e3ce3f33277c516142bc Mon Sep 17 00:00:00 2001 From: andrew Date: Sat, 22 Feb 2014 12:50:30 +0200 Subject: [PATCH 12/16] Semi-working implementation of cMap::UpdatePixel --- src/Map.cpp | 88 ++++++++++++++++++++++++++++++++++++++++++++++------- src/Map.h | 18 +++++++++++ 2 files changed, 95 insertions(+), 11 deletions(-) diff --git a/src/Map.cpp b/src/Map.cpp index 0028a1e94..87fe9555f 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -123,7 +123,7 @@ cMap::cMap(unsigned int a_ID, cWorld * a_World) , m_CenterZ(0) , m_World(a_World) { - m_Data.assign(m_Width * m_Height, 0); + m_Data.assign(m_Width * m_Height, E_BASE_COLOR_TRANSPARENT); Printf(m_Name, "map_%i", m_ID); } @@ -141,7 +141,7 @@ cMap::cMap(unsigned int a_ID, int a_CenterX, int a_CenterZ, cWorld * a_World, un , m_CenterZ(a_CenterZ) , m_World(a_World) { - m_Data.assign(m_Width * m_Height, 0); + m_Data.assign(m_Width * m_Height, E_BASE_COLOR_TRANSPARENT); Printf(m_Name, "map_%i", m_ID); } @@ -195,11 +195,10 @@ void cMap::UpdateRadius(cPlayer & a_Player, unsigned int 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 BlockX = m_CenterX + ((a_X - (m_Width / 2)) * PixelWidth); + int BlockZ = m_CenterZ + ((a_Z - (m_Height / 2)) * PixelWidth); int ChunkX, ChunkY, ChunkZ; m_World->BlockToChunk(BlockX, 0, BlockZ, ChunkX, ChunkY, ChunkZ); @@ -218,7 +217,7 @@ bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Z) 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) {} + : m_Map(a_Map), m_RelX(a_RelX), m_RelZ(a_RelZ), m_PixelData(E_BASE_COLOR_TRANSPARENT) {} virtual bool Item(cChunk * a_Chunk) override { @@ -229,20 +228,88 @@ bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Z) unsigned int PixelWidth = m_Map->GetPixelWidth(); + if (m_Map->GetDimension() == dimNether) + { + // TODO 2014-02-22 xdot: Nether maps + + return false; + } + + typedef std::map ColorCountMap; + ColorCountMap ColorCounts; + + // Count surface blocks for (unsigned int X = m_RelX; X < m_RelX + PixelWidth; ++X) { for (unsigned int Z = m_RelZ; Z < m_RelZ + PixelWidth; ++Z) { + unsigned int WaterDepth = 0; + + BLOCKTYPE TargetBlock = E_BLOCK_AIR; + NIBBLETYPE TargetMeta = 0; + int Height = a_Chunk->GetHeight(X, Z); - if (Height > 0) + while (Height > 0) { - // TODO + a_Chunk->GetBlockTypeMeta(X, Height, Z, TargetBlock, TargetMeta); + + // TODO 2014-02-22 xdot: Check if block color is transparent + if (TargetBlock == E_BLOCK_AIR) + { + --Height; + continue; + } + // TODO 2014-02-22 xdot: Check if block is liquid + else if (false) + { + --Height; + ++WaterDepth; + continue; + } + + break; } + + // TODO 2014-02-22 xdot: Query block color + ColorID Color = E_BASE_COLOR_BROWN; + + // Debug - Temporary + switch (TargetBlock) + { + case E_BLOCK_GRASS: + { + Color = E_BASE_COLOR_LIGHT_GREEN; break; + } + case E_BLOCK_STATIONARY_WATER: + case E_BLOCK_WATER: + { + Color = E_BASE_COLOR_BLUE; break; + } + } + + ++ColorCounts[Color]; } } - m_PixelData = 8; // Debug + // Find dominant color + ColorID PixelColor = E_BASE_COLOR_TRANSPARENT; + + unsigned int MaxCount = 0; + + for (ColorCountMap::iterator it = ColorCounts.begin(); it != ColorCounts.end(); ++it) + { + if (it->second > MaxCount) + { + PixelColor = it->first; + MaxCount = it->second; + } + } + + // TODO 2014-02-22 xdot: Adjust brightness + unsigned int dColor = 1; + + m_PixelData = PixelColor + dColor; return false; } @@ -255,9 +322,8 @@ bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Z) ASSERT(m_World != NULL); m_World->DoWithChunk(ChunkX, ChunkZ, CalculatePixelCb); - */ - m_Data[a_Z + (a_X * m_Height)] = 4; + m_Data[a_Z + (a_X * m_Height)] = CalculatePixelCb.GetPixelData(); return true; } diff --git a/src/Map.h b/src/Map.h index 01ffd19f5..a86de3dd3 100644 --- a/src/Map.h +++ b/src/Map.h @@ -99,6 +99,24 @@ class cMap { public: + enum eBaseColor + { + E_BASE_COLOR_TRANSPARENT = 0, /* Air */ + E_BASE_COLOR_LIGHT_GREEN = 4, /* Grass */ + E_BASE_COLOR_LIGHT_BROWN = 8, /* Sand */ + E_BASE_COLOR_GRAY_1 = 12, /* Cloth */ + E_BASE_COLOR_RED = 16, /* TNT */ + E_BASE_COLOR_PALE_BLUE = 20, /* Ice */ + E_BASE_COLOR_GRAY_2 = 24, /* Iron */ + E_BASE_COLOR_DARK_GREEN = 28, /* Foliage */ + E_BASE_COLOR_WHITE = 32, /* Snow */ + E_BASE_COLOR_LIGHT_GRAY = 36, /* Clay */ + E_BASE_COLOR_BROWN = 40, /* Dirt */ + E_BASE_COLOR_DARK_GRAY = 44, /* Stone */ + E_BASE_COLOR_BLUE = 48, /* Water */ + E_BASE_COLOR_DARK_BROWN = 52 /* Wood */ + }; + typedef Byte ColorID; // tolua_end From 866fde81ca50f223c88af77d0092a2f4e60f7ce9 Mon Sep 17 00:00:00 2001 From: andrew Date: Sat, 22 Feb 2014 13:59:49 +0200 Subject: [PATCH 13/16] Documented and exported cMap --- MCServer/Plugins/APIDump/APIDesc.lua | 49 ++++++++++++++++++++++++++++ src/Bindings/AllToLua.pkg | 1 + src/Map.cpp | 49 ++++++++++++++++++++++++---- src/Map.h | 12 +++++-- 4 files changed, 102 insertions(+), 9 deletions(-) diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 73bb5c7fb..296a60640 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -1485,6 +1485,55 @@ a_Player:OpenWindow(Window); Inherits = "cWindow", }, -- cLuaWindow + cMap = + { + Desc = [[ + This class encapsulates a single in-game colored map.

+

+ The contents (i.e. pixel data) of a cMap are dynamically updated by each + tracked {{cPlayer}} instance. Furthermore, a cMap maintains and periodically + updates a list of map decorators, which are objects drawn on the map that + can freely move (e.g. Player and item frame pointers). + ]], + Functions = + { + EraseData = { Params = "", Return = "", Notes = "Erases all pixel data." }, + GetCenterX = { Params = "", Return = "number", Notes = "Returns the X coord of the map's center." }, + GetCenterZ = { Params = "", Return = "number", Notes = "Returns the Y coord of the map's center." }, + GetDimension = { Params = "", Return = "eDimension", Notes = "Returns the dimension of the associated world." }, + GetHeight = { Params = "", Return = "number", Notes = "Returns the height of the map." }, + GetID = { Params = "", Return = "number", Notes = "Returns the numerical ID of the map. (The item damage value)" }, + GetName = { Params = "", Return = "string", Notes = "Returns the name of the map." }, + GetNumPixels = { Params = "", Return = "number", Notes = "Returns the number of pixels in this map." }, + GetPixel = { Params = "PixelX, PixelZ", Return = "ColorID", Notes = "Returns the color of the specified pixel." }, + GetPixelWidth = { Params = "", Return = "number", Notes = "Returns the width of a single pixel in blocks." }, + GetScale = { Params = "", Return = "number", Notes = "Returns the scale of the map. Range: [0,4]" }, + GetWidth = { Params = "", Return = "number", Notes = "Returns the width of the map." }, + GetWorld = { Params = "", Return = "cWorld", Notes = "Returns the associated world." }, + Resize = { Params = "Width, Height", Return = "", Notes = "Resizes the map. WARNING: This will erase the pixel data." }, + SetPixel = { Params = "PixelX, PixelZ, ColorID", Return = "bool", Notes = "Sets the color of the specified pixel. Returns false on error (Out of range)." }, + SetPosition = { Params = "CenterX, CenterZ", Return = "", Notes = "Relocates the map. The pixel data will not be modified." }, + SetScale = { Params = "number", Return = "", Notes = "Rescales the map. The pixel data will not be modified." }, + }, + Constants = + { + E_BASE_COLOR_BLUE = { Notes = "" }, + E_BASE_COLOR_BROWN = { Notes = "" }, + E_BASE_COLOR_DARK_BROWN = { Notes = "" }, + E_BASE_COLOR_DARK_GRAY = { Notes = "" }, + E_BASE_COLOR_DARK_GREEN = { Notes = "" }, + E_BASE_COLOR_GRAY_1 = { Notes = "" }, + E_BASE_COLOR_GRAY_2 = { Notes = "" }, + E_BASE_COLOR_LIGHT_BROWN = { Notes = "" }, + E_BASE_COLOR_LIGHT_GRAY = { Notes = "" }, + E_BASE_COLOR_LIGHT_GREEN = { Notes = "" }, + E_BASE_COLOR_PALE_BLUE = { Notes = "" }, + E_BASE_COLOR_RED = { Notes = "" }, + E_BASE_COLOR_TRANSPARENT = { Notes = "" }, + E_BASE_COLOR_WHITE = { Notes = "" }, + }, + }, -- cMap + cMonster = { Desc = [[ diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index ef61f55f0..dd45a2aab 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -73,6 +73,7 @@ $cfile "../CraftingRecipes.h" $cfile "../UI/Window.h" $cfile "../Mobs/Monster.h" $cfile "../CompositeChat.h" +$cfile "../Map.h" diff --git a/src/Map.cpp b/src/Map.cpp index 87fe9555f..e89fad8b0 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -323,7 +323,7 @@ bool cMap::UpdatePixel(unsigned int a_X, unsigned int a_Z) ASSERT(m_World != NULL); m_World->DoWithChunk(ChunkX, ChunkZ, CalculatePixelCb); - m_Data[a_Z + (a_X * m_Height)] = CalculatePixelCb.GetPixelData(); + SetPixel(a_X, a_Z, CalculatePixelCb.GetPixelData()); return true; } @@ -516,11 +516,6 @@ void cMap::Resize(unsigned int a_Width, unsigned int a_Height) 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; } @@ -548,6 +543,40 @@ void cMap::SetScale(unsigned int a_Scale) +bool cMap::SetPixel(unsigned int a_X, unsigned int a_Z, cMap::ColorID a_Data) +{ + if ((a_X < m_Width) && (a_Z < m_Height)) + { + m_Data[a_Z + (a_X * m_Height)] = a_Data; + + return true; + } + else + { + return false; + } +} + + + + + +cMap::ColorID cMap::GetPixel(unsigned int a_X, unsigned int a_Z) +{ + if ((a_X < m_Width) && (a_Z < m_Height)) + { + return m_Data[a_Z + (a_X * m_Height)]; + } + else + { + return E_BASE_COLOR_TRANSPARENT; + } +} + + + + + void cMap::SendTo(cClientHandle & a_Client) { a_Client.SendMapInfo(m_ID, m_Scale); @@ -575,6 +604,14 @@ unsigned int cMap::GetNumPixels(void) const +unsigned int cMap::GetNumDecorators(void) const +{ + return m_Decorators.size(); +} + + + + unsigned int cMap::GetPixelWidth(void) const { return pow(2, m_Scale); diff --git a/src/Map.h b/src/Map.h index a86de3dd3..fc754e6eb 100644 --- a/src/Map.h +++ b/src/Map.h @@ -155,6 +155,10 @@ public: void SetScale(unsigned int a_Scale); + bool SetPixel(unsigned int a_X, unsigned int a_Z, ColorID a_Data); + + ColorID GetPixel(unsigned int a_X, unsigned int a_Z); + unsigned int GetWidth (void) const { return m_Width; } unsigned int GetHeight(void) const { return m_Height; } @@ -171,14 +175,16 @@ public: eDimension GetDimension(void) const; - const cColorList & GetData(void) const { return m_Data; } - unsigned int GetNumPixels(void) const; unsigned int GetPixelWidth(void) const; // tolua_end + unsigned int GetNumDecorators(void) const; + + const cColorList & GetData(void) const { return m_Data; } + protected: @@ -247,7 +253,7 @@ private: friend class cMapSerializer; -}; +}; // tolua_export From 9fa4fa1cc737a0cf9a078956def206c31a4ebd02 Mon Sep 17 00:00:00 2001 From: andrew Date: Sun, 23 Feb 2014 12:55:55 +0200 Subject: [PATCH 14/16] Documented and exported cMapManager --- MCServer/Plugins/APIDump/APIDesc.lua | 14 ++++++++++++++ src/Bindings/AllToLua.pkg | 1 + src/MapManager.h | 8 +++++--- 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 296a60640..8265b4e19 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -1534,6 +1534,19 @@ a_Player:OpenWindow(Window); }, }, -- cMap + cMapManager = + { + Desc = [[ + This class is associated with a single {{cWorld}} instance and manages a list of maps. + ]], + Functions = + { + DoWithMap = { Params = "ID, Callback", Return = "bool", Notes = "Calls the callback for the map with the specified ID. Returns true if the map was found and the callback called, false if map not found." }, + GetNumMaps = { Params = "", Return = "number", Notes = "Returns the number of registered maps." }, + }, + + }, -- cMapManager + cMonster = { Desc = [[ @@ -2289,6 +2302,7 @@ World:ForEachEntity( ]], }, }, -- AdditionalInfo + Inherits = "cMapManager" }, -- cWorld HTTPFormData = diff --git a/src/Bindings/AllToLua.pkg b/src/Bindings/AllToLua.pkg index dd45a2aab..4fd5a68b8 100644 --- a/src/Bindings/AllToLua.pkg +++ b/src/Bindings/AllToLua.pkg @@ -74,6 +74,7 @@ $cfile "../UI/Window.h" $cfile "../Mobs/Monster.h" $cfile "../CompositeChat.h" $cfile "../Map.h" +$cfile "../MapManager.h" diff --git a/src/MapManager.h b/src/MapManager.h index 05673c694..5da8be035 100644 --- a/src/MapManager.h +++ b/src/MapManager.h @@ -21,11 +21,13 @@ typedef cItemCallback cMapCallback; +// tolua_begin /** Manages the in-game maps of a single world - Thread safe. */ class cMapManager { public: + // tolua_end cMapManager(cWorld * a_World); @@ -43,7 +45,7 @@ public: * Returns true if the map was found and the callback called, false if map not found. * Callback return ignored. */ - bool DoWithMap(unsigned int a_ID, cMapCallback & a_Callback); + bool DoWithMap(unsigned int a_ID, cMapCallback & a_Callback); // tolua_export /** Calls the callback for each map. * @@ -51,7 +53,7 @@ public: */ bool ForEachMap(cMapCallback & a_Callback); - unsigned int GetNumMaps(void) const; + unsigned int GetNumMaps(void) const; // tolua_export /** Loads the map data from the disk */ void LoadMapData(void); @@ -70,7 +72,7 @@ private: cWorld * m_World; -}; +}; // tolua_export From 30b22e9f59e0873be84e80c83d274dbe5353b835 Mon Sep 17 00:00:00 2001 From: andrew Date: Sun, 23 Feb 2014 13:25:02 +0200 Subject: [PATCH 15/16] Manually exported DoWithMap --- MCServer/Plugins/APIDump/APIDesc.lua | 2 +- src/Bindings/ManualBindings.cpp | 4 ++++ src/Map.h | 5 +++++ src/MapManager.cpp | 2 +- src/MapManager.h | 2 +- 5 files changed, 12 insertions(+), 3 deletions(-) diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index 8265b4e19..c8811df66 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -1541,7 +1541,7 @@ a_Player:OpenWindow(Window); ]], Functions = { - DoWithMap = { Params = "ID, Callback", Return = "bool", Notes = "Calls the callback for the map with the specified ID. Returns true if the map was found and the callback called, false if map not found." }, + DoWithMap = { Params = "ID, CallbackFunction, [CallbackData]", Return = "bool", Notes = "If a map with the specified ID exists, calls the CallbackFunction for that map. The CallbackFunction has the following signature:

function Callback({{cMap|Map}}, [CallbackData])
Returns true if the map was found and the callback called, false if map not found." }, GetNumMaps = { Params = "", Return = "number", Notes = "Returns the number of registered maps." }, }, diff --git a/src/Bindings/ManualBindings.cpp b/src/Bindings/ManualBindings.cpp index c220e5e0a..f2d21a682 100644 --- a/src/Bindings/ManualBindings.cpp +++ b/src/Bindings/ManualBindings.cpp @@ -2511,6 +2511,10 @@ void ManualBindings::Bind(lua_State * tolua_S) tolua_function(tolua_S, "UpdateSign", tolua_cWorld_SetSignLines); tolua_endmodule(tolua_S); + tolua_beginmodule(tolua_S, "cMapManager"); + tolua_function(tolua_S, "DoWithMap", tolua_DoWithID); + tolua_endmodule(tolua_S); + tolua_beginmodule(tolua_S, "cPlugin"); tolua_function(tolua_S, "Call", tolua_cPlugin_Call); tolua_endmodule(tolua_S); diff --git a/src/Map.h b/src/Map.h index fc754e6eb..a6df7e5f2 100644 --- a/src/Map.h +++ b/src/Map.h @@ -185,6 +185,11 @@ public: const cColorList & GetData(void) const { return m_Data; } + static const char * GetClassStatic(void) // Needed for ManualBindings's DoWith templates + { + return "cMap"; + } + protected: diff --git a/src/MapManager.cpp b/src/MapManager.cpp index 2fc44ccc8..9d02eafb4 100644 --- a/src/MapManager.cpp +++ b/src/MapManager.cpp @@ -22,7 +22,7 @@ cMapManager::cMapManager(cWorld * a_World) -bool cMapManager::DoWithMap(unsigned int a_ID, cMapCallback & a_Callback) +bool cMapManager::DoWithMap(int a_ID, cMapCallback & a_Callback) { cCSLock Lock(m_CS); cMap * Map = GetMapData(a_ID); diff --git a/src/MapManager.h b/src/MapManager.h index 5da8be035..80e6d16d1 100644 --- a/src/MapManager.h +++ b/src/MapManager.h @@ -45,7 +45,7 @@ public: * Returns true if the map was found and the callback called, false if map not found. * Callback return ignored. */ - bool DoWithMap(unsigned int a_ID, cMapCallback & a_Callback); // tolua_export + bool DoWithMap(int a_ID, cMapCallback & a_Callback); // Exported in ManualBindings.cpp /** Calls the callback for each map. * From f47187394572027cbfa07884cba2f54eaa6972ec Mon Sep 17 00:00:00 2001 From: andrew Date: Sun, 23 Feb 2014 15:03:40 +0200 Subject: [PATCH 16/16] Maps: Improvements --- MCServer/Plugins/APIDump/APIDesc.lua | 4 ++-- src/Items/ItemEmptyMap.h | 2 +- src/Items/ItemMap.h | 2 +- src/Map.cpp | 12 +++++++++--- src/Map.h | 2 +- src/World.cpp | 6 +++--- src/World.h | 9 ++++++--- src/WorldStorage/MapSerializer.h | 6 +++++- 8 files changed, 28 insertions(+), 15 deletions(-) diff --git a/MCServer/Plugins/APIDump/APIDesc.lua b/MCServer/Plugins/APIDump/APIDesc.lua index c8811df66..8e9d239fa 100644 --- a/MCServer/Plugins/APIDump/APIDesc.lua +++ b/MCServer/Plugins/APIDump/APIDesc.lua @@ -2165,7 +2165,8 @@ end GetGeneratorQueueLength = { Params = "", Return = "number", Notes = "Returns the number of chunks that are queued in the chunk generator." }, GetHeight = { Params = "BlockX, BlockZ", Return = "number", Notes = "Returns the maximum height of the particula block column in the world. If the chunk is not loaded, it waits for it to load / generate. WARNING: Do not use, Use TryGetHeight() instead for a non-waiting version, otherwise you run the risk of a deadlock!" }, GetIniFileName = { Params = "", Return = "string", Notes = "Returns the name of the world.ini file that the world uses to store the information." }, - GetLightingQueueLength = { Params = "", Return = "number", Notes = "Returns the number of chunks in the lighting thread's queue." }, + GetLightingQueueLength = { Params = "", Return = "number", Notes = "Returns the number of chunks in the lighting thread's queue." }, + GetMapManager = { Params = "", Return = "{{cMapManager}}", Notes = "Returns the {{cMapManager|MapManager}} object used by this world." }, GetMaxCactusHeight = { Params = "", Return = "number", Notes = "Returns the configured maximum height to which cacti will grow naturally." }, GetMaxSugarcaneHeight = { Params = "", Return = "number", Notes = "Returns the configured maximum height to which sugarcane will grow naturally." }, GetName = { Params = "", Return = "string", Notes = "Returns the name of the world, as specified in the settings.ini file." }, @@ -2302,7 +2303,6 @@ World:ForEachEntity( ]], }, }, -- AdditionalInfo - Inherits = "cMapManager" }, -- cWorld HTTPFormData = diff --git a/src/Items/ItemEmptyMap.h b/src/Items/ItemEmptyMap.h index b06cf9d13..db28511f3 100644 --- a/src/Items/ItemEmptyMap.h +++ b/src/Items/ItemEmptyMap.h @@ -41,7 +41,7 @@ public: 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); + cMap * NewMap = a_World->GetMapManager().CreateMap(CenterX, CenterZ, DEFAULT_SCALE); // Remove empty map from inventory if (!a_Player->GetInventory().RemoveOneEquippedItem()) diff --git a/src/Items/ItemMap.h b/src/Items/ItemMap.h index 9bb16b189..e8ff9da88 100644 --- a/src/Items/ItemMap.h +++ b/src/Items/ItemMap.h @@ -29,7 +29,7 @@ public: virtual void OnUpdate(cWorld * a_World, cPlayer * a_Player, const cItem & a_Item) { - cMap * Map = a_World->GetMapData(a_Item.m_ItemDamage); + cMap * Map = a_World->GetMapManager().GetMapData(a_Item.m_ItemDamage); if (Map == NULL) { diff --git a/src/Map.cpp b/src/Map.cpp index e89fad8b0..2b8c4c74c 100644 --- a/src/Map.cpp +++ b/src/Map.cpp @@ -344,13 +344,19 @@ void cMap::UpdateDecorators(void) -void cMap::AddPlayer(cPlayer * a_Player, cClientHandle * a_Handle, Int64 a_WorldAge) +void cMap::AddPlayer(cPlayer * a_Player, Int64 a_WorldAge) { + cClientHandle * Handle = a_Player->GetClientHandle(); + if (Handle == NULL) + { + return; + } + cMapClient MapClient; MapClient.m_LastUpdate = a_WorldAge; MapClient.m_SendInfo = true; - MapClient.m_Handle = a_Handle; + MapClient.m_Handle = Handle; m_Clients.push_back(MapClient); @@ -470,7 +476,7 @@ void cMap::UpdateClient(cPlayer * a_Player) } // New player, construct a new client state - AddPlayer(a_Player, Handle, WorldAge); + AddPlayer(a_Player, WorldAge); } diff --git a/src/Map.h b/src/Map.h index a6df7e5f2..a313d5431 100644 --- a/src/Map.h +++ b/src/Map.h @@ -226,7 +226,7 @@ private: bool UpdatePixel(unsigned int a_X, unsigned int a_Z); /** Add a new map client. */ - void AddPlayer(cPlayer * a_Player, cClientHandle * a_Handle, Int64 a_WorldAge); + void AddPlayer(cPlayer * a_Player, Int64 a_WorldAge); /** Remove inactive or invalid clients. */ void RemoveInactiveClients(Int64 a_WorldAge); diff --git a/src/World.cpp b/src/World.cpp index 01fdae697..4870e7d6e 100644 --- a/src/World.cpp +++ b/src/World.cpp @@ -233,7 +233,6 @@ void cWorld::cTickThread::Execute(void) // cWorld: cWorld::cWorld(const AString & a_WorldName) : - cMapManager(this), m_WorldName(a_WorldName), m_IniFileName(m_WorldName + "/world.ini"), m_StorageSchema("Default"), @@ -254,6 +253,7 @@ cWorld::cWorld(const AString & a_WorldName) : m_bCommandBlocksEnabled(false), m_bUseChatPrefixes(true), m_Scoreboard(this), + m_MapManager(this), m_GeneratorCallbacks(*this), m_TickThread(*this) { @@ -265,7 +265,7 @@ cWorld::cWorld(const AString & a_WorldName) : cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard); Serializer.Load(); - LoadMapData(); + m_MapManager.LoadMapData(); } @@ -289,7 +289,7 @@ cWorld::~cWorld() cScoreboardSerializer Serializer(m_WorldName, &m_Scoreboard); Serializer.Save(); - SaveMapData(); + m_MapManager.SaveMapData(); delete m_ChunkMap; } diff --git a/src/World.h b/src/World.h index f05ea9b2a..4b74f7aba 100644 --- a/src/World.h +++ b/src/World.h @@ -71,8 +71,7 @@ typedef cItemCallback cMobHeadBlockCallback; class cWorld : public cForEachChunkProvider, public cWorldInterface, - public cBroadcastInterface, - public cMapManager + public cBroadcastInterface { public: @@ -582,9 +581,12 @@ public: /** Returns the name of the world.ini file used by this world */ const AString & GetIniFileName(void) const {return m_IniFileName; } - /** Returns the associated scoreboard instance */ + /** Returns the associated scoreboard instance. */ cScoreboard & GetScoreBoard(void) { return m_Scoreboard; } + /** Returns the associated map manager instance. */ + cMapManager & GetMapManager(void) { return m_MapManager; } + bool AreCommandBlocksEnabled(void) const { return m_bCommandBlocksEnabled; } void SetCommandBlocksEnabled(bool a_Flag) { m_bCommandBlocksEnabled = a_Flag; } @@ -850,6 +852,7 @@ private: cChunkGenerator m_Generator; cScoreboard m_Scoreboard; + cMapManager m_MapManager; /** The callbacks that the ChunkGenerator uses to store new chunks and interface to plugins */ cChunkGeneratorCallbacks m_GeneratorCallbacks; diff --git a/src/WorldStorage/MapSerializer.h b/src/WorldStorage/MapSerializer.h index 85fe917f5..eb7678a08 100644 --- a/src/WorldStorage/MapSerializer.h +++ b/src/WorldStorage/MapSerializer.h @@ -51,7 +51,11 @@ private: -/** Utility class used to serialize item ID counts. */ +/** Utility class used to serialize item ID counts. + * + * In order to perform bounds checking (while loading), + * the last registered ID of each item is serialized to an NBT file. + */ class cIDCountSerializer { public: