From bfe52277b4a17c4a3a97bf479e20d7b5f0f068d2 Mon Sep 17 00:00:00 2001 From: tycho Date: Sun, 31 May 2015 15:28:38 +0100 Subject: [PATCH] Rewrote ChunkSending queue for significantly improved performance --- MCServer/Plugins/MagicCarpet | 2 +- src/BlockEntities/BlockEntity.h | 1 + src/ChunkMap.h | 2 +- src/ChunkSender.cpp | 217 +++++++++++--------------------- src/ChunkSender.h | 71 ++++------- 5 files changed, 103 insertions(+), 190 deletions(-) diff --git a/MCServer/Plugins/MagicCarpet b/MCServer/Plugins/MagicCarpet index 94da343b6..493f2dfa6 160000 --- a/MCServer/Plugins/MagicCarpet +++ b/MCServer/Plugins/MagicCarpet @@ -1 +1 @@ -Subproject commit 94da343b62f0498a5843247f36d6ee00cbeb8f21 +Subproject commit 493f2dfa6d39f134e37c4c614cf8d6ffd10c825f diff --git a/src/BlockEntities/BlockEntity.h b/src/BlockEntities/BlockEntity.h index 85f75a523..71367efb6 100644 --- a/src/BlockEntities/BlockEntity.h +++ b/src/BlockEntities/BlockEntity.h @@ -85,6 +85,7 @@ public: // tolua_begin // Position, in absolute block coordinates: + Vector3i GetPos(void) const { return Vector3i{m_PosX, m_PosY, m_PosZ}; } int GetPosX(void) const { return m_PosX; } int GetPosY(void) const { return m_PosY; } int GetPosZ(void) const { return m_PosZ; } diff --git a/src/ChunkMap.h b/src/ChunkMap.h index 64e83f3f6..916a3433d 100644 --- a/src/ChunkMap.h +++ b/src/ChunkMap.h @@ -302,7 +302,7 @@ public: Returns false if there's no command block at those coords or callback returns true, returns true if found. */ bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback); // Lua-accessible - /** Calls the callback for the mob head block at the specified coords.chu + /** Calls the callback for the mob head block at the specified coords. Returns false if there's no mob head block at those coords or callback returns true, returns true if found. */ bool DoWithMobHeadAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadCallback & a_Callback); // Lua-accessible diff --git a/src/ChunkSender.cpp b/src/ChunkSender.cpp index 1a3d234cc..43b65014a 100644 --- a/src/ChunkSender.cpp +++ b/src/ChunkSender.cpp @@ -18,6 +18,7 @@ + //////////////////////////////////////////////////////////////////////////////// // cNotifyChunkSender: @@ -93,41 +94,25 @@ void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a { ASSERT(a_Client != nullptr); { - sSendChunk Chunk(a_ChunkX, a_ChunkZ, a_Client); - + cChunkCoords Chunk{a_ChunkX, a_ChunkZ}; cCSLock Lock(m_CS); - if ( - std::find(m_SendChunksLowPriority.begin(), m_SendChunksLowPriority.end(), Chunk) != m_SendChunksLowPriority.end() || - std::find(m_SendChunksMediumPriority.begin(), m_SendChunksMediumPriority.end(), Chunk) != m_SendChunksMediumPriority.end() || - std::find(m_SendChunksHighPriority.begin(), m_SendChunksHighPriority.end(), Chunk) != m_SendChunksHighPriority.end() - ) + auto iter = m_ChunkInfo.find(Chunk); + if (iter != m_ChunkInfo.end()) { - // Already queued, bail out - return; + auto & info = iter->second; + if (info.m_Priority > a_Priority) + { + m_SendChunks.push(sChunkQueue{a_Priority, Chunk}); + info.m_Priority = a_Priority; + } + info.m_Clients.insert(a_Client); } - - switch (a_Priority) + else { - case E_CHUNK_PRIORITY_LOW: - { - m_SendChunksLowPriority.push_back(Chunk); - break; - } - case E_CHUNK_PRIORITY_MEDIUM: - { - m_SendChunksMediumPriority.push_back(Chunk); - break; - } - case E_CHUNK_PRIORITY_HIGH: - { - m_SendChunksHighPriority.push_back(Chunk); - break; - } - case PRIORITY_BROADCAST: - { - m_SendChunksBroadcastPriority.push_back(Chunk); - break; - } + m_SendChunks.push(sChunkQueue{a_Priority, Chunk}); + auto info = sSendChunk{Chunk, a_Priority}; + info.m_Clients.insert(a_Client); + m_ChunkInfo.emplace(Chunk, info); } } m_evtQueue.Set(); @@ -141,43 +126,25 @@ void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, std::list a_Clients) { { + cChunkCoords Chunk{a_ChunkX, a_ChunkZ}; cCSLock Lock(m_CS); - for (auto client : a_Clients) + auto iter = m_ChunkInfo.find(Chunk); + if (iter != m_ChunkInfo.end()) { - sSendChunk Chunk(a_ChunkX, a_ChunkZ, client); - if ( - std::find(m_SendChunksLowPriority.begin(), m_SendChunksLowPriority.end(), Chunk) != m_SendChunksLowPriority.end() || - std::find(m_SendChunksMediumPriority.begin(), m_SendChunksMediumPriority.end(), Chunk) != m_SendChunksMediumPriority.end() || - std::find(m_SendChunksHighPriority.begin(), m_SendChunksHighPriority.end(), Chunk) != m_SendChunksHighPriority.end() - ) + auto & info = iter->second; + if (info.m_Priority > a_Priority) { - // Already queued, bail out - continue; - } - - switch (a_Priority) - { - case E_CHUNK_PRIORITY_LOW: - { - m_SendChunksLowPriority.push_back(Chunk); - break; - } - case E_CHUNK_PRIORITY_MEDIUM: - { - m_SendChunksMediumPriority.push_back(Chunk); - break; - } - case E_CHUNK_PRIORITY_HIGH: - { - m_SendChunksHighPriority.push_back(Chunk); - break; - } - case PRIORITY_BROADCAST: - { - m_SendChunksBroadcastPriority.push_back(Chunk); - break; - } + m_SendChunks.push(sChunkQueue{a_Priority, Chunk}); + info.m_Priority = a_Priority; } + info.m_Clients.insert(a_Clients.begin(), a_Clients.end()); + } + else + { + m_SendChunks.push(sChunkQueue{a_Priority, Chunk}); + auto info = sSendChunk{Chunk, a_Priority}; + info.m_Clients.insert(a_Clients.begin(), a_Clients.end()); + m_ChunkInfo.emplace(Chunk, info); } } m_evtQueue.Set(); @@ -191,33 +158,12 @@ void cChunkSender::RemoveClient(cClientHandle * a_Client) { { cCSLock Lock(m_CS); - for (sSendChunkList::iterator itr = m_SendChunksLowPriority.begin(); itr != m_SendChunksLowPriority.end();) + for (auto && pair : m_ChunkInfo) { - if (itr->m_Client == a_Client) - { - itr = m_SendChunksLowPriority.erase(itr); - continue; - } - ++itr; - } // for itr - m_SendChunksLowPriority[] - for (sSendChunkList::iterator itr = m_SendChunksMediumPriority.begin(); itr != m_SendChunksMediumPriority.end();) - { - if (itr->m_Client == a_Client) - { - itr = m_SendChunksMediumPriority.erase(itr); - continue; - } - ++itr; - } // for itr - m_SendChunksMediumPriority[] - for (sSendChunkList::iterator itr = m_SendChunksHighPriority.begin(); itr != m_SendChunksHighPriority.end();) - { - if (itr->m_Client == a_Client) - { - itr = m_SendChunksHighPriority.erase(itr); - continue; - } - ++itr; - } // for itr - m_SendChunksHighPriority[] + auto && clients = pair.second.m_Clients; + clients.erase(a_Client); // nop for sets that do not contain a_Client + } + m_RemoveCount++; } m_evtQueue.Set(); @@ -233,7 +179,7 @@ void cChunkSender::Execute(void) while (!m_ShouldTerminate) { cCSLock Lock(m_CS); - while (m_SendChunksBroadcastPriority.empty() && m_SendChunksLowPriority.empty() && m_SendChunksMediumPriority.empty() && m_SendChunksHighPriority.empty()) + do { int RemoveCount = m_RemoveCount; m_RemoveCount = 0; @@ -247,52 +193,24 @@ void cChunkSender::Execute(void) { return; } - } // while (empty) + } while (m_SendChunks.empty()); - if (!m_SendChunksHighPriority.empty()) + // Take one from the queue: + auto Chunk = m_SendChunks.top().m_Chunk; + m_SendChunks.pop(); + auto itr = m_ChunkInfo.find(Chunk); + if (itr == m_ChunkInfo.end()) { - // Take one from the queue: - sSendChunk Chunk(m_SendChunksHighPriority.front()); - m_SendChunksHighPriority.pop_front(); - Lock.Unlock(); - - SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, Chunk.m_Client); + continue; } - else if (!m_SendChunksBroadcastPriority.empty()) - { - // Take one from the queue: - sSendChunk Chunk(m_SendChunksBroadcastPriority.front()); - m_SendChunksBroadcastPriority.pop_front(); - Lock.Unlock(); + + std::unordered_set clients; + std::swap(itr->second.m_Clients, clients); + m_ChunkInfo.erase(itr); - SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, Chunk.m_Client); - } - else if (!m_SendChunksMediumPriority.empty()) - { - // Take one from the queue: - sSendChunk Chunk(m_SendChunksMediumPriority.front()); - m_SendChunksMediumPriority.pop_front(); - Lock.Unlock(); - - SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, Chunk.m_Client); - } - else - { - // Take one from the queue: - sSendChunk Chunk(m_SendChunksLowPriority.front()); - m_SendChunksLowPriority.pop_front(); - Lock.Unlock(); - - SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, Chunk.m_Client); - } - Lock.Lock(); - int RemoveCount = m_RemoveCount; - m_RemoveCount = 0; Lock.Unlock(); - for (int i = 0; i < RemoveCount; i++) - { - m_evtRemoved.Set(); // Notify that the removed clients are safe to be deleted - } + + SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, clients); } // while (!mShouldTerminate) } @@ -300,13 +218,22 @@ void cChunkSender::Execute(void) -void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) +void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, std::unordered_set a_Clients) { // Ask the client if it still wants the chunk: - if (!a_Client->WantsSendChunk(a_ChunkX, a_ChunkZ)) + for(auto itr = a_Clients.begin(); itr != a_Clients.end();) { - return; + if(!(*itr)->WantsSendChunk(a_ChunkX, a_ChunkZ)) + { + auto toremove = itr; + itr++; + a_Clients.erase(toremove); + } + else + { + itr++; + } } // If the chunk has no clients, no need to packetize it: @@ -335,14 +262,18 @@ void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Clien } cChunkDataSerializer Data(m_BlockTypes, m_BlockMetas, m_BlockLight, m_BlockSkyLight, m_BiomeMap); - // Send: - a_Client->SendChunkData(a_ChunkX, a_ChunkZ, Data); - - // Send block-entity packets: - for (sBlockCoords::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + for (auto client : a_Clients) { - m_World.SendBlockEntity(itr->m_BlockX, itr->m_BlockY, itr->m_BlockZ, *a_Client); - } // for itr - m_Packets[] + // Send: + client->SendChunkData(a_ChunkX, a_ChunkZ, Data); + + // Send block-entity packets: + for (auto Pos : m_BlockEntities) + { + m_World.SendBlockEntity(Pos.x, Pos.y, Pos.z, *client); + } // for itr - m_Packets[] + + } m_BlockEntities.clear(); // TODO: Send entity spawn packets @@ -354,7 +285,7 @@ void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Clien void cChunkSender::BlockEntity(cBlockEntity * a_Entity) { - m_BlockEntities.push_back(sBlockCoord(a_Entity->GetPosX(), a_Entity->GetPosY(), a_Entity->GetPosZ())); + m_BlockEntities.push_back(a_Entity->GetPos()); } diff --git a/src/ChunkSender.h b/src/ChunkSender.h index 1f87e9e08..b0c48b92b 100644 --- a/src/ChunkSender.h +++ b/src/ChunkSender.h @@ -29,6 +29,9 @@ Note that it may be called by world's BroadcastToChunk() if the client is still #include "ChunkDef.h" #include "ChunkDataCallback.h" +#include +#include + @@ -59,9 +62,10 @@ public: enum eChunkPriority { E_CHUNK_PRIORITY_HIGH = 0, - E_CHUNK_PRIORITY_MEDIUM = 1, - E_CHUNK_PRIORITY_LOW = 2, - PRIORITY_BROADCAST + PRIORITY_BROADCAST, + E_CHUNK_PRIORITY_MEDIUM, + E_CHUNK_PRIORITY_LOW, + }; bool Start(); @@ -76,62 +80,40 @@ public: void RemoveClient(cClientHandle * a_Client); protected: + + struct sChunkQueue + { + eChunkPriority m_Priority; + cChunkCoords m_Chunk; + + bool operator <(const sChunkQueue & a_Other) const { return this->m_Priority < a_Other.m_Priority; } + }; /// Used for sending chunks to specific clients struct sSendChunk { - int m_ChunkX; - int m_ChunkZ; - cClientHandle * m_Client; - - sSendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) : - m_ChunkX(a_ChunkX), - m_ChunkZ(a_ChunkZ), - m_Client(a_Client) + cChunkCoords m_Chunk; + std::unordered_set m_Clients; + eChunkPriority m_Priority; + sSendChunk(cChunkCoords a_Chunk, eChunkPriority a_Priority) : + m_Chunk(a_Chunk), + m_Priority(a_Priority) { } - - bool operator ==(const sSendChunk & a_Other) - { - return ( - (a_Other.m_ChunkX == m_ChunkX) && - (a_Other.m_ChunkZ == m_ChunkZ) && - (a_Other.m_Client == m_Client) - ); - } - } ; - typedef std::list sSendChunkList; - - struct sBlockCoord - { - int m_BlockX; - int m_BlockY; - int m_BlockZ; - - sBlockCoord(int a_BlockX, int a_BlockY, int a_BlockZ) : - m_BlockX(a_BlockX), - m_BlockY(a_BlockY), - m_BlockZ(a_BlockZ) - { - } - } ; - - typedef std::vector sBlockCoords; + }; cWorld & m_World; cCriticalSection m_CS; - sSendChunkList m_SendChunksLowPriority; - sSendChunkList m_SendChunksMediumPriority; - sSendChunkList m_SendChunksBroadcastPriority; - sSendChunkList m_SendChunksHighPriority; + std::priority_queue m_SendChunks; + std::unordered_map m_ChunkInfo; cEvent m_evtQueue; // Set when anything is added to m_ChunksReady cEvent m_evtRemoved; // Set when removed clients are safe to be deleted int m_RemoveCount; // Number of threads waiting for a client removal (m_evtRemoved needs to be set this many times) // Data about the chunk that is being sent: // NOTE that m_BlockData[] is inherited from the cChunkDataCollector unsigned char m_BiomeMap[cChunkDef::Width * cChunkDef::Width]; - sBlockCoords m_BlockEntities; // Coords of the block entities to send + std::vector m_BlockEntities; // Coords of the block entities to send // TODO: sEntityIDs m_Entities; // Entity-IDs of the entities to send // cIsThread override: @@ -144,9 +126,8 @@ protected: virtual void BlockEntity (cBlockEntity * a_Entity) override; /// Sends the specified chunk to a_Client, or to all chunk clients if a_Client == nullptr - void SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client); + void SendChunk(int a_ChunkX, int a_ChunkZ, std::unordered_set a_Clients); } ; -