1
0
Fork 0

Moved Chunk Broadcasts over to the regular queue

This commit is contained in:
tycho 2015-05-30 17:22:03 +01:00
parent 519fd1d36d
commit 19c95ee564
8 changed files with 139 additions and 117 deletions

View File

@ -2852,22 +2852,6 @@ void cChunk::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, cons
void cChunk::BroadcastChunkData(cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude)
{
for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)
{
if (*itr == a_Exclude)
{
continue;
}
(*itr)->SendChunkData(m_PosX, m_PosZ, a_Serializer);
} // for itr - LoadedByClient[]
}
void cChunk::BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude)
{
for (cClientHandleList::iterator itr = m_LoadedByClient.begin(); itr != m_LoadedByClient.end(); ++itr)

View File

@ -319,7 +319,6 @@ public:
void BroadcastBlockAction (int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = nullptr);
void BroadcastBlockBreakAnimation(UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = nullptr);
void BroadcastBlockEntity (int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude = nullptr);
void BroadcastChunkData (cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = nullptr);
void BroadcastCollectEntity (const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
void BroadcastEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration, const cClientHandle * a_Exclude = nullptr);

View File

@ -409,22 +409,6 @@ void cChunkMap::BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, c
void cChunkMap::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);
cChunkPtr Chunk = GetChunkNoGen(a_ChunkX, a_ChunkZ);
if (Chunk == nullptr)
{
return;
}
// It's perfectly legal to broadcast packets even to invalid chunks!
Chunk->BroadcastChunkData(a_Serializer, a_Exclude);
}
void cChunkMap::BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude)
{
cCSLock Lock(m_CSLayers);

View File

@ -73,7 +73,6 @@ public:
void BroadcastBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType, const cClientHandle * a_Exclude = nullptr);
void BroadcastBlockBreakAnimation(UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage, const cClientHandle * a_Exclude = nullptr);
void BroadcastBlockEntity(int a_BlockX, int a_BlockY, int a_BlockZ, const cClientHandle * a_Exclude);
void BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = nullptr);
void BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
void BroadcastCollectPickup(const cPickup & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
void BroadcastDestroyEntity(const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
@ -303,7 +302,7 @@ public:
Returns false if there's no command block at those coords or callback returns true, returns true if found. */
bool DoWithCommandBlockAt(int a_BlockX, int a_BlockY, int a_BlockZ, cCommandBlockCallback & a_Callback); // Lua-accessible
/** Calls the callback for the mob head block at the specified coords.
/** Calls the callback for the mob head block at the specified coords.chu
Returns false if there's no mob head block at those coords or callback returns true, returns true if found. */
bool DoWithMobHeadAt(int a_BlockX, int a_BlockY, int a_BlockZ, cMobHeadCallback & a_Callback); // Lua-accessible

View File

@ -13,7 +13,7 @@
#include "BlockEntities/BlockEntity.h"
#include "Protocol/ChunkDataSerializer.h"
#include "ClientHandle.h"
#include "Chunk.h"
@ -28,25 +28,29 @@ class cNotifyChunkSender :
{
virtual void Call(int a_ChunkX, int a_ChunkZ) override
{
m_ChunkSender->ChunkReady(a_ChunkX, a_ChunkZ);
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::PRIORITY_BROADCAST, a_Chunk.GetAllClients());
return true;
}
);
}
cChunkSender * m_ChunkSender;
cChunkSender & m_ChunkSender;
cWorld & m_World;
public:
cNotifyChunkSender(cChunkSender * a_ChunkSender) : m_ChunkSender(a_ChunkSender) {}
cNotifyChunkSender(cChunkSender & a_ChunkSender, cWorld & a_World) : m_ChunkSender(a_ChunkSender), m_World(a_World) {}
};
////////////////////////////////////////////////////////////////////////////////
// cChunkSender:
cChunkSender::cChunkSender(void) :
cChunkSender::cChunkSender(cWorld & a_World) :
super("ChunkSender"),
m_World(nullptr),
m_World(a_World),
m_RemoveCount(0)
{
}
@ -64,10 +68,9 @@ cChunkSender::~cChunkSender()
bool cChunkSender::Start(cWorld * a_World)
bool cChunkSender::Start()
{
m_ShouldTerminate = false;
m_World = a_World;
return super::Start();
}
@ -86,20 +89,6 @@ void cChunkSender::Stop(void)
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, a_ChunkZ));
}
m_evtQueue.Set();
}
void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, cClientHandle * a_Client)
{
ASSERT(a_Client != nullptr);
@ -134,6 +123,61 @@ void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a
m_SendChunksHighPriority.push_back(Chunk);
break;
}
case PRIORITY_BROADCAST:
{
m_SendChunksBroadcastPriority.push_back(Chunk);
break;
}
}
}
m_evtQueue.Set();
}
void cChunkSender::QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, std::list<cClientHandle *> a_Clients)
{
{
cCSLock Lock(m_CS);
for (auto client : a_Clients)
{
sSendChunk Chunk(a_ChunkX, a_ChunkZ, client);
if (
std::find(m_SendChunksLowPriority.begin(), m_SendChunksLowPriority.end(), Chunk) != m_SendChunksLowPriority.end() ||
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
continue;
}
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;
}
case PRIORITY_BROADCAST:
{
m_SendChunksBroadcastPriority.push_back(Chunk);
break;
}
}
}
}
m_evtQueue.Set();
@ -189,7 +233,7 @@ void cChunkSender::Execute(void)
while (!m_ShouldTerminate)
{
cCSLock Lock(m_CS);
while (m_ChunksReady.empty() && m_SendChunksLowPriority.empty() && m_SendChunksMediumPriority.empty() && m_SendChunksHighPriority.empty())
while (m_SendChunksBroadcastPriority.empty() && m_SendChunksLowPriority.empty() && m_SendChunksMediumPriority.empty() && m_SendChunksHighPriority.empty())
{
int RemoveCount = m_RemoveCount;
m_RemoveCount = 0;
@ -214,14 +258,14 @@ void cChunkSender::Execute(void)
SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, Chunk.m_Client);
}
else if (!m_ChunksReady.empty())
else if (!m_SendChunksBroadcastPriority.empty())
{
// Take one from the queue:
cChunkCoords Coords(m_ChunksReady.front());
m_ChunksReady.pop_front();
sSendChunk Chunk(m_SendChunksBroadcastPriority.front());
m_SendChunksBroadcastPriority.pop_front();
Lock.Unlock();
SendChunk(Coords.m_ChunkX, Coords.m_ChunkZ, nullptr);
SendChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ, Chunk.m_Client);
}
else if (!m_SendChunksMediumPriority.empty())
{
@ -258,61 +302,46 @@ void cChunkSender::Execute(void)
void cChunkSender::SendChunk(int a_ChunkX, int a_ChunkZ, cClientHandle * a_Client)
{
ASSERT(m_World != nullptr);
// Ask the client if it still wants the chunk:
if ((a_Client != nullptr) && !a_Client->WantsSendChunk(a_ChunkX, a_ChunkZ))
if (!a_Client->WantsSendChunk(a_ChunkX, a_ChunkZ))
{
return;
}
// If the chunk has no clients, no need to packetize it:
if (!m_World->HasChunkAnyClients(a_ChunkX, a_ChunkZ))
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))
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))
if (!m_World.IsChunkLighted(a_ChunkX, a_ChunkZ))
{
m_World->QueueLightChunk(a_ChunkX, a_ChunkZ, cpp14::make_unique<cNotifyChunkSender>(this));
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))
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 == nullptr)
{
m_World->BroadcastChunkData(a_ChunkX, a_ChunkZ, Data);
}
else
{
a_Client->SendChunkData(a_ChunkX, a_ChunkZ, Data);
}
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 == 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);
}
m_World.SendBlockEntity(itr->m_BlockX, itr->m_BlockY, itr->m_BlockZ, *a_Client);
} // for itr - m_Packets[]
m_BlockEntities.clear();

View File

@ -53,7 +53,7 @@ class cChunkSender:
{
typedef cIsThread super;
public:
cChunkSender(void);
cChunkSender(cWorld & a_World);
~cChunkSender();
enum eChunkPriority
@ -61,17 +61,16 @@ public:
E_CHUNK_PRIORITY_HIGH = 0,
E_CHUNK_PRIORITY_MEDIUM = 1,
E_CHUNK_PRIORITY_LOW = 2,
PRIORITY_BROADCAST
};
bool Start(cWorld * a_World);
bool Start();
void Stop(void);
/// Notifies that a chunk has become ready and it should be sent to all its clients
void ChunkReady(int a_ChunkX, int a_ChunkZ);
/// Queues a chunk to be sent to a specific client
void QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, cClientHandle * a_Client);
void QueueSendChunkTo(int a_ChunkX, int a_ChunkZ, eChunkPriority a_Priority, std::list<cClientHandle *> a_Client);
/// Removes the a_Client from all waiting chunk send operations
void RemoveClient(cClientHandle * a_Client);
@ -119,17 +118,16 @@ protected:
typedef std::vector<sBlockCoord> sBlockCoords;
cWorld * m_World;
cWorld & m_World;
cCriticalSection m_CS;
cChunkCoordsList m_ChunksReady;
sSendChunkList m_SendChunksLowPriority;
sSendChunkList m_SendChunksMediumPriority;
sSendChunkList m_SendChunksBroadcastPriority;
sSendChunkList m_SendChunksHighPriority;
cEvent m_evtQueue; // Set when anything is added to m_ChunksReady
cEvent m_evtRemoved; // Set when removed clients are safe to be deleted
int m_RemoveCount; // Number of threads waiting for a client removal (m_evtRemoved needs to be set this many times)
// Data about the chunk that is being sent:
// NOTE that m_BlockData[] is inherited from the cChunkDataCollector
unsigned char m_BiomeMap[cChunkDef::Width * cChunkDef::Width];

View File

@ -186,6 +186,7 @@ cWorld::cWorld(const AString & a_WorldName, eDimension a_Dimension, const AStrin
m_Scoreboard(this),
m_MapManager(this),
m_GeneratorCallbacks(*this),
m_ChunkSender(*this),
m_TickThread(*this)
{
LOGD("cWorld::cWorld(\"%s\")", a_WorldName.c_str());
@ -509,7 +510,7 @@ void cWorld::Start(void)
m_Lighting.Start(this);
m_Storage.Start(this, m_StorageSchema, m_StorageCompressionFactor);
m_Generator.Start(m_GeneratorCallbacks, m_GeneratorCallbacks, IniFile);
m_ChunkSender.Start(this);
m_ChunkSender.Start();
m_TickThread.Start();
// Init of the spawn monster time (as they are supposed to have different spawn rate)
@ -1326,6 +1327,30 @@ bool cWorld::DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback
bool cWorld::DoWithChunk(int a_ChunkX, int a_ChunkZ, std::function<bool(cChunk &)> a_Callback)
{
struct cCallBackWrapper : cChunkCallback
{
cCallBackWrapper(std::function<bool(cChunk &)> a_InnerCallback) :
m_Callback(a_InnerCallback)
{
}
virtual bool Item(cChunk * a_Chunk)
{
return m_Callback(*a_Chunk);
}
private:
std::function<bool(cChunk &)> m_Callback;
} callback(a_Callback);
return m_ChunkMap->DoWithChunk(a_ChunkX, a_ChunkZ, callback);
}
bool cWorld::DoWithChunkAt(Vector3i a_BlockPos, std::function<bool(cChunk &)> a_Callback)
{
return m_ChunkMap->DoWithChunkAt(a_BlockPos, a_Callback);
@ -2001,15 +2026,6 @@ void cWorld::BroadcastChat(const cCompositeChat & a_Message, const cClientHandle
void cWorld::BroadcastChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude)
{
m_ChunkMap->BroadcastChunkData(a_ChunkX, a_ChunkZ, a_Serializer, a_Exclude);
}
void cWorld::BroadcastCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player, const cClientHandle * a_Exclude)
{
m_ChunkMap->BroadcastCollectEntity(a_Entity, a_Player, a_Exclude);
@ -2461,10 +2477,23 @@ void cWorld::SetChunkData(cSetChunkData & a_SetChunkData)
// If a client is requesting this chunk, send it to them:
int ChunkX = a_SetChunkData.GetChunkX();
int ChunkZ = a_SetChunkData.GetChunkZ();
if (m_ChunkMap->HasChunkAnyClients(ChunkX, ChunkZ))
{
m_ChunkSender.ChunkReady(ChunkX, ChunkZ);
}
cChunkSender & ChunkSender = m_ChunkSender;
DoWithChunk(
ChunkX, ChunkZ,
[&ChunkSender] (cChunk & a_Chunk) -> bool
{
if (a_Chunk.HasAnyClients())
{
ChunkSender.QueueSendChunkTo(
a_Chunk.GetPosX(),
a_Chunk.GetPosZ(),
cChunkSender::PRIORITY_BROADCAST,
a_Chunk.GetAllClients()
);
}
return true;
}
);
// Save the chunk right after generating, so that we don't have to generate it again on next run
if (a_SetChunkData.ShouldMarkDirty())

View File

@ -231,7 +231,6 @@ public:
void BroadcastChat (const cCompositeChat & a_Message, const cClientHandle * a_Exclude = nullptr);
// tolua_end
void BroadcastChunkData (int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer, const cClientHandle * a_Exclude = nullptr);
void BroadcastCollectEntity (const cEntity & a_Pickup, const cPlayer & a_Player, const cClientHandle * a_Exclude = nullptr);
void BroadcastDestroyEntity (const cEntity & a_Entity, const cClientHandle * a_Exclude = nullptr);
void BroadcastEntityEffect (const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration, const cClientHandle * a_Exclude = nullptr);
@ -609,6 +608,7 @@ public:
/** Calls the callback for the chunk specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback */
bool DoWithChunk(int a_ChunkX, int a_ChunkZ, cChunkCallback & a_Callback);
bool DoWithChunk(int a_ChunkX, int a_ChunkZ, std::function<bool(cChunk &)> a_Callback);
/** Calls the callback for the chunk at the block position specified, with ChunkMapCS locked; returns false if the chunk doesn't exist, otherwise returns the same value as the callback **/
bool DoWithChunkAt(Vector3i a_BlockPos, std::function<bool(cChunk &)> a_Callback);