1
0
Fork 0
cuberite-2a/src/ChunkSender.cpp

363 lines
7.6 KiB
C++
Raw Normal View History

// 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"
2014-09-26 17:13:19 +00:00
#include "ClientHandle.h"
////////////////////////////////////////////////////////////////////////////////
// cNotifyChunkSender:
2015-05-30 10:11:17 +00:00
/// Callback that can be used to notify chunk sender upon another chunkcoord notification
class cNotifyChunkSender :
public cChunkCoordCallback
{
2015-05-30 10:11:17 +00:00
virtual void Call(int a_ChunkX, int a_ChunkZ) override
{
m_ChunkSender->ChunkReady(a_ChunkX, a_ChunkZ);
}
cChunkSender * m_ChunkSender;
public:
cNotifyChunkSender(cChunkSender * a_ChunkSender) : m_ChunkSender(a_ChunkSender) {}
};
////////////////////////////////////////////////////////////////////////////////
// cChunkSender:
cChunkSender::cChunkSender(void) :
super("ChunkSender"),
2014-10-20 20:55:07 +00:00
m_World(nullptr),
2015-05-30 10:11:17 +00:00
m_RemoveCount(0)
{
}
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);
2014-08-28 09:36:35 +00:00
m_ChunksReady.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
}
m_evtQueue.Set();
}
void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, cClientHandle * a_Client)
{
2014-10-20 20:55:07 +00:00
ASSERT(a_Client != nullptr);
{
sSendChunk Chunk(a_ChunkX, a_ChunkZ, a_Client);
cCSLock Lock(m_CS);
if (
std::find(m_SendChunksLowPriority.begin(), m_SendChunksLowPriority.end(), Chunk) != m_SendChunksLowPriority.end() ||
2014-10-23 19:19:43 +00:00
std::find(m_SendChunksMediumPriority.begin(), m_SendChunksMediumPriority.end(), Chunk) != m_SendChunksMediumPriority.end() ||
std::find(m_SendChunksHighPriority.begin(), m_SendChunksHighPriority.end(), Chunk) != m_SendChunksHighPriority.end()
)
{
// Already queued, bail out
return;
}
2014-10-23 19:19:43 +00:00
switch (a_Priority)
{
case E_CHUNK_PRIORITY_LOW:
{
m_SendChunksLowPriority.push_back(Chunk);
break;
}
case E_CHUNK_PRIORITY_MEDIUM:
{
m_SendChunksMediumPriority.push_back(Chunk);
break;
}
case E_CHUNK_PRIORITY_HIGH:
{
m_SendChunksHighPriority.push_back(Chunk);
break;
}
}
}
m_evtQueue.Set();
}
void cChunkSender::RemoveClient(cClientHandle * a_Client)
{
{
cCSLock Lock(m_CS);
for (sSendChunkList::iterator itr = m_SendChunksLowPriority.begin(); itr != m_SendChunksLowPriority.end();)
{
if (itr->m_Client == a_Client)
{
itr = m_SendChunksLowPriority.erase(itr);
continue;
}
++itr;
} // for itr - m_SendChunksLowPriority[]
2014-10-23 19:19:43 +00:00
for (sSendChunkList::iterator itr = m_SendChunksMediumPriority.begin(); itr != m_SendChunksMediumPriority.end();)
{
if (itr->m_Client == a_Client)
{
itr = m_SendChunksMediumPriority.erase(itr);
continue;
}
++itr;
} // for itr - m_SendChunksMediumPriority[]
for (sSendChunkList::iterator itr = m_SendChunksHighPriority.begin(); itr != m_SendChunksHighPriority.end();)
{
2014-10-21 16:32:02 +00:00
if (itr->m_Client == a_Client)
{
itr = m_SendChunksHighPriority.erase(itr);
continue;
}
++itr;
} // for itr - m_SendChunksHighPriority[]
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_SendChunksLowPriority.empty() && m_SendChunksMediumPriority.empty() && m_SendChunksHighPriority.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_SendChunksHighPriority.empty())
{
// Take one from the queue:
sSendChunk Chunk(m_SendChunksHighPriority.front());
m_SendChunksHighPriority.pop_front();
Lock.Unlock();
SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, Chunk.m_Client);
}
else if (!m_ChunksReady.empty())
{
// Take one from the queue:
cChunkCoords Coords(m_ChunksReady.front());
m_ChunksReady.pop_front();
Lock.Unlock();
2014-10-20 20:55:07 +00:00
SendChunk(Coords.m_ChunkX, Coords.m_ChunkZ, nullptr);
}
2014-10-23 19:19:43 +00:00
else if (!m_SendChunksMediumPriority.empty())
{
// Take one from the queue:
sSendChunk Chunk(m_SendChunksMediumPriority.front());
m_SendChunksMediumPriority.pop_front();
Lock.Unlock();
SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, Chunk.m_Client);
}
else
{
// Take one from the queue:
sSendChunk Chunk(m_SendChunksLowPriority.front());
m_SendChunksLowPriority.pop_front();
Lock.Unlock();
2014-08-28 09:36:35 +00:00
SendChunk(Chunk.m_ChunkX, 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)
}
2014-08-28 09:36:35 +00:00
void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client)
{
2014-10-20 20:55:07 +00:00
ASSERT(m_World != nullptr);
// Ask the client if it still wants the chunk:
if ((a_Client != nullptr) && !a_Client->WantsSendChunk(a_ChunkX, a_ChunkZ))
{
2014-10-02 21:50:41 +00:00
return;
}
2014-10-02 21:50:41 +00:00
// If the chunk has no clients, no need to packetize it:
if (!m_World->HasChunkAnyClients(a_ChunkX, a_ChunkZ))
{
return;
}
2014-10-02 21:50:41 +00:00
// 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;
}
2014-10-02 21:50:41 +00:00
// 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))
{
2015-05-30 10:11:17 +00:00
m_World->QueueLightChunk(a_ChunkX, a_ChunkZ, cpp14::make_unique<cNotifyChunkSender>(this));
return;
}
2014-10-02 21:50:41 +00:00
// 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);
2014-10-02 21:50:41 +00:00
// Send:
2014-10-20 20:55:07 +00:00
if (a_Client == nullptr)
{
m_World->BroadcastChunkData(a_ChunkX, a_ChunkZ, Data);
}
else
{
a_Client->SendChunkData(a_ChunkX, a_ChunkZ, Data);
}
2014-10-02 21:50:41 +00:00
// Send block-entity packets:
for (sBlockCoords::iterator itr = m_BlockEntities.begin(); itr != m_BlockEntities.end(); ++itr)
{
2014-10-20 20:55:07 +00:00
if (a_Client == nullptr)
{
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();
2014-10-02 21:50:41 +00:00
// 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()));
}
2013-12-21 15:00:31 +00:00
void cChunkSender::Entity(cEntity *)
{
// 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 (size_t i = 0; i < ARRAYCOUNT(m_BiomeMap); i++)
{
if ((*a_BiomeMap)[i] < 255)
{
// Normal MC biome, copy as-is:
2015-05-24 11:56:56 +00:00
m_BiomeMap[i] = static_cast<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[]
}