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
This commit is contained in:
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)
|
void cChunkSender::Execute(void)
|
||||||
{
|
{
|
||||||
while (!mShouldTerminate)
|
while (!mShouldTerminate)
|
||||||
{
|
{
|
||||||
cCSLock Lock(m_CS);
|
cCSLock Lock(m_CS);
|
||||||
while (m_ChunksReady.empty())
|
while (m_ChunksReady.empty() && m_SendChunks.empty())
|
||||||
{
|
{
|
||||||
cCSUnlock Unlock(Lock);
|
cCSUnlock Unlock(Lock);
|
||||||
m_Event.Wait();
|
m_Event.Wait();
|
||||||
@ -77,28 +109,24 @@ void cChunkSender::Execute(void)
|
|||||||
}
|
}
|
||||||
} // while (empty)
|
} // while (empty)
|
||||||
|
|
||||||
// Take one from the queue:
|
if (!m_ChunksReady.empty())
|
||||||
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)
|
|
||||||
{
|
{
|
||||||
m_World->BroadcastToChunk(Coords.m_ChunkX, Coords.m_ChunkY, Coords.m_ChunkZ, **itr);
|
// Take one from the queue:
|
||||||
delete *itr;
|
cChunkCoords Coords(m_ChunksReady.front());
|
||||||
} // for itr - m_Packets
|
m_ChunksReady.pop_front();
|
||||||
m_Packets.clear();
|
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)
|
} // 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)
|
void cChunkSender::BlockData(const char * a_Data)
|
||||||
{
|
{
|
||||||
memcpy(m_BlockData, a_Data, cChunk::c_BlockDataSize);
|
memcpy(m_BlockData, a_Data, cChunk::c_BlockDataSize);
|
||||||
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
|
|
||||||
class cWorld;
|
class cWorld;
|
||||||
|
class cClientHandle;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@ -34,14 +35,40 @@ public:
|
|||||||
|
|
||||||
bool Start(cWorld * a_World);
|
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);
|
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:
|
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;
|
cWorld * m_World;
|
||||||
|
|
||||||
cCriticalSection m_CS;
|
cCriticalSection m_CS;
|
||||||
cChunkCoordsList m_ChunksReady;
|
cChunkCoordsList m_ChunksReady;
|
||||||
|
sSendChunkList m_SendChunks;
|
||||||
cEvent m_Event; // Set when anything is added to m_ChunksReady
|
cEvent m_Event; // Set when anything is added to m_ChunksReady
|
||||||
|
|
||||||
// Data about the chunk that is being sent:
|
// Data about the chunk that is being sent:
|
||||||
@ -56,6 +83,8 @@ protected:
|
|||||||
virtual void Entity(cEntity * a_Entity) override;
|
virtual void Entity(cEntity * a_Entity) override;
|
||||||
virtual void BlockEntity(cBlockEntity * 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))
|
if ((m_Player != NULL) && (m_Player->GetWorld() != NULL))
|
||||||
{
|
{
|
||||||
RemoveFromAllChunks();
|
RemoveFromAllChunks();
|
||||||
|
m_Player->GetWorld()->RemoveClientFromChunkSender(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
m_bDestroyed = true;
|
m_bDestroyed = true;
|
||||||
@ -355,7 +356,8 @@ void cClientHandle::StreamChunks(void)
|
|||||||
cWorld * World = m_Player->GetWorld();
|
cWorld * World = m_Player->GetWorld();
|
||||||
ASSERT(World != NULL);
|
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);
|
cCSLock Lock(m_CSChunkLists);
|
||||||
for (cChunkCoordsList::iterator itr = m_LoadedChunks.begin(); itr != m_LoadedChunks.end();)
|
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;
|
int RelZ = (*itr).m_ChunkZ - ChunkPosZ;
|
||||||
if ((RelX > m_ViewDistance) || (RelX < -m_ViewDistance) || (RelZ > m_ViewDistance) || (RelZ < -m_ViewDistance))
|
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);
|
RemoveChunks.push_back(*itr);
|
||||||
Send( cPacket_PreChunk( itr->m_ChunkX, itr->m_ChunkZ, false ) );
|
|
||||||
itr = m_LoadedChunks.erase(itr);
|
itr = m_LoadedChunks.erase(itr);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -385,8 +386,13 @@ void cClientHandle::StreamChunks(void)
|
|||||||
{
|
{
|
||||||
++itr;
|
++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:
|
// Add all chunks that are in range and not yet in m_LoadedChunks:
|
||||||
// Queue these smartly - from the center out to the edge
|
// 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);
|
cCSLock Lock(m_CSChunkLists);
|
||||||
m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
|
m_LoadedChunks.push_back(cChunkCoords(a_ChunkX, a_ChunkY, a_ChunkZ));
|
||||||
m_ChunksToSend.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)
|
void cWorld::TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ)
|
||||||
{
|
{
|
||||||
m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
|
m_ChunkMap->TouchChunk(a_ChunkX, a_ChunkY, a_ChunkZ);
|
||||||
|
@ -130,6 +130,12 @@ public:
|
|||||||
/// Removes the client from all chunks specified
|
/// Removes the client from all chunks specified
|
||||||
void RemoveClientFromChunks(cClientHandle * a_Client, const cChunkCoordsList & a_Chunks);
|
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
|
/// Touches the chunk, causing it to be loaded or generated
|
||||||
void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
|
void TouchChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user