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-c427514a06d6master
parent
ea1ecd36db
commit
8cdd63f06c
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
} ;
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
Loading…
Reference in New Issue