diff options
Diffstat (limited to 'src/ChunkSender.cpp')
-rw-r--r-- | src/ChunkSender.cpp | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/src/ChunkSender.cpp b/src/ChunkSender.cpp new file mode 100644 index 000000000..005cfe29d --- /dev/null +++ b/src/ChunkSender.cpp @@ -0,0 +1,295 @@ + +// ChunkSender.cpp + +// Interfaces to the cChunkSender class representing the thread that waits for chunks becoming ready (loaded / generated) and sends them to clients + + + + + +#include "Globals.h" +#include "ChunkSender.h" +#include "World.h" +#include "BlockEntities/BlockEntity.h" +#include "Protocol/ChunkDataSerializer.h" + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cNotifyChunkSender: + +void cNotifyChunkSender::Call(int a_ChunkX, int a_ChunkZ) +{ + m_ChunkSender->ChunkReady(a_ChunkX, a_ChunkZ); +} + + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// cChunkSender: + +cChunkSender::cChunkSender(void) : + super("ChunkSender"), + m_World(NULL), + m_Notify(NULL), + m_RemoveCount(0) +{ + m_Notify.SetChunkSender(this); +} + + + + + +cChunkSender::~cChunkSender() +{ + Stop(); +} + + + + + +bool cChunkSender::Start(cWorld * a_World) +{ + m_ShouldTerminate = false; + m_World = a_World; + return super::Start(); +} + + + + + +void cChunkSender::Stop(void) +{ + m_ShouldTerminate = true; + m_evtQueue.Set(); + Wait(); +} + + + + + +void cChunkSender::ChunkReady(int a_ChunkX, int a_ChunkZ) +{ + // This is probably never gonna be called twice for the same chunk, and if it is, we don't mind, so we don't check + { + cCSLock Lock(m_CS); + m_ChunksReady.push_back(cChunkCoords(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ)); + } + m_evtQueue.Set(); +} + + + + + +void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client) +{ + ASSERT(a_Client != NULL); + { + cCSLock Lock(m_CS); + if (std::find(m_SendChunks.begin(), m_SendChunks.end(), sSendChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ, a_Client)) != m_SendChunks.end()) + { + // Already queued, bail out + return; + } + m_SendChunks.push_back(sSendChunk(a_ChunkX, ZERO_CHUNK_Y, a_ChunkZ, a_Client)); + } + m_evtQueue.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[] + m_RemoveCount++; + } + m_evtQueue.Set(); + m_evtRemoved.Wait(); // Wait for removal confirmation +} + + + + + +void cChunkSender::Execute(void) +{ + while (!m_ShouldTerminate) + { + cCSLock Lock(m_CS); + while (m_ChunksReady.empty() && m_SendChunks.empty()) + { + int RemoveCount = m_RemoveCount; + m_RemoveCount = 0; + cCSUnlock Unlock(Lock); + for (int i = 0; i < RemoveCount; i++) + { + m_evtRemoved.Set(); // Notify that the removed clients are safe to be deleted + } + m_evtQueue.Wait(); + if (m_ShouldTerminate) + { + return; + } + } // 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); + } + 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 + } + } // while (!mShouldTerminate) +} + + + + + +void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkY, int a_ChunkZ, cClientHandle * a_Client) +{ + ASSERT(m_World != NULL); + + // Ask the client if it still wants the chunk: + if (a_Client != NULL) + { + if (!a_Client->WantsSendChunk(a_ChunkX, a_ChunkY, a_ChunkZ)) + { + return; + } + } + + // If the chunk has no clients, no need to packetize it: + if (!m_World->HasChunkAnyClients(a_ChunkX, a_ChunkZ)) + { + return; + } + + // If the chunk is not valid, do nothing - whoever needs it has queued it for loading / generating + if (!m_World->IsChunkValid(a_ChunkX, a_ChunkZ)) + { + return; + } + + // If the chunk is not lighted, queue it for relighting and get notified when it's ready: + if (!m_World->IsChunkLighted(a_ChunkX, a_ChunkZ)) + { + m_World->QueueLightChunk(a_ChunkX, a_ChunkZ, &m_Notify); + return; + } + + // Query and prepare chunk data: + if (!m_World->GetChunkData(a_ChunkX, a_ChunkZ, *this)) + { + return; + } + cChunkDataSerializer Data(m_BlockTypes, m_BlockMetas, m_BlockLight, m_BlockSkyLight, m_BiomeMap); + + // Send: + if (a_Client == NULL) + { + m_World->BroadcastChunkData(a_ChunkX, a_ChunkZ, Data); + } + else + { + a_Client->SendChunkData(a_ChunkX, a_ChunkZ, Data); + } + + // Send block-entity packets: + for (sBlockCoords::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr) + { + if (a_Client == NULL) + { + m_World->BroadcastBlockEntity(itr->m_BlockX, itr->m_BlockY, itr->m_BlockZ); + } + else + { + m_World->SendBlockEntity(itr->m_BlockX, itr->m_BlockY, itr->m_BlockZ, *a_Client); + } + } // for itr - m_Packets[] + m_BlockEntities.clear(); + + // TODO: Send entity spawn packets +} + + + + + +void cChunkSender::BlockEntity(cBlockEntity * a_Entity) +{ + m_BlockEntities.push_back(sBlockCoord(a_Entity->GetPosX(), a_Entity->GetPosY(), a_Entity->GetPosZ())); +} + + + + +void cChunkSender::Entity(cEntity * a_Entity) +{ + // Nothing needed yet, perhaps in the future when we save entities into chunks we'd like to send them upon load, too ;) +} + + + + + +void cChunkSender::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap) +{ + for (int i = 0; i < ARRAYCOUNT(m_BiomeMap); i++) + { + if ((*a_BiomeMap)[i] < 255) + { + // Normal MC biome, copy as-is: + m_BiomeMap[i] = (unsigned char)((*a_BiomeMap)[i]); + } + else + { + // TODO: MCS-specific biome, need to map to some basic MC biome: + ASSERT(!"Unimplemented MCS-specific biome"); + } + } // for i - m_BiomeMap[] +} + + + + |