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
master
madmaxoft@gmail.com 2012-03-06 14:52:44 +00:00
parent ea1ecd36db
commit 8cdd63f06c
5 changed files with 155 additions and 26 deletions

View File

@ -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,29 +109,66 @@ void cChunkSender::Execute(void)
}
} // while (empty)
if (!m_ChunksReady.empty())
{
// 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)
}
void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client)
{
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);
// 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)
{
m_World->BroadcastToChunk(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, **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
} // for itr - m_Packets[]
m_Packets.clear();
} // while (!mShouldTerminate)
}

View File

@ -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<sSendChunk> 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);
} ;

View File

@ -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);
}
}

View File

@ -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);

View File

@ -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);