From 8cdd63f06c692f117088909ea5c9b950bba34376 Mon Sep 17 00:00:00 2001 From: "madmaxoft@gmail.com" Date: Tue, 6 Mar 2012 14:52:44 +0000 Subject: Fixed rev368's ChunkSender, now sends properly even chunks that are loaded. Fixed a deadlock in cClientHandle vs TickThread over cClientHandle::m_CSChunkLists git-svn-id: http://mc-server.googlecode.com/svn/trunk@371 0a769ca7-a7f5-676a-18bf-c427514a06d6 --- source/ChunkSender.cpp | 113 ++++++++++++++++++++++++++++++++++++++--------- source/ChunkSender.h | 29 ++++++++++++ source/cClientHandle.cpp | 15 +++++-- source/cWorld.cpp | 18 ++++++++ source/cWorld.h | 6 +++ 5 files changed, 155 insertions(+), 26 deletions(-) diff --git a/source/ChunkSender.cpp b/source/ChunkSender.cpp index 527db4543..47d84d166 100644 --- a/source/ChunkSender.cpp +++ b/source/ChunkSender.cpp @@ -62,12 +62,44 @@ void cChunkSender::ChunkReady(int a_ChunkX, int a_ChunkY, int a_ChunkZ) +void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) +{ + ASSERT(a_Client != NULL); + { + cCSLock Lock(m_CS); + m_SendChunks.push_back(sSendChunk(a_ChunkX, a_ChunkY, a_ChunkZ, a_Client)); + } + m_Event.Set(); +} + + + + + +void cChunkSender::RemoveClient(cClientHandle * a_Client) +{ + cCSLock Lock(m_CS); + for (sSendChunkList::iterator itr = m_SendChunks.begin(); itr != m_SendChunks.end();) + { + if (itr->m_Client == a_Client) + { + itr = m_SendChunks.erase(itr); + continue; + } + ++itr; + } // for itr - m_SendChunks[] +} + + + + + void cChunkSender::Execute(void) { while (!mShouldTerminate) { cCSLock Lock(m_CS); - while (m_ChunksReady.empty()) + while (m_ChunksReady.empty() && m_SendChunks.empty()) { cCSUnlock Unlock(Lock); m_Event.Wait(); @@ -77,28 +109,24 @@ void cChunkSender::Execute(void) } } // while (empty) - // Take one from the queue: - cChunkCoords Coords(m_ChunksReady.front()); - m_ChunksReady.pop_front(); - Lock.Unlock(); - - ASSERT(m_World != NULL); - - // Send it to anyone waiting: - m_World->GetChunkData(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, this); - cPacket_PreChunk PreChunk(Coords.m_ChunkX, Coords.m_ChunkZ, true); - cPacket_MapChunk MapChunk(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, m_BlockData); - m_World->BroadcastToChunk(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, PreChunk); - m_World->BroadcastToChunk(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, MapChunk); - - // Send entity creation packets: - for (PacketList::iterator itr = m_Packets.begin(); itr != m_Packets.end(); ++itr) + if (!m_ChunksReady.empty()) { - m_World->BroadcastToChunk(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, **itr); - delete *itr; - } // for itr - m_Packets - m_Packets.clear(); - + // Take one from the queue: + cChunkCoords Coords(m_ChunksReady.front()); + m_ChunksReady.pop_front(); + Lock.Unlock(); + + SendChunk(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, NULL); + } + else + { + // Take one from the queue: + sSendChunk Chunk(m_SendChunks.front()); + m_SendChunks.pop_front(); + Lock.Unlock(); + + SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkY, Chunk.m_ChunkZ, Chunk.m_Client); + } } // while (!mShouldTerminate) } @@ -106,6 +134,47 @@ void cChunkSender::Execute(void) +void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) +{ + ASSERT(m_World != NULL); + + // Prepare MapChunk packets: + m_World->GetChunkData(a_ChunkX, a_ChunkY, a_ChunkZ, this); + cPacket_PreChunk PreChunk(a_ChunkX, a_ChunkZ, true); + cPacket_MapChunk MapChunk(a_ChunkX, a_ChunkY, a_ChunkZ, m_BlockData); + + // 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); + } + else + { + a_Client->Send(PreChunk); + a_Client->Send(MapChunk); + } + + // Send entity creation packets: + for (PacketList::iterator itr = m_Packets.begin(); itr != m_Packets.end(); ++itr) + { + if (a_Client == NULL) + { + m_World->BroadcastToChunk(a_ChunkX, a_ChunkY, a_ChunkZ, **itr); + } + else + { + a_Client->Send(**itr); + } + delete *itr; + } // for itr - m_Packets[] + m_Packets.clear(); +} + + + + + void cChunkSender::BlockData(const char * a_Data) { memcpy(m_BlockData, a_Data, cChunk::c_BlockDataSize); diff --git a/source/ChunkSender.h b/source/ChunkSender.h index a56e797da..e61c45d1e 100644 --- a/source/ChunkSender.h +++ b/source/ChunkSender.h @@ -18,6 +18,7 @@ class cWorld; +class cClientHandle; @@ -34,14 +35,40 @@ public: bool Start(cWorld * a_World); + /// Notifies that a chunk has become ready and it should be sent to all its clients void ChunkReady(int a_ChunkX, int a_ChunkY, int a_ChunkZ); + /// Queues a chunk to be sent to a specific client + void QueueSendChunkTo(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes the a_Client from all waiting chunk send operations + void RemoveClient(cClientHandle * a_Client); + protected: + /// Used for sending chunks to specific clients + struct sSendChunk + { + int m_ChunkX; + int m_ChunkY; + int m_ChunkZ; + cClientHandle * m_Client; + + sSendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) : + m_ChunkX(a_ChunkX), + m_ChunkY(a_ChunkY), + m_ChunkZ(a_ChunkZ), + m_Client(a_Client) + { + } + }; + typedef std::list sSendChunkList; + cWorld * m_World; cCriticalSection m_CS; cChunkCoordsList m_ChunksReady; + sSendChunkList m_SendChunks; cEvent m_Event; // Set when anything is added to m_ChunksReady // Data about the chunk that is being sent: @@ -56,6 +83,8 @@ protected: virtual void Entity(cEntity * a_Entity) override; virtual void BlockEntity(cBlockEntity * a_Entity) override; + /// Sends the specified chunk to a_Client, or to all chunk clients if a_Client == NULL + void SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); } ; diff --git a/source/cClientHandle.cpp b/source/cClientHandle.cpp index 9ea063f11..ee9f0ab3a 100644 --- a/source/cClientHandle.cpp +++ b/source/cClientHandle.cpp @@ -240,6 +240,7 @@ void cClientHandle::Destroy() if ((m_Player != NULL) && (m_Player->GetWorld() != NULL)) { RemoveFromAllChunks(); + m_Player->GetWorld()->RemoveClientFromChunkSender(this); } m_bDestroyed = true; @@ -355,7 +356,8 @@ void cClientHandle::StreamChunks(void) cWorld * World = m_Player->GetWorld(); ASSERT(World != NULL); - // Remove all loaded chunks that are no longer in range: + // Remove all loaded chunks that are no longer in range; deferred to out-of-CS: + cChunkCoordsList RemoveChunks; { cCSLock Lock(m_CSChunkLists); for (cChunkCoordsList::iterator itr = m_LoadedChunks.begin(); itr != m_LoadedChunks.end();) @@ -364,8 +366,7 @@ void cClientHandle::StreamChunks(void) int RelZ = (*itr).m_ChunkZ - ChunkPosZ; if ((RelX > m_ViewDistance) || (RelX < -m_ViewDistance) || (RelZ > m_ViewDistance) || (RelZ < -m_ViewDistance)) { - World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ, this); - Send( cPacket_PreChunk( itr->m_ChunkX, itr->m_ChunkZ, false ) ); + RemoveChunks.push_back(*itr); itr = m_LoadedChunks.erase(itr); } else @@ -385,8 +386,13 @@ void cClientHandle::StreamChunks(void) { ++itr; } - } + } // for itr - m_ChunksToSend[] } + for (cChunkCoordsList::iterator itr = RemoveChunks.begin(); itr != RemoveChunks.end(); ++itr) + { + World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkY, itr->m_ChunkZ, this); + Send(cPacket_PreChunk(itr->m_ChunkX, itr->m_ChunkZ, false)); + } // for itr - RemoveChunks[] // Add all chunks that are in range and not yet in m_LoadedChunks: // Queue these smartly - from the center out to the edge @@ -435,6 +441,7 @@ void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) cCSLock Lock(m_CSChunkLists); m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); m_ChunksToSend.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ)); + World->SendChunkTo(a_ChunkX, a_ChunkY, a_ChunkZ, this); } } diff --git a/source/cWorld.cpp b/source/cWorld.cpp index d74f6ea93..c5cdca03a 100644 --- a/source/cWorld.cpp +++ b/source/cWorld.cpp @@ -1292,6 +1292,24 @@ void cWorld::RemoveClientFromChunks(cClientHandle * a_Client, const cChunkCoords +void cWorld::SendChunkTo(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) +{ + m_ChunkSender.QueueSendChunkTo(a_ChunkX, a_ChunkY, a_ChunkZ, a_Client); +} + + + + + +void cWorld::RemoveClientFromChunkSender(cClientHandle * a_Client) +{ + m_ChunkSender.RemoveClient(a_Client); +} + + + + + void cWorld::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ) { m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkY, a_ChunkZ); diff --git a/source/cWorld.h b/source/cWorld.h index e4fac9d90..bd562ce6f 100644 --- a/source/cWorld.h +++ b/source/cWorld.h @@ -130,6 +130,12 @@ public: /// Removes the client from all chunks specified void RemoveClientFromChunks(cClientHandle * a_Client, const cChunkCoordsList & a_Chunks); + /// Sends the chunk to the client specified, if the chunk is valid. If not valid, the request is ignored (ChunkSender will send that chunk when it becomes valid) + void SendChunkTo(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client); + + /// Removes client from ChunkSender's queue of chunks to be sent + void RemoveClientFromChunkSender(cClientHandle * a_Client); + /// Touches the chunk, causing it to be loaded or generated void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ); -- cgit v1.2.3