diff --git a/VC2008/MCServer.vcproj b/VC2008/MCServer.vcproj index dccdf956..d0c87b76 100644 --- a/VC2008/MCServer.vcproj +++ b/VC2008/MCServer.vcproj @@ -388,6 +388,14 @@ RelativePath="..\source\cHeartBeat.h" > + + + + diff --git a/VC2010/MCServer.vcxproj b/VC2010/MCServer.vcxproj index 6b32cc77..f9eaa781 100644 --- a/VC2010/MCServer.vcxproj +++ b/VC2010/MCServer.vcxproj @@ -338,6 +338,7 @@ + @@ -576,6 +577,7 @@ + diff --git a/VC2010/MCServer.vcxproj.filters b/VC2010/MCServer.vcxproj.filters index f85d0ba4..eadf6b09 100644 --- a/VC2010/MCServer.vcxproj.filters +++ b/VC2010/MCServer.vcxproj.filters @@ -951,6 +951,7 @@ cBlockEntity\cNoteEntity + @@ -1663,6 +1664,7 @@ cBlockEntity\cNoteEntity + diff --git a/source/ChunkDataSerializer.cpp b/source/ChunkDataSerializer.cpp new file mode 100644 index 00000000..1d798beb --- /dev/null +++ b/source/ChunkDataSerializer.cpp @@ -0,0 +1,118 @@ + +// ChunkDataSerializer.cpp + +// Implements the cChunkDataSerializer class representing the object that can: +// - serialize chunk data to different protocol versions +// - cache such serialized data for multiple clients + +#include "Globals.h" +#include "ChunkDataSerializer.h" +#include "zlib.h" + + + + +cChunkDataSerializer::cChunkDataSerializer( + const cChunkDef::BlockTypes & a_BlockTypes, + const cChunkDef::BlockNibbles & a_BlockMetas, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_BlockSkyLight, + const unsigned char * a_BiomeData +) : + m_BlockTypes(a_BlockTypes), + m_BlockMetas(a_BlockMetas), + m_BlockLight(a_BlockLight), + m_BlockSkyLight(a_BlockSkyLight), + m_BiomeData(a_BiomeData) +{ +} + + + + +const AString & cChunkDataSerializer::Serialize(int a_Version) +{ + Serializations::const_iterator itr = m_Serializations.find(a_Version); + if (itr != m_Serializations.end()) + { + return itr->second; + } + + AString data; + switch (a_Version) + { + case RELEASE_1_2_5: Serialize29(data); break; + // TODO: Other protocol versions may serialize the data differently; implement here + + default: + { + LOGERROR("cChunkDataSerializer::Serialize(): Unknown version: %d", a_Version); + ASSERT(!"Unknown chunk data serialization version"); + break; + } + } + m_Serializations[a_Version] = data; + return m_Serializations[a_Version]; +} + + + + + +void cChunkDataSerializer::Serialize29(AString & a_Data) +{ + // TODO: Do not copy data and then compress it; rather, compress partial blocks of data (zlib *can* stream) + + const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; + const int MetadataOffset = sizeof(m_BlockTypes); + const int BlockLightOffset = MetadataOffset + sizeof(m_BlockMetas); + const int SkyLightOffset = BlockLightOffset + sizeof(m_BlockLight); + const int BiomeOffset = SkyLightOffset + sizeof(m_BlockSkyLight); + const int DataSize = BiomeOffset + BiomeDataSize; + + // Temporary buffer for the composed data: + char AllData [DataSize]; + + memcpy(AllData, m_BlockTypes, sizeof(m_BlockTypes)); + memcpy(AllData + MetadataOffset, m_BlockMetas, sizeof(m_BlockMetas)); + memcpy(AllData + BlockLightOffset, m_BlockLight, sizeof(m_BlockLight)); + memcpy(AllData + SkyLightOffset, m_BlockSkyLight, sizeof(m_BlockSkyLight)); + memcpy(AllData + BiomeOffset, m_BiomeData, BiomeDataSize); + + // Compress the data: + // In order not to use allocation, use a fixed-size buffer, with the size + // that uses the same calculation as compressBound(): + const uLongf CompressedMaxSize = DataSize + (DataSize >> 12) + (DataSize >> 14) + (DataSize >> 25) + 16; + char CompressedBlockData[CompressedMaxSize]; + + uLongf CompressedSize = compressBound(DataSize); + + // Run-time check that our compile-time guess about CompressedMaxSize was enough: + ASSERT(CompressedSize <= CompressedMaxSize); + + compress2((Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION); + + // Now put all those data into a_Data: + + // "Ground-up continuous", or rather, "biome data present" flag: + a_Data.push_back('\xff'); + + // Two bitmaps; we're aways sending the full chunk with no additional data, so the bitmaps are 0xffff and 0, respectively + // Also, no endian flipping is needed because of the const values + unsigned short BitMap1 = 0xffff; + unsigned short BitMap2 = 0; + a_Data.append((const char *)&BitMap1, sizeof(short)); + a_Data.append((const char *)&BitMap2, sizeof(short)); + + Int32 CompressedSizeBE = htonl(CompressedSize); + a_Data.append((const char *)&CompressedSizeBE, sizeof(CompressedSizeBE)); + + Int32 UnusedInt32 = 0; + a_Data.append((const char *)&UnusedInt32, sizeof(UnusedInt32)); + + a_Data.append(CompressedBlockData, CompressedSize); +} + + + + diff --git a/source/ChunkDataSerializer.h b/source/ChunkDataSerializer.h new file mode 100644 index 00000000..4c2f4fb4 --- /dev/null +++ b/source/ChunkDataSerializer.h @@ -0,0 +1,46 @@ + +// ChunkDataSerializer.h + +// Interfaces to the cChunkDataSerializer class representing the object that can: +// - serialize chunk data to different protocol versions +// - cache such serialized data for multiple clients + + + + + +class cChunkDataSerializer +{ +protected: + const cChunkDef::BlockTypes & m_BlockTypes; + const cChunkDef::BlockNibbles & m_BlockMetas; + const cChunkDef::BlockNibbles & m_BlockLight; + const cChunkDef::BlockNibbles & m_BlockSkyLight; + const unsigned char * m_BiomeData; + + typedef std::map Serializations; + + Serializations m_Serializations; + + void Serialize29(AString & a_Data); // Release 1.2.4 and 1.2.5 + +public: + enum + { + RELEASE_1_2_5 = 29, + } ; + + cChunkDataSerializer( + const cChunkDef::BlockTypes & a_BlockTypes, + const cChunkDef::BlockNibbles & a_BlockMetas, + const cChunkDef::BlockNibbles & a_BlockLight, + const cChunkDef::BlockNibbles & a_BlockSkyLight, + const unsigned char * a_BiomeData + ); + + const AString & Serialize(int a_Version); // Returns one of the internal m_Serializations[] +} ; + + + + diff --git a/source/ChunkSender.cpp b/source/ChunkSender.cpp index cefc6458..25fb94e1 100644 --- a/source/ChunkSender.cpp +++ b/source/ChunkSender.cpp @@ -10,9 +10,8 @@ #include "Globals.h" #include "ChunkSender.h" #include "cWorld.h" -#include "packets/cPacket_MapChunk.h" -#include "packets/cPacket_PreChunk.h" #include "cBlockEntity.h" +#include "ChunkDataSerializer.h" @@ -217,24 +216,21 @@ void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHa return; } - // Prepare MapChunk packets: + // Query and prepare chunk data: if( !m_World->GetChunkData(a_ChunkX, a_ChunkY, a_ChunkZ, *this) ) { return; } - cPacket_PreChunk PreChunk(a_ChunkX, a_ChunkZ, true); - cPacket_MapChunk MapChunk(a_ChunkX, a_ChunkY, a_ChunkZ, (BLOCKTYPE *)m_BlockData, m_BiomeMap); + cChunkDataSerializer Data(m_BlockTypes, m_BlockMetas, m_BlockLight, m_BlockSkyLight, m_BiomeMap); // Send: if (a_Client == NULL) { - m_World->BroadcastToChunk(a_ChunkX, a_ChunkY, a_ChunkZ, PreChunk); - m_World->BroadcastToChunk(a_ChunkX, a_ChunkY, a_ChunkZ, MapChunk); + m_World->BroadcastChunkData(a_ChunkX, a_ChunkZ, Data); } else { - a_Client->Send(PreChunk); - a_Client->Send(MapChunk); + a_Client->SendChunkData(a_ChunkX, a_ChunkZ, Data); } // Send block-entity packets: diff --git a/source/ChunkSender.h b/source/ChunkSender.h index b1b276a2..ac5e8994 100644 --- a/source/ChunkSender.h +++ b/source/ChunkSender.h @@ -68,7 +68,7 @@ public: class cChunkSender: public cIsThread, - public cChunkDataCollector + public cChunkDataSeparateCollector { typedef cIsThread super; public: diff --git a/source/cChunk.cpp b/source/cChunk.cpp index c479a7bc..aec75cd4 100644 --- a/source/cChunk.cpp +++ b/source/cChunk.cpp @@ -1936,6 +1936,22 @@ void cChunk::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, cons +void cChunk::BroadcastChunkData(cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) +{ + for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr ) + { + if (*itr == a_Exclude) + { + continue; + } + (*itr)->SendChunkData(m_PosX, m_PosZ, a_Serializer); + } // for itr - LoadedByClient[] +} + + + + + void cChunk::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) { // We can operate on entity pointers, we're inside the ChunkMap's CS lock which guards the list diff --git a/source/cChunk.h b/source/cChunk.h index 714a1445..9539b20d 100644 --- a/source/cChunk.h +++ b/source/cChunk.h @@ -43,6 +43,7 @@ class cFurnaceEntity; class cBlockArea; class cPawn; class cPickup; +class cChunkDataSerializer; typedef std::list cClientHandleList; typedef cItemCallback cEntityCallback; @@ -191,6 +192,7 @@ public: void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); void BroadcastCollectPickup (const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); + void BroadcastChunkData (cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); void SendBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, cClientHandle & a_Client); diff --git a/source/cChunkMap.cpp b/source/cChunkMap.cpp index 16e25086..d46ee36f 100644 --- a/source/cChunkMap.cpp +++ b/source/cChunkMap.cpp @@ -485,6 +485,22 @@ void cChunkMap::BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, c +void cChunkMap::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) +{ + cCSLock Lock(m_CSLayers); + cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, 0, a_ChunkZ); + if (Chunk == NULL) + { + return; + } + // It's perfectly legal to broadcast packets even to invalid chunks! + Chunk->BroadcastChunkData(a_Serializer, a_Exclude); +} + + + + + void cChunkMap::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) { cCSLock Lock(m_CSLayers); diff --git a/source/cChunkMap.h b/source/cChunkMap.h index 95591c5b..3af00b13 100644 --- a/source/cChunkMap.h +++ b/source/cChunkMap.h @@ -22,6 +22,7 @@ class cChestEntity; class cFurnaceEntity; class cPawn; class cPickup; +class cChunkDataSerializer; typedef std::list cClientHandleList; typedef cChunk * cChunkPtr; @@ -81,6 +82,8 @@ public: void BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = NULL); void BroadcastThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); + + void BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); /// Broadcasts the block entity, if it is at the coords specified, to all clients except a_Exclude void BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude); diff --git a/source/cClientHandle.cpp b/source/cClientHandle.cpp index 5cfd0a51..21d1f172 100644 --- a/source/cClientHandle.cpp +++ b/source/cClientHandle.cpp @@ -27,6 +27,7 @@ #include "cTimer.h" #include "items/Item.h" #include "blocks/Block.h" +#include "ChunkDataSerializer.h" #include "cTracer.h" #include "Vector3f.h" @@ -2025,6 +2026,21 @@ void cClientHandle::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ) +void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer) +{ + // Send the pre-chunk: + cPacket_PreChunk pre(a_ChunkX, a_ChunkZ, true); + Send(pre); + + // Send the data: + cPacket_MapChunk mc(a_ChunkX, a_ChunkZ, a_Serializer.Serialize(cChunkDataSerializer::RELEASE_1_2_5)); + Send(mc); +} + + + + + void cClientHandle::CheckIfWorldDownloaded(void) { if (m_State != csDownloadingWorld) diff --git a/source/cClientHandle.h b/source/cClientHandle.h index 91a886c3..1c7cfa75 100644 --- a/source/cClientHandle.h +++ b/source/cClientHandle.h @@ -36,6 +36,7 @@ class cWindow; class cPawn; class cPickup; class cMonster; +class cChunkDataSerializer; @@ -121,6 +122,7 @@ public: void SendWeather(eWeather a_Weather); void SendTimeUpdate(Int64 a_WorldTime); void SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ); + void SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer); const AString & GetUsername(void) const; //tolua_export diff --git a/source/cWorld.cpp b/source/cWorld.cpp index e5462fd9..8eb4530a 100644 --- a/source/cWorld.cpp +++ b/source/cWorld.cpp @@ -1438,6 +1438,15 @@ void cWorld::BroadcastTimeUpdate(const cClientHandle * a_Exclude) +void cWorld::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude) +{ + m_ChunkMap->BroadcastChunkData(a_ChunkX, a_ChunkZ, a_Serializer, a_Exclude); +} + + + + + void cWorld::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude) { m_ChunkMap->BroadcastBlockEntity(a_BlockX, a_BlockY, a_BlockZ, a_Exclude); diff --git a/source/cWorld.h b/source/cWorld.h index 5b4cf956..f168ab18 100644 --- a/source/cWorld.h +++ b/source/cWorld.h @@ -95,6 +95,7 @@ public: void BroadcastWeather (eWeather a_Weather, const cClientHandle * a_Exclude = NULL); void BroadcastThunderbolt (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); void BroadcastTimeUpdate (const cClientHandle * a_Exclude = NULL); + void BroadcastChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = NULL); /// If there is a block entity at the specified coods, sends it to all clients except a_Exclude void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = NULL); diff --git a/source/packets/cPacket_MapChunk.cpp b/source/packets/cPacket_MapChunk.cpp index 21c62da4..17fbdba2 100644 --- a/source/packets/cPacket_MapChunk.cpp +++ b/source/packets/cPacket_MapChunk.cpp @@ -4,131 +4,16 @@ #include "cPacket_MapChunk.h" #include "../ChunkDef.h" -#include "zlib.h" - -cPacket_MapChunk::~cPacket_MapChunk() -{ - delete [] m_CompressedData; -} - - - - - -cPacket_MapChunk::cPacket_MapChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, const BLOCKTYPE * a_BlockData, const unsigned char * a_BiomeData) +cPacket_MapChunk::cPacket_MapChunk(int a_ChunkX, int a_ChunkZ, const AString & a_SerializedData) : + m_PosX(a_ChunkX), + m_PosZ(a_ChunkZ), + m_SerializedData(a_SerializedData) { m_PacketID = E_MAP_CHUNK; - - m_PosX = a_ChunkX; // Chunk coordinates now, instead of block coordinates - m_PosZ = a_ChunkZ; - - m_bContiguous = true; // false = no biome data, true = with biome data - m_BitMap1 = 0; - m_BitMap2 = 0; - - m_UnusedInt = 0; - - - const int BlockDataSize = (cChunkDef::Height / 16) * (4096 + 2048 + 2048 + 2048); - const int BiomeDataSize = cChunkDef::Width * cChunkDef::Width; - char AllData [ BlockDataSize + BiomeDataSize ]; - -#if AXIS_ORDER == AXIS_ORDER_YZX - memset( AllData, 0, BlockDataSize ); - - unsigned int iterator = 0; - for ( int i = 0; i < (cChunkDef::Height / 16); ++i ) - { - m_BitMap1 |= (1 << i); // This tells what chunks are sent. Use this to NOT send air only chunks (right now everything is sent) - for ( int y = 0; y < 16; ++y ) for( int z = 0; z < 16; ++z ) for( int x = 0; x < 16; ++x ) - { - int idx = cChunk::MakeIndex(x, y + i * 16, z); - AllData[iterator] = a_BlockData[idx]; - ++iterator; - } // for y, z, x - } - - // Send block metadata: - char * Meta = a_BlockData + cChunkDef::NumBlocks; - for ( int i = 0; i < (cChunkDef::Height / 16); ++i ) - { - for ( int y = 0; y < 16; ++y ) for( int z = 0; z < 16; ++z ) - { - for ( int x = 0; x < 8; ++x ) - { - AllData[iterator] = cChunk::GetNibble(Meta, x * 2 + 0, y + i * 16, z) | (cChunk::GetNibble(Meta, x * 2 + 1, y + i * 16, z ) << 4); - ++iterator; - } // for x - } // for y, z - } - - // Send block light: - char * Light = Meta + cChunkDef::NumBlocks / 2; - for ( int i = 0; i < (cChunkDef::Height / 16); ++i ) - { - for ( int y = 0; y < 16; ++y ) for( int z = 0; z < 16; ++z ) - { - for ( int x = 0; x < 8; ++x ) - { - AllData[iterator] = cChunk::GetNibble(Light, x * 2 + 0, y + i * 16, z ) | (cChunk::GetNibble(Light, x * 2 + 1, y + i * 16, z ) << 4); - ++iterator; - } - } - } - - // Send sky light: - char * SkyLight = Light + cChunkDef::NumBlocks / 2; - for( int i = 0; i < (cChunkDef::Height/16); ++i ) - { - for( int y = 0; y < 16; ++y ) for( int z = 0; z < 16; ++z ) - { - for( int x = 0; x < 8; ++x ) - { - AllData[iterator] = cChunk::GetNibble(SkyLight, x * 2 + 0, y + i * 16, z ) | (cChunk::GetNibble(SkyLight, x * 2 + 1, y + i * 16, z ) << 4); - ++iterator; - } - } - } - memcpy(AllData + BlockDataSize, a_BiomeData, BiomeDataSize); -#elif AXIS_ORDER == AXIS_ORDER_XZY - for ( int i = 0; i < 16; ++i ) - { - m_BitMap1 |= (1 << i); - } - memcpy(AllData, a_BlockData, BlockDataSize); - memcpy(AllData + BlockDataSize, a_BiomeData, BiomeDataSize); -#endif // AXIS_ORDER - - uLongf CompressedSize = compressBound( sizeof(AllData) ); - char * CompressedBlockData = new char[CompressedSize]; - - compress2( (Bytef*)CompressedBlockData, &CompressedSize, (const Bytef*)AllData, sizeof(AllData), Z_DEFAULT_COMPRESSION); - - m_CompressedData = CompressedBlockData; - m_CompressedSize = CompressedSize; -} - - - - - -cPacket_MapChunk::cPacket_MapChunk( const cPacket_MapChunk & a_Copy ) -{ - m_PacketID = E_MAP_CHUNK; - - m_PosX = a_Copy.m_PosX; - m_PosZ = a_Copy.m_PosZ; - m_bContiguous = a_Copy.m_bContiguous; - m_BitMap1 = a_Copy.m_BitMap1; - m_BitMap2 = a_Copy.m_BitMap2; - - m_CompressedSize = a_Copy.m_CompressedSize; - m_CompressedData = new char[m_CompressedSize]; - memcpy( m_CompressedData, a_Copy.m_CompressedData, m_CompressedSize ); } @@ -141,12 +26,7 @@ void cPacket_MapChunk::Serialize(AString & a_Data) const AppendInteger(a_Data, m_PosX); AppendInteger(a_Data, m_PosZ); - AppendBool (a_Data, m_bContiguous); - AppendShort (a_Data, m_BitMap1); - AppendShort (a_Data, m_BitMap2); - AppendInteger(a_Data, m_CompressedSize); - AppendInteger(a_Data, m_UnusedInt); - AppendData (a_Data, m_CompressedData, m_CompressedSize); + a_Data.append(m_SerializedData); } diff --git a/source/packets/cPacket_MapChunk.h b/source/packets/cPacket_MapChunk.h index ec2f6863..35b5de08 100644 --- a/source/packets/cPacket_MapChunk.h +++ b/source/packets/cPacket_MapChunk.h @@ -15,29 +15,18 @@ public: cPacket_MapChunk() : m_PosX( 0 ) , m_PosZ( 0 ) - , m_bContiguous( false ) - , m_BitMap1( 0 ) - , m_BitMap2( 0 ) - , m_CompressedSize( 0 ) - , m_UnusedInt( 0 ) - , m_CompressedData( NULL ) - { m_PacketID = E_MAP_CHUNK; } + { + m_PacketID = E_MAP_CHUNK; + } - cPacket_MapChunk( const cPacket_MapChunk & a_Copy ); - cPacket_MapChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, const BLOCKTYPE * a_BlockData, const unsigned char * a_BiomeData); - ~cPacket_MapChunk(); + cPacket_MapChunk(int a_ChunkX, int a_ChunkZ, const AString & a_SerializedData); virtual cPacket* Clone() const { return new cPacket_MapChunk(*this); } virtual void Serialize(AString & a_Data) const override; - int m_PosX; - int m_PosZ; - bool m_bContiguous; - short m_BitMap1; - short m_BitMap2; - int m_CompressedSize; - int m_UnusedInt; - char * m_CompressedData; + int m_PosX; + int m_PosZ; + AString m_SerializedData; };