summaryrefslogtreecommitdiffstats
path: root/src/ChunkSender.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ChunkSender.cpp')
-rw-r--r--src/ChunkSender.cpp235
1 files changed, 55 insertions, 180 deletions
diff --git a/src/ChunkSender.cpp b/src/ChunkSender.cpp
index ddcd3b534..e27c9f541 100644
--- a/src/ChunkSender.cpp
+++ b/src/ChunkSender.cpp
@@ -14,44 +14,7 @@
#include "Protocol/ChunkDataSerializer.h"
#include "ClientHandle.h"
#include "Chunk.h"
-
-
-
-
-
-////////////////////////////////////////////////////////////////////////////////
-// cNotifyChunkSender:
-
-
-/** Callback that can be used to notify chunk sender upon another chunkcoord notification */
-class cNotifyChunkSender :
- public cChunkCoordCallback
-{
- virtual void Call(int a_ChunkX, int a_ChunkZ, bool a_IsSuccess) override
- {
- cChunkSender & ChunkSender = m_ChunkSender;
- m_World.DoWithChunk(
- a_ChunkX, a_ChunkZ,
- [&ChunkSender] (cChunk & a_Chunk) -> bool
- {
- ChunkSender.QueueSendChunkTo(a_Chunk.GetPosX(), a_Chunk.GetPosZ(), cChunkSender::E_CHUNK_PRIORITY_MIDHIGH, a_Chunk.GetAllClients());
- return true;
- }
- );
- }
-
- cChunkSender & m_ChunkSender;
-
- cWorld & m_World;
-
-public:
- cNotifyChunkSender(cChunkSender & a_ChunkSender, cWorld & a_World):
- m_ChunkSender(a_ChunkSender),
- m_World(a_World)
- {
- }
-};
-
+#include "Entities/Player.h"
@@ -100,82 +63,38 @@ void cChunkSender::Stop(void)
-void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, cClientHandle * a_Client)
+void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, const std::weak_ptr<cClientHandle> & a_Client)
{
- ASSERT(a_Client != nullptr);
+ ASSERT(!a_Client.expired());
{
- cChunkCoords Chunk{a_ChunkX, a_ChunkZ};
+ cChunkCoords Chunk{ a_ChunkX, a_ChunkZ };
cCSLock Lock(m_CS);
- auto iter = m_ChunkInfo.find(Chunk);
- if (iter != m_ChunkInfo.end())
- {
- auto & info = iter->second;
- if (info.m_Priority > a_Priority)
- {
- m_SendChunks.push(sChunkQueue{a_Priority, Chunk});
- info.m_Priority = a_Priority;
- }
- info.m_Clients.insert(a_Client);
- }
- else
- {
- m_SendChunks.push(sChunkQueue{a_Priority, Chunk});
- auto info = sSendChunk{Chunk, a_Priority};
- info.m_Clients.insert(a_Client);
- m_ChunkInfo.emplace(Chunk, info);
- }
- }
- m_evtQueue.Set();
-}
-
-
-
-
-
-void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, std::list<cClientHandle *> a_Clients)
-{
- {
- cChunkCoords Chunk{a_ChunkX, a_ChunkZ};
- cCSLock Lock(m_CS);
- auto iter = m_ChunkInfo.find(Chunk);
- if (iter != m_ChunkInfo.end())
+ auto Iterator = std::find_if(m_LoadQueue.begin(), m_LoadQueue.end(), [Chunk](const decltype(m_LoadQueue)::value_type & a_Entry) { return (a_Entry->m_Chunk == Chunk); });
+ if (Iterator == m_LoadQueue.end())
{
- auto & info = iter->second;
- if (info.m_Priority > a_Priority)
- {
- m_SendChunks.push(sChunkQueue{a_Priority, Chunk});
- info.m_Priority = a_Priority;
- }
- info.m_Clients.insert(a_Clients.begin(), a_Clients.end());
+ auto ChunkStay = new cChunkQueue(a_Priority, Chunk, a_Client, *this);
+ m_LoadQueue.emplace_back(ChunkStay);
+ m_World.QueueTask([ChunkStay, this](cWorld & a_World) { ChunkStay->Enable(*m_World.GetChunkMap()); });
}
else
{
- m_SendChunks.push(sChunkQueue{a_Priority, Chunk});
- auto info = sSendChunk{Chunk, a_Priority};
- info.m_Clients.insert(a_Clients.begin(), a_Clients.end());
- m_ChunkInfo.emplace(Chunk, info);
+ (*Iterator)->m_Priority = std::min(a_Priority, (*Iterator)->m_Priority);
+ (*Iterator)->m_Clients.emplace_back(a_Client);
}
}
- m_evtQueue.Set();
}
-void cChunkSender::RemoveClient(cClientHandle * a_Client)
+void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, const std::vector<std::weak_ptr<cClientHandle>> & a_Clients)
{
+ for (const auto Client : a_Clients)
{
- cCSLock Lock(m_CS);
- for (auto && pair : m_ChunkInfo)
- {
- auto && clients = pair.second.m_Clients;
- clients.erase(a_Client); // nop for sets that do not contain a_Client
- }
+ QueueSendChunkTo(a_ChunkX, a_ChunkZ, a_Priority, Client);
}
- m_evtQueue.Set();
- m_evtRemoved.Wait(); // Wait for all remaining instances of a_Client to be processed (Execute() makes a copy of m_ChunkInfo)
}
@@ -188,29 +107,29 @@ void cChunkSender::Execute(void)
{
m_evtQueue.Wait();
+ decltype(m_SendChunks) QueuedChunks;
{
cCSLock Lock(m_CS);
- while (!m_SendChunks.empty())
+ std::swap(m_SendChunks, QueuedChunks);
+ }
+
+ std::sort(QueuedChunks.begin(), QueuedChunks.end(), [](const decltype(QueuedChunks)::value_type & a_Lhs, const decltype(QueuedChunks)::value_type & a_Rhs)
{
- // Take one from the queue:
- auto Chunk = m_SendChunks.top().m_Chunk;
- m_SendChunks.pop();
- auto itr = m_ChunkInfo.find(Chunk);
- if (itr == m_ChunkInfo.end())
- {
- continue;
- }
-
- std::unordered_set<cClientHandle *> clients;
- std::swap(itr->second.m_Clients, clients);
- m_ChunkInfo.erase(itr);
-
- cCSUnlock Unlock(Lock);
- SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, clients);
+ /* The Standard Priority Queue sorts from biggest to smallest
+ return true here means you are smaller than the other object, and you get pushed down.
+
+ The priorities go from HIGH (0) to LOW (3), so a smaller priority should mean further up the list
+ therefore, return true (affirm we're "smaller", and get pushed down) only if our priority is bigger than theirs (they're more urgent)
+ */
+ return a_Lhs->m_Priority < a_Rhs->m_Priority;
}
- }
+ );
- m_evtRemoved.SetAll(); // Notify all waiting threads that all clients are processed and thus safe to destroy
+ for (const auto & Entry : QueuedChunks)
+ {
+ SendChunk(*Entry);
+ m_World.QueueTask([Entry](cWorld & a_World) { Entry->Disable(); });
+ }
} // while (!m_ShouldTerminate)
}
@@ -218,86 +137,28 @@ void cChunkSender::Execute(void)
-void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, std::unordered_set<cClientHandle *> a_Clients)
+void cChunkSender::SendChunk(const cChunkQueue & a_Item)
{
- // Ask the client if it still wants the chunk:
- for (auto itr = a_Clients.begin(); itr != a_Clients.end();)
+ cChunkDataSerializer Data(a_Item.m_BlockTypes, a_Item.m_BlockMetas, a_Item.m_BlockLight, a_Item.m_BlockSkyLight, a_Item.m_BiomeMap, m_World.GetDimension());
+ for (const auto Client : a_Item.m_Clients)
{
- if (!(*itr)->WantsSendChunk(a_ChunkX, a_ChunkZ))
- {
- itr = a_Clients.erase(itr);
- }
- else
+ // Atomically acquired shared_ptr; thread safe
+ auto ClientPointer = Client.lock();
+ if (ClientPointer == nullptr)
{
- itr++;
+ continue;
}
- }
-
- // 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, cpp14::make_unique<cNotifyChunkSender>(*this, m_World));
- 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, m_World.GetDimension());
-
- for (const auto client : a_Clients)
- {
// Send:
- client->SendChunkData(a_ChunkX, a_ChunkZ, Data);
-
- // Send block-entity packets:
- for (const auto & Pos : m_BlockEntities)
- {
- m_World.SendBlockEntity(Pos.x, Pos.y, Pos.z, *client);
- } // for itr - m_Packets[]
-
+ ClientPointer->SendChunkData(m_World, a_Item.m_Chunk, Data);
}
- m_BlockEntities.clear();
-
- // TODO: Send entity spawn packets
}
-void cChunkSender::BlockEntity(cBlockEntity * a_Entity)
-{
- m_BlockEntities.push_back(a_Entity->GetPos());
-}
-
-
-
-
-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)
+void cChunkSender::cChunkQueue::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap)
{
for (size_t i = 0; i < ARRAYCOUNT(m_BiomeMap); i++)
{
@@ -317,3 +178,17 @@ void cChunkSender::BiomeData(const cChunkDef::BiomeMap * a_BiomeMap)
+
+bool cChunkSender::cChunkQueue::OnAllChunksAvailable()
+{
+ VERIFY(m_ChunkSender.m_World.GetChunkData(m_Chunk.m_ChunkX, m_Chunk.m_ChunkZ, *this));
+
+ {
+ cCSLock Lock(m_ChunkSender.m_CS);
+ m_ChunkSender.m_LoadQueue.erase(std::remove(m_ChunkSender.m_LoadQueue.begin(), m_ChunkSender.m_LoadQueue.end(), this), m_ChunkSender.m_LoadQueue.end());
+ m_ChunkSender.m_SendChunks.push_back(this);
+ }
+
+ m_ChunkSender.m_evtQueue.Set();
+ return false;
+}