summaryrefslogtreecommitdiffstats
path: root/src/ClientHandle.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/ClientHandle.cpp')
-rw-r--r--src/ClientHandle.cpp1193
1 files changed, 460 insertions, 733 deletions
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp
index 7dbf5a0a4..8ae81f645 100644
--- a/src/ClientHandle.cpp
+++ b/src/ClientHandle.cpp
@@ -1,5 +1,6 @@
#include "Globals.h" // NOTE: MSVC stupidness requires this to be the same across all modules
+#include "Chunk.h"
#include "ClientHandle.h"
#include "Server.h"
#include "World.h"
@@ -65,11 +66,8 @@ cClientHandle::cClientHandle(const AString & a_IPString, int a_ViewDistance) :
m_CurrentViewDistance(a_ViewDistance),
m_RequestedViewDistance(a_ViewDistance),
m_IPString(a_IPString),
- m_Player(nullptr),
- m_CachedSentChunk(0, 0),
- m_HasSentDC(false),
- m_LastStreamedChunkX(0x7fffffff), // bogus chunk coords to force streaming upon login
- m_LastStreamedChunkZ(0x7fffffff),
+ m_Protocol(this),
+ m_Player(nullptr), // In order to catch when client leaves before authentication
m_TicksSinceLastPacket(0),
m_Ping(1000),
m_PingID(1),
@@ -82,18 +80,15 @@ cClientHandle::cClientHandle(const AString & a_IPString, int a_ViewDistance) :
m_LastDigBlockX(0),
m_LastDigBlockY(256), // Invalid Y, so that the coords don't get picked up
m_LastDigBlockZ(0),
- m_State(csConnected),
- m_ShouldCheckDownloaded(false),
+ m_State(eState::csConnected),
+ m_ShouldRefreshSentChunks(true),
m_NumExplosionsThisTick(0),
m_NumBlockChangeInteractionsThisTick(0),
m_UniqueID(0),
- m_HasSentPlayerChunk(false),
m_Locale("en_GB"),
m_LastPlacedSign(0, -1, 0),
m_ProtocolVersion(0)
{
- m_Protocol = new cProtocolRecognizer(this);
-
s_ClientCount++; // Not protected by CS because clients are always constructed from the same thread
m_UniqueID = s_ClientCount;
m_PingStartTime = std::chrono::steady_clock::now();
@@ -107,43 +102,28 @@ cClientHandle::cClientHandle(const AString & a_IPString, int a_ViewDistance) :
cClientHandle::~cClientHandle()
{
- ASSERT(m_State == csDestroyed); // Has Destroy() been called?
-
- LOGD("Deleting client \"%s\" at %p", GetUsername().c_str(), static_cast<void *>(this));
-
- {
- cCSLock Lock(m_CSChunkLists);
- m_LoadedChunks.clear();
- m_ChunksToSend.clear();
- }
+ ASSERT(m_State == eState::csDestroyed); // Has Destroy() been called?
if (m_Player != nullptr)
{
cWorld * World = m_Player->GetWorld();
- if (World != nullptr)
- {
- RemoveFromAllChunks();
- m_Player->GetWorld()->RemoveClientFromChunkSender(this);
- if (!m_Username.empty())
+
+ // Upon clienthandle destruction, the world pointer of a valid player SHALL NOT be null.
+ // The only time where it can be null is after player construction but before cEntity::Initialize is called in the queued task in Authenticate.
+ // During this time, it is guaranteed that the clienthandle is not deleted.
+ ASSERT(World != nullptr);
+
+ World->QueueTask(
+ [this](cWorld & a_World)
{
- // Send the Offline PlayerList packet:
- World->BroadcastPlayerListRemovePlayer(*m_Player, this);
+ a_World.RemovePlayer(m_Player);
+ a_World.BroadcastPlayerListRemovePlayer(*m_Player);
+ m_Player->Destroy();
}
- m_Player->DestroyNoScheduling(true);
- }
- delete m_Player;
- m_Player = nullptr;
- }
-
- if (!m_HasSentDC)
- {
- SendDisconnect("Server shut down? Kthnxbai");
+ );
}
- delete m_Protocol;
- m_Protocol = nullptr;
-
- LOGD("ClientHandle at %p deleted", static_cast<void *>(this));
+ LOGD("Deletied client \"%s\" at %p", GetUsername().c_str(), static_cast<void *>(this));
}
@@ -152,35 +132,47 @@ cClientHandle::~cClientHandle()
void cClientHandle::Destroy(void)
{
+ ASSERT(cRoot::Get()->GetServer()->IsInTickThread());
+ if (!SetState(eState::csDestroying))
{
- cCSLock Lock(m_CSOutgoingData);
- m_Link.reset();
+ // Already destroyed.
+ return;
}
+
{
- cCSLock Lock(m_CSDestroyingState);
- if (m_State >= csDestroying)
- {
- // Already called
- return;
- }
- m_State = csDestroying;
+ cCSLock Lock(m_CSLink);
+ m_Link.reset();
}
+ RemoveFromWorld();
+
LOGD("%s: client %p, \"%s\"", __FUNCTION__, static_cast<void *>(this), m_Username.c_str());
+ SetState(eState::csDestroyed);
+}
- if (m_Player != nullptr)
+
+
+
+
+bool cClientHandle::SetState(eState a_State)
+{
+ // Obtain state at a point in time to compare to a_State
+ auto ExpectedState = m_State.load();
+
+ while (
+ // Ensure that an exchange will be performed validly
+ (ExpectedState < a_State) &&
+
+ // Ensure that the current state matches what we have just verified
+ // If so, set the desired state
+ // Else, update ExpectedState to be the current value in preparation for another check and loop
+ !m_State.compare_exchange_weak(ExpectedState, a_State)
+ )
{
- cWorld * World = m_Player->GetWorld();
- if (World != nullptr)
- {
- m_Player->StopEveryoneFromTargetingMe();
- m_Player->SetIsTicking(false);
- World->RemovePlayer(m_Player, true);
- }
- m_Player->RemoveClientHandle();
+ continue;
}
- m_State = csDestroyed;
+ return (ExpectedState < a_State);
}
@@ -304,7 +296,7 @@ bool cClientHandle::IsUUIDOnline(const AString & a_UUID)
void cClientHandle::Kick(const AString & a_Reason)
{
- if (m_State >= csAuthenticating) // Don't log pings
+ if (IsLoggedIn()) // Don't log pings
{
LOGINFO("Kicking player %s for \"%s\"", m_Username.c_str(), StripColorCodes(a_Reason).c_str());
}
@@ -317,13 +309,11 @@ void cClientHandle::Kick(const AString & a_Reason)
void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties)
{
- if (m_State != csAuthenticating)
+ if (m_State != eState::csAuthenticating)
{
return;
}
- ASSERT(m_Player == nullptr);
-
m_Username = a_Name;
// Only assign UUID and properties if not already pre-assigned (BungeeCord sends those in the Handshake packet):
@@ -337,108 +327,118 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID,
}
// Send login success (if the protocol supports it):
- m_Protocol->SendLoginSuccess();
+ m_Protocol.SendLoginSuccess();
+
+ // Obtain a strong reference to ourselves
+ auto Client = shared_from_this();
// Spawn player (only serversided, so data is loaded)
- m_Player = new cPlayer(m_Self, GetUsername());
- InvalidateCachedSentChunk();
- m_Self.reset();
+ auto Player = cpp14::make_unique<cPlayer>(Client, m_Username);
+ m_Player = Player.get();
cWorld * World = cRoot::Get()->GetWorld(m_Player->GetLoadedWorldName());
if (World == nullptr)
{
World = cRoot::Get()->GetDefaultWorld();
- m_Player->SetPosition(World->GetSpawnX(), World->GetSpawnY(), World->GetSpawnZ());
}
if (m_Player->GetGameMode() == eGameMode_NotSet)
{
m_Player->LoginSetGameMode(World->GetGameMode());
}
+ m_Player->SetIP(m_IPString);
- m_Player->SetIP (m_IPString);
+ cpp14::move_on_copy_wrapper<decltype(Player)> PlayerPtr(std::move(Player));
+ World->QueueTask(
+ [World, Player = m_Player, PlayerPtr, Client](cWorld & a_World) mutable
+ {
+ // Plugins expect world to be set:
+ Player->SetWorld(World);
- if (!cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*m_Player))
- {
- cRoot::Get()->BroadcastChatJoin(Printf("%s has joined the game", GetUsername().c_str()));
- LOGINFO("Player %s has joined the game", m_Username.c_str());
- }
+ if (!cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*Player))
+ {
+ cRoot::Get()->BroadcastChatJoin(Printf("%s has joined the game", Client->GetUsername().c_str()));
+ LOGINFO("Player %s has joined the game", Client->GetUsername().c_str());
+ }
- m_ConfirmPosition = m_Player->GetPosition();
+ // Return a server login packet
+ Client->GetProtocol().SendLogin(*Player, *World);
- // Return a server login packet
- m_Protocol->SendLogin(*m_Player, *World);
+ // Send Weather if raining:
+ if ((World->GetWeather() == 1) || (World->GetWeather() == 2))
+ {
+ Client->GetProtocol().SendWeather(World->GetWeather());
+ }
- // Send Weather if raining:
- if ((World->GetWeather() == 1) || (World->GetWeather() == 2))
- {
- m_Protocol->SendWeather(World->GetWeather());
- }
+ // Send time:
+ Client->GetProtocol().SendTimeUpdate(World->GetWorldAge(), World->GetTimeOfDay(), World->IsDaylightCycleEnabled());
- // Send time:
- m_Protocol->SendTimeUpdate(World->GetWorldAge(), World->GetTimeOfDay(), World->IsDaylightCycleEnabled());
+ // Send contents of the inventory window
+ Client->GetProtocol().SendWholeInventory(*Player->GetWindow());
- // Send contents of the inventory window
- m_Protocol->SendWholeInventory(*m_Player->GetWindow());
+ // Send health
+ Client->GetProtocol().SendHealth();
- // Send health
- m_Player->SendHealth();
+ // Send experience
+ Client->GetProtocol().SendExperience();
- // Send experience
- m_Player->SendExperience();
+ // Send player list items
+ cRoot::Get()->BroadcastPlayerListsAddPlayer(*Player);
+ cRoot::Get()->SendPlayerLists(Player);
- // Send player list items
- SendPlayerListAddPlayer(*m_Player);
- cRoot::Get()->BroadcastPlayerListsAddPlayer(*m_Player);
- cRoot::Get()->SendPlayerLists(m_Player);
+ // Note: cEnttiy::Initialize takes ownership of the player object, however:
+ // 1. It is guaranteed to exist whilst the server is running and this clienthandle is alive
+ // 2. In the case that the server is stopping, it is guaranteed that we are destroyed before the player object is destroyed
+ Player->Initialize(std::move(PlayerPtr.value), *World);
- m_Player->SetWorld(World);
- m_State = csAuthenticated;
+ World->AddPlayer(Player);
+ Client->SetState(cClientHandle::eState::csAuthenticated);
- // Query player team
- m_Player->UpdateTeam();
+ // Query player team
+ Player->UpdateTeam();
- // Send scoreboard data
- World->GetScoreBoard().SendTo(*this);
+ // Send scoreboard data
+ World->GetScoreBoard().SendTo(*Client);
- // Send statistics
- SendStatistics(m_Player->GetStatManager());
+ // Send statistics
+ Client->GetProtocol().SendStatistics(Player->GetStatManager());
+
+ cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*Player);
+ }
+ );
// Delay the first ping until the client "settles down"
// This should fix #889, "BadCast exception, cannot convert bit to fm" error in client
m_PingStartTime = std::chrono::steady_clock::now() + std::chrono::seconds(3); // Send the first KeepAlive packet in 3 seconds
-
- cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player);
}
-bool cClientHandle::StreamNextChunk(void)
+void cClientHandle::StreamAllChunks(void)
{
- if ((m_State < csAuthenticated) || (m_State >= csDestroying))
+ ASSERT((m_State >= eState::csAuthenticated) || (m_State < eState::csDestroying));
+ ASSERT(m_Player != nullptr);
+
+ if (m_Player->IsChangingWorlds())
{
- return true;
+ // Make a best-effort attempt at bailing.
+ return;
}
- ASSERT(m_Player != nullptr);
int ChunkPosX = m_Player->GetChunkX();
int ChunkPosZ = m_Player->GetChunkZ();
- if ((m_LastStreamedChunkX == ChunkPosX) && (m_LastStreamedChunkZ == ChunkPosZ))
- {
- // All chunks are already loaded. Abort loading.
- return true;
- }
+
+ std::vector<std::pair<cChunkCoords, cChunkSender::eChunkPriority>> ChunksToSend = {
+ { cChunkCoords(ChunkPosX, ChunkPosZ), cChunkSender::E_CHUNK_PRIORITY_HIGH }
+ };
// Get the look vector and normalize it.
Vector3d Position = m_Player->GetEyePosition();
Vector3d LookVector = m_Player->GetLookVector();
LookVector.Normalize();
- // Lock the list
- cCSLock Lock(m_CSChunkLists);
-
// High priority: Load the chunks that are in the view-direction of the player (with a radius of 3)
for (int Range = 0; Range < m_CurrentViewDistance; Range++)
{
@@ -462,19 +462,13 @@ bool cClientHandle::StreamNextChunk(void)
continue;
}
- // If the chunk already loading / loaded -> skip
- if (
- (m_ChunksToSend.find(Coords) != m_ChunksToSend.end()) ||
- (m_LoadedChunks.find(Coords) != m_LoadedChunks.end())
- )
- {
- continue;
- }
-
- // Unloaded chunk found -> Send it to the client.
- Lock.Unlock();
- StreamChunk(ChunkX, ChunkZ, ((Range <= 2) ? cChunkSender::E_CHUNK_PRIORITY_HIGH : cChunkSender::E_CHUNK_PRIORITY_MEDIUM));
- return false;
+ // Send the chunk; StreamChunk queues a task which will abort if it has been called for the chunk already (we are part of the chunk's clientlist)
+ ChunksToSend.emplace_back(
+ std::make_pair(
+ cChunkCoords(ChunkX, ChunkZ),
+ (Range <= 2) ? cChunkSender::E_CHUNK_PRIORITY_HIGH : cChunkSender::E_CHUNK_PRIORITY_MEDIUM
+ )
+ );
}
}
}
@@ -483,144 +477,72 @@ bool cClientHandle::StreamNextChunk(void)
for (int d = 0; d <= m_CurrentViewDistance; ++d) // cycle through (square) distance, from nearest to furthest
{
// For each distance add chunks in a hollow square centered around current position:
- cChunkCoordsList CurcleChunks;
for (int i = -d; i <= d; ++i)
{
- CurcleChunks.push_back(cChunkCoords(ChunkPosX + d, ChunkPosZ + i));
- CurcleChunks.push_back(cChunkCoords(ChunkPosX - d, ChunkPosZ + i));
+ ChunksToSend.emplace_back(std::make_pair(cChunkCoords(ChunkPosX + d, ChunkPosZ + i), cChunkSender::E_CHUNK_PRIORITY_LOW));
+ ChunksToSend.emplace_back(std::make_pair(cChunkCoords(ChunkPosX - d, ChunkPosZ + i), cChunkSender::E_CHUNK_PRIORITY_LOW));
}
for (int i = -d + 1; i < d; ++i)
{
- CurcleChunks.push_back(cChunkCoords(ChunkPosX + i, ChunkPosZ + d));
- CurcleChunks.push_back(cChunkCoords(ChunkPosX + i, ChunkPosZ - d));
- }
-
- // For each the CurcleChunks list and send the first unloaded chunk:
- for (cChunkCoordsList::iterator itr = CurcleChunks.begin(), end = CurcleChunks.end(); itr != end; ++itr)
- {
- cChunkCoords Coords = *itr;
-
- // If the chunk already loading / loaded -> skip
- if (
- (m_ChunksToSend.find(Coords) != m_ChunksToSend.end()) ||
- (m_LoadedChunks.find(Coords) != m_LoadedChunks.end())
- )
- {
- continue;
- }
-
- // Unloaded chunk found -> Send it to the client.
- Lock.Unlock();
- StreamChunk(Coords.m_ChunkX, Coords.m_ChunkZ, cChunkSender::E_CHUNK_PRIORITY_LOW);
- return false;
+ ChunksToSend.emplace_back(std::make_pair(cChunkCoords(ChunkPosZ + i, ChunkPosZ + d), cChunkSender::E_CHUNK_PRIORITY_LOW));
+ ChunksToSend.emplace_back(std::make_pair(cChunkCoords(ChunkPosZ + i, ChunkPosZ - d), cChunkSender::E_CHUNK_PRIORITY_LOW));
}
}
- // All chunks are loaded -> Sets the last loaded chunk coordinates to current coordinates
- m_LastStreamedChunkX = ChunkPosX;
- m_LastStreamedChunkZ = ChunkPosZ;
- return true;
-}
-
-
-
-
-
-void cClientHandle::UnloadOutOfRangeChunks(void)
-{
- int ChunkPosX = FAST_FLOOR_DIV(static_cast<int>(m_Player->GetPosX()), cChunkDef::Width);
- int ChunkPosZ = FAST_FLOOR_DIV(static_cast<int>(m_Player->GetPosZ()), cChunkDef::Width);
-
- cChunkCoordsList ChunksToRemove;
- {
- cCSLock Lock(m_CSChunkLists);
- for (auto itr = m_LoadedChunks.begin(); itr != m_LoadedChunks.end();)
+ m_Player->GetWorld()->QueueTask(
+ [ChunksToSend = std::move(ChunksToSend), ClientHandle = shared_from_this()](cWorld & a_World)
{
- int DiffX = Diff((*itr).m_ChunkX, ChunkPosX);
- int DiffZ = Diff((*itr).m_ChunkZ, ChunkPosZ);
- if ((DiffX > m_CurrentViewDistance) || (DiffZ > m_CurrentViewDistance))
- {
- ChunksToRemove.push_back(*itr);
- itr = m_LoadedChunks.erase(itr);
- }
- else
+ for (const auto & Chunk : ChunksToSend)
{
- ++itr;
- }
- }
+ if (a_World.AddChunkClient(Chunk.first.m_ChunkX, Chunk.first.m_ChunkZ, ClientHandle))
+ {
+ a_World.GetChunkSender().QueueSendChunkTo(Chunk.first.m_ChunkX, Chunk.first.m_ChunkZ, Chunk.second, ClientHandle);
- for (auto itr = m_ChunksToSend.begin(); itr != m_ChunksToSend.end();)
- {
- int DiffX = Diff((*itr).m_ChunkX, ChunkPosX);
- int DiffZ = Diff((*itr).m_ChunkZ, ChunkPosZ);
- if ((DiffX > m_CurrentViewDistance) || (DiffZ > m_CurrentViewDistance))
- {
- itr = m_ChunksToSend.erase(itr);
- }
- else
- {
- ++itr;
+ // Clarification: std::partition, which cWorld::TickQueuedTasks uses, doesn't guarantee ordering
+ // However, we won't end up with a situation where a RemoveChunkClient ends up before an AddChunkClient for a chunk
+ // because UnloadChunk / RemoveChunkClient is based on our m_LoadedChunks list, which is only updated when this task to add a client has finished.
+ }
}
}
- }
-
- for (cChunkCoordsList::iterator itr = ChunksToRemove.begin(); itr != ChunksToRemove.end(); ++itr)
- {
- m_Player->GetWorld()->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this);
- SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ);
- }
+ );
}
-
-void cClientHandle::StreamChunk(int a_ChunkX, int a_ChunkZ, cChunkSender::eChunkPriority a_Priority)
+void cClientHandle::UnloadOutOfRangeChunks(void)
{
- if (m_State >= csDestroying)
+ if (m_LoadedChunks.empty())
{
- // Don't stream chunks to clients that are being destroyed
return;
}
- cWorld * World = m_Player->GetWorld();
- ASSERT(World != nullptr);
+ int ChunkPosX = FAST_FLOOR_DIV(static_cast<int>(m_Player->GetPosX()), cChunkDef::Width);
+ int ChunkPosZ = FAST_FLOOR_DIV(static_cast<int>(m_Player->GetPosZ()), cChunkDef::Width);
- if (World->AddChunkClient(a_ChunkX, a_ChunkZ, this))
+ for (auto EraseItr = m_LoadedChunks.begin(); EraseItr != m_LoadedChunks.end();)
{
+ int DiffX = Diff(EraseItr->first.m_ChunkX, ChunkPosX);
+ int DiffZ = Diff(EraseItr->first.m_ChunkZ, ChunkPosZ);
+ if (
+ (DiffX <= m_CurrentViewDistance) &&
+ (DiffZ <= m_CurrentViewDistance) &&
+ (m_Player->GetWorld() == &EraseItr->second.get())
+ )
{
- cCSLock Lock(m_CSChunkLists);
- m_LoadedChunks.emplace(a_ChunkX, a_ChunkZ);
- m_ChunksToSend.emplace(a_ChunkX, a_ChunkZ);
+ ++EraseItr;
+ continue;
}
- World->SendChunkTo(a_ChunkX, a_ChunkZ, a_Priority, this);
- }
-}
-
-
-
+ EraseItr->second.get().QueueTask(
+ [ChunkCoordinates = EraseItr->first, ClientHandle = shared_from_this()](cWorld & a_World)
+ {
+ a_World.RemoveChunkClient(ChunkCoordinates.m_ChunkX, ChunkCoordinates.m_ChunkZ, ClientHandle);
+ }
+ );
+ m_Protocol.SendUnloadChunk(EraseItr->first.m_ChunkX, EraseItr->first.m_ChunkZ);
-// Removes the client from all chunks. Used when switching worlds or destroying the player
-void cClientHandle::RemoveFromAllChunks()
-{
- cWorld * World = m_Player->GetWorld();
- if (World != nullptr)
- {
- World->RemoveClientFromChunks(this);
- }
-
- {
- // Reset all chunk lists:
- cCSLock Lock(m_CSChunkLists);
- m_LoadedChunks.clear();
- m_ChunksToSend.clear();
- m_SentChunks.clear();
-
- // Also reset the LastStreamedChunk coords to bogus coords,
- // so that all chunks are streamed in subsequent StreamChunks() call (FS #407)
- m_LastStreamedChunkX = 0x7fffffff;
- m_LastStreamedChunkZ = 0x7fffffff;
+ EraseItr = m_LoadedChunks.erase(EraseItr);
}
}
@@ -638,26 +560,6 @@ void cClientHandle::HandleNPCTrade(int a_SlotNum)
-void cClientHandle::HandlePing(void)
-{
- // Somebody tries to retrieve information about the server
- AString Reply;
- const cServer & Server = *cRoot::Get()->GetServer();
-
- Printf(Reply, "%s%s%i%s%i",
- Server.GetDescription().c_str(),
- cChatColor::Delimiter,
- Server.GetNumPlayers(),
- cChatColor::Delimiter,
- Server.GetMaxPlayers()
- );
- Kick(Reply);
-}
-
-
-
-
-
bool cClientHandle::HandleLogin(UInt32 a_ProtocolVersion, const AString & a_Username)
{
// If the protocol version hasn't been set yet, set it now:
@@ -676,8 +578,8 @@ bool cClientHandle::HandleLogin(UInt32 a_ProtocolVersion, const AString & a_User
}
// Schedule for authentication; until then, let the player wait (but do not block)
- m_State = csAuthenticating;
- cRoot::Get()->GetAuthenticator().Authenticate(GetUniqueID(), GetUsername(), m_Protocol->GetAuthServerID());
+ SetState(eState::csAuthenticating);
+ cRoot::Get()->GetAuthenticator().Authenticate(GetUniqueID(), GetUsername(), m_Protocol.GetAuthServerID());
return true;
}
@@ -762,7 +664,7 @@ void cClientHandle::HandlePlayerAbilities(bool a_CanFly, bool a_IsFlying, float
void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ, double a_Stance, bool a_IsOnGround)
{
- if ((m_Player == nullptr) || (m_State != csPlaying))
+ if (m_State != eState::csPlaying)
{
// The client hasn't been spawned yet and sends nonsense, we know better
return;
@@ -775,8 +677,7 @@ void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ,
}
Vector3d NewPosition(a_PosX, a_PosY, a_PosZ);
- Vector3d OldPosition = GetPlayer()->GetPosition();
- auto PreviousIsOnGround = GetPlayer()->IsOnGround();
+ Vector3d OldPosition = m_Player->GetPosition();
// If the player has moved too far, "repair" them:
if ((OldPosition - NewPosition).SqrLength() > 100 * 100)
@@ -794,7 +695,13 @@ void cClientHandle::HandlePlayerPos(double a_PosX, double a_PosY, double a_PosZ,
// TODO: should do some checks to see if player is not moving through terrain
// TODO: Official server refuses position packets too far away from each other, kicking "hacked" clients; we should, too
+
+ if (cChunkDef::BlockToChunk(NewPosition) != cChunkDef::BlockToChunk(m_Player->GetLastPosition()))
+ {
+ m_ShouldRefreshSentChunks = true;
+ }
+ auto PreviousIsOnGround = m_Player->IsOnGround();
m_Player->SetPosition(NewPosition);
m_Player->SetStance(a_Stance);
m_Player->SetTouchGround(a_IsOnGround);
@@ -996,14 +903,6 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_Status
);
- m_NumBlockChangeInteractionsThisTick++;
-
- if (!CheckBlockInteractionsRate())
- {
- Kick("Too many blocks were destroyed per unit time - hacked client?");
- return;
- }
-
if ((a_Status == DIG_STATUS_STARTED) || (a_Status == DIG_STATUS_FINISHED))
{
if (a_BlockFace == BLOCK_FACE_NONE)
@@ -1042,7 +941,7 @@ void cClientHandle::HandleLeftClick(int a_BlockX, int a_BlockY, int a_BlockZ, eB
}
}
- cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
+ auto PlgMgr = cRoot::Get()->GetPluginManager();
if (m_Player->IsFrozen() || PlgMgr->CallHookPlayerLeftClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, static_cast<char>(a_Status)))
{
// A plugin doesn't agree with the action, replace the block on the client and quit:
@@ -1349,7 +1248,7 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
a_BlockFace = BLOCK_FACE_NONE;
}
- cPluginManager * PlgMgr = cRoot::Get()->GetPluginManager();
+ auto PlgMgr = cRoot::Get()->GetPluginManager();
if (m_Player->IsFrozen() || PlgMgr->CallHookPlayerRightClick(*m_Player, a_BlockX, a_BlockY, a_BlockZ, a_BlockFace, a_CursorX, a_CursorY, a_CursorZ))
{
// A plugin doesn't agree with the action, replace the block on the client and quit:
@@ -1378,14 +1277,6 @@ void cClientHandle::HandleRightClick(int a_BlockX, int a_BlockY, int a_BlockZ, e
return;
}
- m_NumBlockChangeInteractionsThisTick++;
-
- if (!CheckBlockInteractionsRate())
- {
- Kick("Too many blocks were placed / interacted with per unit time - hacked client?");
- return;
- }
-
const cItem & Equipped = m_Player->GetInventory().GetEquippedItem();
if ((Equipped.m_ItemType != a_HeldItem.m_ItemType) && (a_HeldItem.m_ItemType != -1))
@@ -1488,6 +1379,7 @@ void cClientHandle::HandleChat(const AString & a_Message)
// Not a command, broadcast as a message:
cCompositeChat Msg;
AString Color = m_Player->GetColor();
+
if (Color.length() == 3)
{
Color = AString("@") + Color[2];
@@ -1496,6 +1388,7 @@ void cClientHandle::HandleChat(const AString & a_Message)
{
Color.clear();
}
+
Msg.AddTextPart(AString("<") + m_Player->GetName() + "> ", Color);
Msg.ParseText(Message);
Msg.UnderlineUrls();
@@ -1508,7 +1401,7 @@ void cClientHandle::HandleChat(const AString & a_Message)
void cClientHandle::HandlePlayerLook(float a_Rotation, float a_Pitch, bool a_IsOnGround)
{
- if ((m_Player == nullptr) || (m_State != csPlaying))
+ if (m_State != eState::csPlaying)
{
return;
}
@@ -1606,7 +1499,12 @@ void cClientHandle::HandleUpdateSign(
if (m_LastPlacedSign.Equals(Vector3i(a_BlockX, a_BlockY, a_BlockZ)))
{
m_LastPlacedSign.Set(0, -1, 0);
- m_Player->GetWorld()->SetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, m_Player);
+ m_Player->GetWorld()->QueueTask(
+ [=](cWorld & a_World)
+ {
+ a_World.SetSignLines(a_BlockX, a_BlockY, a_BlockZ, a_Line1, a_Line2, a_Line3, a_Line4, m_Player);
+ }
+ );
}
}
@@ -1684,11 +1582,6 @@ void cClientHandle::HandleUseEntity(UInt32 a_TargetEntityID, bool a_IsLeftClick)
void cClientHandle::HandleRespawn(void)
{
- if (m_Player == nullptr)
- {
- Destroy();
- return;
- }
m_Player->Respawn();
cRoot::Get()->GetPluginManager()->CallHookPlayerSpawned(*m_Player);
}
@@ -1717,28 +1610,12 @@ bool cClientHandle::CheckMultiLogin(const AString & a_Username)
return true;
}
- // Check if the player is waiting to be transferred to the World.
+ // Check if the username is connected already:
if (cRoot::Get()->GetServer()->IsPlayerInQueue(a_Username))
{
Kick("A player of the username is already logged in");
return false;
}
-
- class cCallback :
- public cPlayerListCallback
- {
- virtual bool Item(cPlayer * a_Player) override
- {
- return true;
- }
- } Callback;
-
- // Check if the player is in any World.
- if (cRoot::Get()->DoWithPlayer(a_Username, Callback))
- {
- Kick("A player of the username is already logged in");
- return false;
- }
return true;
}
@@ -1793,9 +1670,9 @@ void cClientHandle::HandleEntityLeaveBed(UInt32 a_EntityID)
return;
}
- cChunkInterface Interface(GetPlayer()->GetWorld()->GetChunkMap());
- cBlockBedHandler::SetBedOccupationState(Interface, GetPlayer()->GetLastBedPos(), false);
- GetPlayer()->SetIsInBed(false);
+ cChunkInterface Interface(m_Player->GetWorld()->GetChunkMap());
+ cBlockBedHandler::SetBedOccupationState(Interface, m_Player->GetLastBedPos(), false);
+ m_Player->SetIsInBed(false);
}
@@ -1819,10 +1696,6 @@ void cClientHandle::HandleEntitySprinting(UInt32 a_EntityID, bool a_IsSprinting)
void cClientHandle::HandleUnmount(void)
{
- if (m_Player == nullptr)
- {
- return;
- }
m_Player->Detach();
}
@@ -1833,6 +1706,7 @@ void cClientHandle::HandleUnmount(void)
void cClientHandle::HandleTabCompletion(const AString & a_Text)
{
AStringVector Results;
+
// Get player name completions.
if (cRoot::Get()->GetServer()->ShouldAllowMultiWorldTabCompletion())
{
@@ -1843,8 +1717,9 @@ void cClientHandle::HandleTabCompletion(const AString & a_Text)
m_Player->GetWorld()->TabCompleteUserName(a_Text, Results);
}
- // Get command completions.
+ // Get command completions
cRoot::Get()->GetPluginManager()->TabCompleteCommand(a_Text, Results, m_Player);
+
if (Results.empty())
{
return;
@@ -1861,12 +1736,6 @@ void cClientHandle::HandleTabCompletion(const AString & a_Text)
void cClientHandle::SendData(const char * a_Data, size_t a_Size)
{
- if (m_HasSentDC)
- {
- // This could crash the client, because they've already unloaded the world etc., and suddenly a wild packet appears (#31)
- return;
- }
-
cCSLock Lock(m_CSOutgoingData);
m_OutgoingData.append(a_Data, a_Size);
}
@@ -1877,92 +1746,131 @@ void cClientHandle::SendData(const char * a_Data, size_t a_Size)
void cClientHandle::RemoveFromWorld(void)
{
+ ASSERT(cRoot::Get()->GetServer()->IsInTickThread());
+
// Remove all associated chunks:
- decltype(m_LoadedChunks) Chunks;
+ for (const auto & Chunk : m_LoadedChunks)
{
- cCSLock Lock(m_CSChunkLists);
- std::swap(Chunks, m_LoadedChunks);
- m_ChunksToSend.clear();
+ Chunk.second.get().QueueTask(
+ [ChunkCoordinates = Chunk.first, ClientHandle = shared_from_this()](cWorld & a_World)
+ {
+ a_World.RemoveChunkClient(ChunkCoordinates.m_ChunkX, ChunkCoordinates.m_ChunkZ, ClientHandle);
+ }
+ );
+ GetProtocol().SendUnloadChunk(Chunk.first.m_ChunkX, Chunk.first.m_ChunkZ);
}
- for (auto && Chunk : Chunks)
- {
- m_Protocol->SendUnloadChunk(Chunk.m_ChunkX, Chunk.m_ChunkZ);
- } // for itr - Chunks[]
- // Here, we set last streamed values to bogus ones so everything is resent
- m_LastStreamedChunkX = 0x7fffffff;
- m_LastStreamedChunkZ = 0x7fffffff;
+ m_LoadedChunks.clear();
}
-void cClientHandle::InvalidateCachedSentChunk()
+bool cClientHandle::EnforceBlockInteractionsRate(void)
{
- ASSERT(m_Player != nullptr);
- // Sets this to a junk value different from the player's current chunk, which invalidates it and
- // ensures its value will not be used.
- m_CachedSentChunk = cChunkCoords(m_Player->GetChunkX() + 500, m_Player->GetChunkZ());
+ ASSERT(cRoot::Get()->GetServer()->IsInTickThread());
+
+ m_NumBlockChangeInteractionsThisTick++;
+ if (m_NumBlockChangeInteractionsThisTick > MAX_BLOCK_CHANGE_INTERACTIONS)
+ {
+ Kick("Too many blocks were destroyed per unit time - hacked client?");
+ return false;
+ }
+
+ return true;
}
-bool cClientHandle::IsPlayerChunkSent()
+void cClientHandle::ProcessQueuedIncomingData(void)
{
- return m_HasSentPlayerChunk;
+ decltype(m_IncomingData) IncomingData;
+ {
+ cCSLock Lock(m_CSIncomingData);
+ std::swap(IncomingData, m_IncomingData);
+ }
+
+ if (!IncomingData.empty())
+ {
+ m_Protocol.DataReceived(IncomingData.data(), IncomingData.size());
+ }
}
-bool cClientHandle::CheckBlockInteractionsRate(void)
+void cClientHandle::ProcessDataCommitQueue(void)
{
- ASSERT(m_Player != nullptr);
- ASSERT(m_Player->GetWorld() != nullptr);
+ if (m_DataCommitQueue.empty())
+ {
+ return;
+ }
- if (m_NumBlockChangeInteractionsThisTick > MAX_BLOCK_CHANGE_INTERACTIONS)
+ if ((m_Player == nullptr) || (m_Player->GetWorld() == nullptr))
{
- return false;
+ Kick("Illegal packet sent - player not spawned yet - hacked client?");
+ return;
}
- return true;
+ decltype(m_DataCommitQueue) DataCommitTasks;
+ std::swap(DataCommitTasks, m_DataCommitQueue);
+
+ m_Player->GetWorld()->QueueTask(
+ [DataCommitTasks = std::move(DataCommitTasks), Player = m_Player](cWorld & a_World)
+ {
+ if (Player->IsChangingWorlds() || (Player->GetWorld() != &a_World))
+ {
+ // IsChangingWorlds is true: cPlayer::m_World may be updated without warning - cannot handle this.
+ // cPlayer::m_World is unequal to a_World: player has completed world change - deal with after effect by bailing
+ return;
+ }
+
+ for (const auto & Task : DataCommitTasks)
+ {
+ Task();
+ }
+ }
+ );
}
-void cClientHandle::Tick(float a_Dt)
+void cClientHandle::ProcessQueuedOutgoingData(void)
{
- // Process received network data:
- AString IncomingData;
- {
- cCSLock Lock(m_CSIncomingData);
- std::swap(IncomingData, m_IncomingData);
- }
- if (!IncomingData.empty())
- {
- m_Protocol->DataReceived(IncomingData.data(), IncomingData.size());
- }
-
- // Send any queued outgoing data:
- AString OutgoingData;
+ decltype(m_OutgoingData) OutgoingData;
{
cCSLock Lock(m_CSOutgoingData);
std::swap(OutgoingData, m_OutgoingData);
}
+
if (!OutgoingData.empty())
{
- cTCPLinkPtr Link(m_Link); // Grab a copy of the link in a multithread-safe way
- if ((Link != nullptr))
{
- Link->Send(OutgoingData.data(), OutgoingData.size());
+ cCSLock Lock(m_CSLink);
+
+ // All possible operations which reset m_Link MUST cause the clienthandle to cease to be ticked by the server
+ ASSERT(m_Link != nullptr);
+
+ m_Link->Send(OutgoingData);
}
}
+}
+
+
+
+
+
+void cClientHandle::Tick(float a_Dt)
+{
+ ProcessQueuedIncomingData();
+ ProcessQueuedOutgoingData();
+ ProcessDataCommitQueue();
m_TicksSinceLastPacket += 1;
if (m_TicksSinceLastPacket > 600) // 30 seconds time-out
@@ -1976,71 +1884,20 @@ void cClientHandle::Tick(float a_Dt)
return;
}
-
- // Freeze the player if it is standing on a chunk not yet sent to the client
- m_HasSentPlayerChunk = false;
- if (m_Player->GetParentChunk() != nullptr)
- {
- // If the chunk is invalid, do not bother checking if it's sent to the client, it is definitely not
- if (m_Player->GetParentChunk()->IsValid())
- {
- // Before iterating m_SentChunks, see if the player's coords equal m_CachedSentChunk
- // If so, the chunk has been sent to the client. This is an optimization that saves an iteration of m_SentChunks.
- if (cChunkCoords(m_Player->GetChunkX(), m_Player->GetChunkZ()) == m_CachedSentChunk)
- {
- m_HasSentPlayerChunk = true;
- }
- else
- {
- // This block is entered only when the player moves to a new chunk, invalidating the cached coords.
- // Otherwise the cached coords are used.
- cCSLock Lock(m_CSChunkLists);
- auto itr = std::find(m_SentChunks.begin(), m_SentChunks.end(), cChunkCoords(m_Player->GetChunkX(), m_Player->GetChunkZ()));
- if (itr != m_SentChunks.end())
- {
- m_CachedSentChunk = *itr;
- m_HasSentPlayerChunk = true;
- }
- }
- }
- }
-
- // If the chunk the player's in was just sent, spawn the player:
- if (m_HasSentPlayerChunk && (m_State == csDownloadingWorld))
- {
- m_Protocol->SendPlayerMoveLook();
- m_State = csPlaying;
- }
-
// Send a ping packet:
- if (m_State == csPlaying)
+ if ((m_PingStartTime + PING_TIME_MS <= std::chrono::steady_clock::now()))
{
- if ((m_PingStartTime + PING_TIME_MS <= std::chrono::steady_clock::now()))
- {
- m_PingID++;
- m_PingStartTime = std::chrono::steady_clock::now();
- m_Protocol->SendKeepAlive(m_PingID);
- }
+ m_PingID++;
+ m_PingStartTime = std::chrono::steady_clock::now();
+ m_Protocol.SendKeepAlive(m_PingID);
}
- if ((m_State >= csAuthenticated) && (m_State < csDestroying))
+ if (m_ShouldRefreshSentChunks && (m_State >= eState::csAuthenticated) && (m_State < eState::csDestroying))
{
- // Stream 4 chunks per tick
- for (int i = 0; i < 4; i++)
- {
- // Stream the next chunk
- if (StreamNextChunk())
- {
- // Streaming finished. All chunks are loaded.
- break;
- }
- }
+ StreamAllChunks();
+ UnloadOutOfRangeChunks();
- // Unload all chunks that are out of the view distance (every 5 seconds)
- if ((m_Player->GetWorld()->GetWorldAge() % 100) == 0)
- {
- UnloadOutOfRangeChunks();
- }
+ m_ShouldRefreshSentChunks = false;
}
// Handle block break animation:
@@ -2067,57 +1924,9 @@ void cClientHandle::Tick(float a_Dt)
-void cClientHandle::ServerTick(float a_Dt)
-{
- // Process received network data:
- AString IncomingData;
- {
- cCSLock Lock(m_CSIncomingData);
- std::swap(IncomingData, m_IncomingData);
- }
- if (!IncomingData.empty())
- {
- m_Protocol->DataReceived(IncomingData.data(), IncomingData.size());
- }
-
- // Send any queued outgoing data:
- AString OutgoingData;
- {
- cCSLock Lock(m_CSOutgoingData);
- std::swap(OutgoingData, m_OutgoingData);
- }
- if ((m_Link != nullptr) && !OutgoingData.empty())
- {
- m_Link->Send(OutgoingData.data(), OutgoingData.size());
- }
-
- if (m_State == csAuthenticated)
- {
- StreamNextChunk();
-
- // Remove the client handle from the server, it will be ticked from its cPlayer object from now on
- cRoot::Get()->GetServer()->ClientMovedToWorld(this);
-
- // Add the player to the world (start ticking from there):
- m_State = csDownloadingWorld;
- m_Player->Initialize(*(m_Player->GetWorld()));
- return;
- }
-
- m_TicksSinceLastPacket += 1;
- if (m_TicksSinceLastPacket > 600) // 30 seconds
- {
- SendDisconnect("Nooooo!! You timed out! D: Come back!");
- }
-}
-
-
-
-
-
void cClientHandle::SendAttachEntity(const cEntity & a_Entity, const cEntity & a_Vehicle)
{
- m_Protocol->SendAttachEntity(a_Entity, a_Vehicle);
+ m_Protocol.SendAttachEntity(a_Entity, a_Vehicle);
}
@@ -2126,7 +1935,7 @@ void cClientHandle::SendAttachEntity(const cEntity & a_Entity, const cEntity & a
void cClientHandle::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, char a_Byte1, char a_Byte2, BLOCKTYPE a_BlockType)
{
- m_Protocol->SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType);
+ m_Protocol.SendBlockAction(a_BlockX, a_BlockY, a_BlockZ, a_Byte1, a_Byte2, a_BlockType);
}
@@ -2135,7 +1944,7 @@ void cClientHandle::SendBlockAction(int a_BlockX, int a_BlockY, int a_BlockZ, ch
void cClientHandle::SendBlockBreakAnim(UInt32 a_EntityID, int a_BlockX, int a_BlockY, int a_BlockZ, char a_Stage)
{
- m_Protocol->SendBlockBreakAnim(a_EntityID, a_BlockX, a_BlockY, a_BlockZ, a_Stage);
+ m_Protocol.SendBlockBreakAnim(a_EntityID, a_BlockX, a_BlockY, a_BlockZ, a_Stage);
}
@@ -2144,17 +1953,7 @@ void cClientHandle::SendBlockBreakAnim(UInt32 a_EntityID, int a_BlockX, int a_Bl
void cClientHandle::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BLOCKTYPE a_BlockType, NIBBLETYPE a_BlockMeta)
{
- int ChunkX, ChunkZ = 0;
- cChunkDef::BlockToChunk(a_BlockX, a_BlockZ, ChunkX, ChunkZ);
- cChunkCoords ChunkCoords = cChunkCoords(ChunkX, ChunkZ);
-
- // Do not send block changes in chunks that weren't sent to the client yet:
- cCSLock Lock(m_CSChunkLists);
- if (std::find(m_SentChunks.begin(), m_SentChunks.end(), ChunkCoords) != m_SentChunks.end())
- {
- Lock.Unlock();
- m_Protocol->SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta);
- }
+ GetProtocol().SendBlockChange(a_BlockX, a_BlockY, a_BlockZ, a_BlockType, a_BlockMeta);
}
@@ -2163,16 +1962,7 @@ void cClientHandle::SendBlockChange(int a_BlockX, int a_BlockY, int a_BlockZ, BL
void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlockVector & a_Changes)
{
- ASSERT(!a_Changes.empty()); // We don't want to be sending empty change packets!
-
- // Do not send block changes in chunks that weren't sent to the client yet:
- cChunkCoords ChunkCoords = cChunkCoords(a_ChunkX, a_ChunkZ);
- cCSLock Lock(m_CSChunkLists);
- if (std::find(m_SentChunks.begin(), m_SentChunks.end(), ChunkCoords) != m_SentChunks.end())
- {
- Lock.Unlock();
- m_Protocol->SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes);
- }
+ GetProtocol().SendBlockChanges(a_ChunkX, a_ChunkZ, a_Changes);
}
@@ -2181,19 +1971,9 @@ void cClientHandle::SendBlockChanges(int a_ChunkX, int a_ChunkZ, const sSetBlock
void cClientHandle::SendChat(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
{
- cWorld * World = GetPlayer()->GetWorld();
- if (World == nullptr)
- {
- World = cRoot::Get()->GetWorld(GetPlayer()->GetLoadedWorldName());
- if (World == nullptr)
- {
- World = cRoot::Get()->GetDefaultWorld();
- }
- }
-
- bool ShouldUsePrefixes = World->ShouldUseChatPrefixes();
+ bool ShouldUsePrefixes = m_Player->GetWorld()->ShouldUseChatPrefixes();
AString Message = FormatMessageType(ShouldUsePrefixes, a_ChatPrefix, a_AdditionalData);
- m_Protocol->SendChat(Message.append(a_Message), ctChatBox, ShouldUsePrefixes);
+ m_Protocol.SendChat(Message.append(a_Message), ctChatBox, ShouldUsePrefixes);
}
@@ -2202,18 +1982,8 @@ void cClientHandle::SendChat(const AString & a_Message, eMessageType a_ChatPrefi
void cClientHandle::SendChat(const cCompositeChat & a_Message)
{
- cWorld * World = GetPlayer()->GetWorld();
- if (World == nullptr)
- {
- World = cRoot::Get()->GetWorld(GetPlayer()->GetLoadedWorldName());
- if (World == nullptr)
- {
- World = cRoot::Get()->GetDefaultWorld();
- }
- }
-
- bool ShouldUsePrefixes = World->ShouldUseChatPrefixes();
- m_Protocol->SendChat(a_Message, ctChatBox, ShouldUsePrefixes);
+ bool ShouldUsePrefixes = m_Player->GetWorld()->ShouldUseChatPrefixes();
+ m_Protocol.SendChat(a_Message, ctChatBox, ShouldUsePrefixes);
}
@@ -2222,18 +1992,8 @@ void cClientHandle::SendChat(const cCompositeChat & a_Message)
void cClientHandle::SendChatAboveActionBar(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
{
- cWorld * World = GetPlayer()->GetWorld();
- if (World == nullptr)
- {
- World = cRoot::Get()->GetWorld(GetPlayer()->GetLoadedWorldName());
- if (World == nullptr)
- {
- World = cRoot::Get()->GetDefaultWorld();
- }
- }
-
- AString Message = FormatMessageType(World->ShouldUseChatPrefixes(), a_ChatPrefix, a_AdditionalData);
- m_Protocol->SendChat(Message.append(a_Message), ctAboveActionBar);
+ AString Message = FormatMessageType(m_Player->GetWorld()->ShouldUseChatPrefixes(), a_ChatPrefix, a_AdditionalData);
+ m_Protocol.SendChat(Message.append(a_Message), ctAboveActionBar);
}
@@ -2242,7 +2002,7 @@ void cClientHandle::SendChatAboveActionBar(const AString & a_Message, eMessageTy
void cClientHandle::SendChatAboveActionBar(const cCompositeChat & a_Message)
{
- m_Protocol->SendChat(a_Message, ctAboveActionBar, GetPlayer()->GetWorld()->ShouldUseChatPrefixes());
+ m_Protocol.SendChat(a_Message, ctAboveActionBar, m_Player->GetWorld()->ShouldUseChatPrefixes());
}
@@ -2251,19 +2011,9 @@ void cClientHandle::SendChatAboveActionBar(const cCompositeChat & a_Message)
void cClientHandle::SendChatSystem(const AString & a_Message, eMessageType a_ChatPrefix, const AString & a_AdditionalData)
{
- cWorld * World = GetPlayer()->GetWorld();
- if (World == nullptr)
- {
- World = cRoot::Get()->GetWorld(GetPlayer()->GetLoadedWorldName());
- if (World == nullptr)
- {
- World = cRoot::Get()->GetDefaultWorld();
- }
- }
-
- auto ShouldUsePrefixes = World->ShouldUseChatPrefixes();
+ auto ShouldUsePrefixes = m_Player->GetWorld()->ShouldUseChatPrefixes();
AString Message = FormatMessageType(ShouldUsePrefixes, a_ChatPrefix, a_AdditionalData);
- m_Protocol->SendChat(Message.append(a_Message), ctSystem, ShouldUsePrefixes);
+ m_Protocol.SendChat(Message.append(a_Message), ctSystem, ShouldUsePrefixes);
}
@@ -2272,49 +2022,80 @@ void cClientHandle::SendChatSystem(const AString & a_Message, eMessageType a_Cha
void cClientHandle::SendChatSystem(const cCompositeChat & a_Message)
{
- m_Protocol->SendChat(a_Message, ctSystem, GetPlayer()->GetWorld()->ShouldUseChatPrefixes());
+ m_Protocol.SendChat(a_Message, ctSystem, m_Player->GetWorld()->ShouldUseChatPrefixes());
}
-void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializer & a_Serializer)
+void cClientHandle::SendChunkData(cWorld & a_ChunkSenderWorld, const cChunkCoords & a_ChunkCoordinates, cChunkDataSerializer & a_Serializer)
{
- ASSERT(m_Player != nullptr);
+ m_Protocol.SendChunkData(a_ChunkCoordinates.m_ChunkX, a_ChunkCoordinates.m_ChunkZ, a_Serializer);
- // Check chunks being sent, erase them from m_ChunksToSend:
- bool Found = false;
- {
- cCSLock Lock(m_CSChunkLists);
- auto itr = m_ChunksToSend.find(cChunkCoords{a_ChunkX, a_ChunkZ});
- if (itr != m_ChunksToSend.end())
+ cRoot::Get()->GetServer()->QueueTask(
+ [&a_ChunkSenderWorld, a_ChunkCoordinates, ClientHandle = shared_from_this()]
{
- m_ChunksToSend.erase(itr);
- Found = true;
- }
- }
- if (!Found)
- {
- // This just sometimes happens. If you have a reliably replicatable situation for this, go ahead and fix it
- // It's not a big issue anyway, just means that some chunks may be compressed several times
- // LOGD("Refusing to send chunk [%d, %d] to client \"%s\" at [%d, %d].", ChunkX, ChunkZ, m_Username.c_str(), m_Player->GetChunkX(), m_Player->GetChunkZ());
- return;
- }
-
- if (m_Protocol == nullptr)
- {
- // TODO (#2588): investigate if and why this occurs
- return;
- }
+ // Add the chunk to the list of chunks sent to the player:
+ auto Result = ClientHandle->m_LoadedChunks.emplace(a_ChunkCoordinates, std::ref(a_ChunkSenderWorld));
+ if (Result.second || (&Result.first->second.get() != &a_ChunkSenderWorld))
+ {
+ auto Player = ClientHandle->GetPlayer();
+ if (
+ (Player->GetChunkX() == a_ChunkCoordinates.m_ChunkX) &&
+ (Player->GetChunkZ() == a_ChunkCoordinates.m_ChunkZ)
+ )
+ {
+ if (ClientHandle->m_State == eState::csAuthenticated)
+ {
+ ClientHandle->SendPlayerMoveLook();
+ ClientHandle->SetState(eState::csPlaying);
+ }
+
+ Player->GetWorld()->QueueTask(
+ [Player](cWorld & a_World)
+ {
+ auto SearchingPosition = Player->GetPosition();
+ if (SearchingPosition.y < 0)
+ {
+ SearchingPosition.y = 0;
+ }
+
+ while (SearchingPosition.y < cChunkDef::Height - 2)
+ {
+ // If we find a position with enough space for the player
+ if (a_World.GetBlock(SearchingPosition.x, SearchingPosition.y, SearchingPosition.z) == E_BLOCK_AIR)
+ {
+ if (a_World.GetBlock(SearchingPosition.x, SearchingPosition.y + 1, SearchingPosition.z) == E_BLOCK_AIR)
+ {
+ Player->TeleportToCoords(SearchingPosition);
+ break;
+ }
+ else
+ {
+ SearchingPosition.y += 2;
+ }
+ }
+ else
+ {
+ SearchingPosition.y++;
+ }
+ }
+ }
+ );
+ }
- m_Protocol->SendChunkData(a_ChunkX, a_ChunkZ, a_Serializer);
+ Result.first->second.get().QueueTask(
+ [a_ChunkCoordinates, ClientHandle](cWorld & a_World)
+ {
+ a_World.RemoveChunkClient(a_ChunkCoordinates.m_ChunkX, a_ChunkCoordinates.m_ChunkZ, ClientHandle);
+ }
+ );
- // Add the chunk to the list of chunks sent to the player:
- {
- cCSLock Lock(m_CSChunkLists);
- m_SentChunks.push_back(cChunkCoords(a_ChunkX, a_ChunkZ));
- }
+ Result.first->second = std::ref(a_ChunkSenderWorld);
+ }
+ }
+ );
}
@@ -2323,7 +2104,7 @@ void cClientHandle::SendChunkData(int a_ChunkX, int a_ChunkZ, cChunkDataSerializ
void cClientHandle::SendCollectEntity(const cEntity & a_Entity, const cPlayer & a_Player)
{
- m_Protocol->SendCollectEntity(a_Entity, a_Player);
+ m_Protocol.SendCollectEntity(a_Entity, a_Player);
}
@@ -2332,7 +2113,7 @@ void cClientHandle::SendCollectEntity(const cEntity & a_Entity, const cPlayer &
void cClientHandle::SendDestroyEntity(const cEntity & a_Entity)
{
- m_Protocol->SendDestroyEntity(a_Entity);
+ m_Protocol.SendDestroyEntity(a_Entity);
}
@@ -2341,7 +2122,7 @@ void cClientHandle::SendDestroyEntity(const cEntity & a_Entity)
void cClientHandle::SendDetachEntity(const cEntity & a_Entity, const cEntity & a_PreviousVehicle)
{
- m_Protocol->SendDetachEntity(a_Entity, a_PreviousVehicle);
+ m_Protocol.SendDetachEntity(a_Entity, a_PreviousVehicle);
}
@@ -2350,14 +2131,27 @@ void cClientHandle::SendDetachEntity(const cEntity & a_Entity, const cEntity & a
void cClientHandle::SendDisconnect(const AString & a_Reason)
{
- // Destruction (Destroy()) is called when the client disconnects, not when a disconnect packet (or anything else) is sent
- // Otherwise, the cClientHandle instance is can be unexpectedly removed from the associated player - Core/#142
- if (!m_HasSentDC)
+ // Lemma 1: All outgoing data is queued to be sent on the server tick thread.
+ // Lemma 2: All link-closing actions will set state to destroyed.
+ // Lemma 3: A call to Destroy() is guaranteed to prevent the send queue from being processed.
+ // Corollary: Prevents situations where the client was signalled to disconnect - Kick() - and suddenly a wild packet appears (#31)
+ // Note: Kick packets are sent explicitly by flushing the queue; TODO: probably fine?
+
+ LOGD("Sending a DC: \"%s\"", StripColorCodes(a_Reason).c_str());
+
{
- LOGD("Sending a DC: \"%s\"", StripColorCodes(a_Reason).c_str());
- m_Protocol->SendDisconnect(a_Reason);
- m_HasSentDC = true;
+ cCSLock Lock(m_CSLink);
+ m_OutgoingData.clear();
+ m_Protocol.SendDisconnect(a_Reason);
+ ProcessQueuedOutgoingData();
}
+
+ cRoot::Get()->GetServer()->QueueTask(
+ [ClientHandle = shared_from_this()]
+ {
+ ClientHandle->Destroy();
+ }
+ );
}
@@ -2367,7 +2161,7 @@ void cClientHandle::SendDisconnect(const AString & a_Reason)
void cClientHandle::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
{
m_LastPlacedSign.Set(a_BlockX, a_BlockY, a_BlockZ);
- m_Protocol->SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
+ m_Protocol.SendEditSign(a_BlockX, a_BlockY, a_BlockZ);
}
@@ -2376,7 +2170,7 @@ void cClientHandle::SendEditSign(int a_BlockX, int a_BlockY, int a_BlockZ)
void cClientHandle::SendEntityEffect(const cEntity & a_Entity, int a_EffectID, int a_Amplifier, short a_Duration)
{
- m_Protocol->SendEntityEffect(a_Entity, a_EffectID, a_Amplifier, a_Duration);
+ m_Protocol.SendEntityEffect(a_Entity, a_EffectID, a_Amplifier, a_Duration);
}
@@ -2385,7 +2179,7 @@ void cClientHandle::SendEntityEffect(const cEntity & a_Entity, int a_EffectID, i
void cClientHandle::SendEntityEquipment(const cEntity & a_Entity, short a_SlotNum, const cItem & a_Item)
{
- m_Protocol->SendEntityEquipment(a_Entity, a_SlotNum, a_Item);
+ m_Protocol.SendEntityEquipment(a_Entity, a_SlotNum, a_Item);
}
@@ -2396,7 +2190,7 @@ void cClientHandle::SendEntityHeadLook(const cEntity & a_Entity)
{
ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
- m_Protocol->SendEntityHeadLook(a_Entity);
+ m_Protocol.SendEntityHeadLook(a_Entity);
}
@@ -2407,7 +2201,7 @@ void cClientHandle::SendEntityLook(const cEntity & a_Entity)
{
ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
- m_Protocol->SendEntityLook(a_Entity);
+ m_Protocol.SendEntityLook(a_Entity);
}
@@ -2416,7 +2210,7 @@ void cClientHandle::SendEntityLook(const cEntity & a_Entity)
void cClientHandle::SendEntityMetadata(const cEntity & a_Entity)
{
- m_Protocol->SendEntityMetadata(a_Entity);
+ m_Protocol.SendEntityMetadata(a_Entity);
}
@@ -2427,7 +2221,7 @@ void cClientHandle::SendEntityRelMove(const cEntity & a_Entity, char a_RelX, cha
{
ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
- m_Protocol->SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ);
+ m_Protocol.SendEntityRelMove(a_Entity, a_RelX, a_RelY, a_RelZ);
}
@@ -2438,7 +2232,7 @@ void cClientHandle::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX,
{
ASSERT(a_Entity.GetUniqueID() != m_Player->GetUniqueID()); // Must not send for self
- m_Protocol->SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ);
+ m_Protocol.SendEntityRelMoveLook(a_Entity, a_RelX, a_RelY, a_RelZ);
}
@@ -2447,7 +2241,7 @@ void cClientHandle::SendEntityRelMoveLook(const cEntity & a_Entity, char a_RelX,
void cClientHandle::SendEntityStatus(const cEntity & a_Entity, char a_Status)
{
- m_Protocol->SendEntityStatus(a_Entity, a_Status);
+ m_Protocol.SendEntityStatus(a_Entity, a_Status);
}
@@ -2456,7 +2250,7 @@ void cClientHandle::SendEntityStatus(const cEntity & a_Entity, char a_Status)
void cClientHandle::SendEntityVelocity(const cEntity & a_Entity)
{
- m_Protocol->SendEntityVelocity(a_Entity);
+ m_Protocol.SendEntityVelocity(a_Entity);
}
@@ -2474,7 +2268,7 @@ void cClientHandle::SendExplosion(double a_BlockX, double a_BlockY, double a_Blo
// Update the statistics:
m_NumExplosionsThisTick++;
- m_Protocol->SendExplosion(a_BlockX, a_BlockY, a_BlockZ, a_Radius, a_BlocksAffected, a_PlayerMotion);
+ m_Protocol.SendExplosion(a_BlockX, a_BlockY, a_BlockZ, a_Radius, a_BlocksAffected, a_PlayerMotion);
}
@@ -2483,7 +2277,7 @@ void cClientHandle::SendExplosion(double a_BlockX, double a_BlockY, double a_Blo
void cClientHandle::SendGameMode(eGameMode a_GameMode)
{
- m_Protocol->SendGameMode(a_GameMode);
+ m_Protocol.SendGameMode(a_GameMode);
}
@@ -2492,7 +2286,7 @@ void cClientHandle::SendGameMode(eGameMode a_GameMode)
void cClientHandle::SendHealth(void)
{
- m_Protocol->SendHealth();
+ m_Protocol.SendHealth();
}
@@ -2501,7 +2295,7 @@ void cClientHandle::SendHealth(void)
void cClientHandle::SendHideTitle(void)
{
- m_Protocol->SendHideTitle();
+ m_Protocol.SendHideTitle();
}
@@ -2510,7 +2304,7 @@ void cClientHandle::SendHideTitle(void)
void cClientHandle::SendInventorySlot(char a_WindowID, short a_SlotNum, const cItem & a_Item)
{
- m_Protocol->SendInventorySlot(a_WindowID, a_SlotNum, a_Item);
+ m_Protocol.SendInventorySlot(a_WindowID, a_SlotNum, a_Item);
}
@@ -2519,7 +2313,7 @@ void cClientHandle::SendInventorySlot(char a_WindowID, short a_SlotNum, const cI
void cClientHandle::SendMapData(const cMap & a_Map, int a_DataStartX, int a_DataStartY)
{
- m_Protocol->SendMapData(a_Map, a_DataStartX, a_DataStartY);
+ m_Protocol.SendMapData(a_Map, a_DataStartX, a_DataStartY);
}
@@ -2528,7 +2322,7 @@ void cClientHandle::SendMapData(const cMap & a_Map, int a_DataStartX, int a_Data
void cClientHandle::SendParticleEffect(const AString & a_ParticleName, float a_SrcX, float a_SrcY, float a_SrcZ, float a_OffsetX, float a_OffsetY, float a_OffsetZ, float a_ParticleData, int a_ParticleAmount)
{
- m_Protocol->SendParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmount);
+ m_Protocol.SendParticleEffect(a_ParticleName, a_SrcX, a_SrcY, a_SrcZ, a_OffsetX, a_OffsetY, a_OffsetZ, a_ParticleData, a_ParticleAmount);
}
@@ -2537,7 +2331,7 @@ void cClientHandle::SendParticleEffect(const AString & a_ParticleName, float a_S
void cClientHandle::SendParticleEffect(const AString & a_ParticleName, const Vector3f a_Src, const Vector3f a_Offset, float a_ParticleData, int a_ParticleAmount, std::array<int, 2> a_Data)
{
- m_Protocol->SendParticleEffect(a_ParticleName, a_Src, a_Offset, a_ParticleData, a_ParticleAmount, a_Data);
+ m_Protocol.SendParticleEffect(a_ParticleName, a_Src, a_Offset, a_ParticleData, a_ParticleAmount, a_Data);
}
@@ -2546,7 +2340,7 @@ void cClientHandle::SendParticleEffect(const AString & a_ParticleName, const Vec
void cClientHandle::SendPickupSpawn(const cPickup & a_Pickup)
{
- m_Protocol->SendPickupSpawn(a_Pickup);
+ m_Protocol.SendPickupSpawn(a_Pickup);
}
@@ -2554,7 +2348,7 @@ void cClientHandle::SendPickupSpawn(const cPickup & a_Pickup)
void cClientHandle::SendPaintingSpawn(const cPainting & a_Painting)
{
- m_Protocol->SendPaintingSpawn(a_Painting);
+ m_Protocol.SendPaintingSpawn(a_Painting);
}
@@ -2563,7 +2357,7 @@ void cClientHandle::SendPaintingSpawn(const cPainting & a_Painting)
void cClientHandle::SendEntityAnimation(const cEntity & a_Entity, char a_Animation)
{
- m_Protocol->SendEntityAnimation(a_Entity, a_Animation);
+ m_Protocol.SendEntityAnimation(a_Entity, a_Animation);
}
@@ -2572,7 +2366,7 @@ void cClientHandle::SendEntityAnimation(const cEntity & a_Entity, char a_Animati
void cClientHandle::SendPlayerAbilities()
{
- m_Protocol->SendPlayerAbilities();
+ m_Protocol.SendPlayerAbilities();
}
@@ -2581,7 +2375,7 @@ void cClientHandle::SendPlayerAbilities()
void cClientHandle::SendPlayerListAddPlayer(const cPlayer & a_Player)
{
- m_Protocol->SendPlayerListAddPlayer(a_Player);
+ m_Protocol.SendPlayerListAddPlayer(a_Player);
}
@@ -2590,7 +2384,7 @@ void cClientHandle::SendPlayerListAddPlayer(const cPlayer & a_Player)
void cClientHandle::SendPlayerListRemovePlayer(const cPlayer & a_Player)
{
- m_Protocol->SendPlayerListRemovePlayer(a_Player);
+ m_Protocol.SendPlayerListRemovePlayer(a_Player);
}
@@ -2599,7 +2393,7 @@ void cClientHandle::SendPlayerListRemovePlayer(const cPlayer & a_Player)
void cClientHandle::SendPlayerListUpdateGameMode(const cPlayer & a_Player)
{
- m_Protocol->SendPlayerListUpdateGameMode(a_Player);
+ m_Protocol.SendPlayerListUpdateGameMode(a_Player);
}
@@ -2608,7 +2402,7 @@ void cClientHandle::SendPlayerListUpdateGameMode(const cPlayer & a_Player)
void cClientHandle::SendPlayerListUpdatePing(const cPlayer & a_Player)
{
- m_Protocol->SendPlayerListUpdatePing(a_Player);
+ m_Protocol.SendPlayerListUpdatePing(a_Player);
}
@@ -2617,7 +2411,7 @@ void cClientHandle::SendPlayerListUpdatePing(const cPlayer & a_Player)
void cClientHandle::SendPlayerListUpdateDisplayName(const cPlayer & a_Player, const AString & a_CustomName)
{
- m_Protocol->SendPlayerListUpdateDisplayName(a_Player, a_CustomName);
+ m_Protocol.SendPlayerListUpdateDisplayName(a_Player, a_CustomName);
}
@@ -2626,7 +2420,7 @@ void cClientHandle::SendPlayerListUpdateDisplayName(const cPlayer & a_Player, co
void cClientHandle::SendPlayerMaxSpeed(void)
{
- m_Protocol->SendPlayerMaxSpeed();
+ m_Protocol.SendPlayerMaxSpeed();
}
@@ -2640,7 +2434,7 @@ void cClientHandle::SendPlayerMoveLook(void)
m_Player->GetPosX(), m_Player->GetPosY(), m_Player->GetPosZ(), m_Player->GetStance(), m_Player->IsOnGround() ? 1 : 0
);
*/
- m_Protocol->SendPlayerMoveLook();
+ m_Protocol.SendPlayerMoveLook();
}
@@ -2649,7 +2443,7 @@ void cClientHandle::SendPlayerMoveLook(void)
void cClientHandle::SendPlayerPosition(void)
{
- m_Protocol->SendPlayerPosition();
+ m_Protocol.SendPlayerPosition();
}
@@ -2665,10 +2459,10 @@ void cClientHandle::SendPlayerSpawn(const cPlayer & a_Player)
}
LOGD("Spawning player \"%s\" on client \"%s\" @ %s",
- a_Player.GetName().c_str(), GetPlayer()->GetName().c_str(), GetIPString().c_str()
+ a_Player.GetName().c_str(), m_Player->GetName().c_str(), GetIPString().c_str()
);
- m_Protocol->SendPlayerSpawn(a_Player);
+ m_Protocol.SendPlayerSpawn(a_Player);
}
@@ -2677,7 +2471,7 @@ void cClientHandle::SendPlayerSpawn(const cPlayer & a_Player)
void cClientHandle::SendPluginMessage(const AString & a_Channel, const AString & a_Message)
{
- m_Protocol->SendPluginMessage(a_Channel, a_Message);
+ m_Protocol.SendPluginMessage(a_Channel, a_Message);
}
@@ -2686,7 +2480,7 @@ void cClientHandle::SendPluginMessage(const AString & a_Channel, const AString &
void cClientHandle::SendRemoveEntityEffect(const cEntity & a_Entity, int a_EffectID)
{
- m_Protocol->SendRemoveEntityEffect(a_Entity, a_EffectID);
+ m_Protocol.SendRemoveEntityEffect(a_Entity, a_EffectID);
}
@@ -2695,7 +2489,7 @@ void cClientHandle::SendRemoveEntityEffect(const cEntity & a_Entity, int a_Effec
void cClientHandle::SendResetTitle()
{
- m_Protocol->SendResetTitle();
+ m_Protocol.SendResetTitle();
}
@@ -2704,7 +2498,7 @@ void cClientHandle::SendResetTitle()
void cClientHandle::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimensionChecks)
{
- m_Protocol->SendRespawn(a_Dimension, a_ShouldIgnoreDimensionChecks);
+ m_Protocol.SendRespawn(a_Dimension, a_ShouldIgnoreDimensionChecks);
}
@@ -2713,7 +2507,7 @@ void cClientHandle::SendRespawn(eDimension a_Dimension, bool a_ShouldIgnoreDimen
void cClientHandle::SendExperience(void)
{
- m_Protocol->SendExperience();
+ m_Protocol.SendExperience();
}
@@ -2722,7 +2516,7 @@ void cClientHandle::SendExperience(void)
void cClientHandle::SendExperienceOrb(const cExpOrb & a_ExpOrb)
{
- m_Protocol->SendExperienceOrb(a_ExpOrb);
+ m_Protocol.SendExperienceOrb(a_ExpOrb);
}
@@ -2731,7 +2525,7 @@ void cClientHandle::SendExperienceOrb(const cExpOrb & a_ExpOrb)
void cClientHandle::SendScoreboardObjective(const AString & a_Name, const AString & a_DisplayName, Byte a_Mode)
{
- m_Protocol->SendScoreboardObjective(a_Name, a_DisplayName, a_Mode);
+ m_Protocol.SendScoreboardObjective(a_Name, a_DisplayName, a_Mode);
}
@@ -2740,7 +2534,7 @@ void cClientHandle::SendScoreboardObjective(const AString & a_Name, const AStrin
void cClientHandle::SendScoreUpdate(const AString & a_Objective, const AString & a_Player, cObjective::Score a_Score, Byte a_Mode)
{
- m_Protocol->SendScoreUpdate(a_Objective, a_Player, a_Score, a_Mode);
+ m_Protocol.SendScoreUpdate(a_Objective, a_Player, a_Score, a_Mode);
}
@@ -2749,7 +2543,7 @@ void cClientHandle::SendScoreUpdate(const AString & a_Objective, const AString &
void cClientHandle::SendDisplayObjective(const AString & a_Objective, cScoreboard::eDisplaySlot a_Display)
{
- m_Protocol->SendDisplayObjective(a_Objective, a_Display);
+ m_Protocol.SendDisplayObjective(a_Objective, a_Display);
}
@@ -2758,7 +2552,7 @@ void cClientHandle::SendDisplayObjective(const AString & a_Objective, cScoreboar
void cClientHandle::SendSetSubTitle(const cCompositeChat & a_SubTitle)
{
- m_Protocol->SendSetSubTitle(a_SubTitle);
+ m_Protocol.SendSetSubTitle(a_SubTitle);
}
@@ -2767,7 +2561,7 @@ void cClientHandle::SendSetSubTitle(const cCompositeChat & a_SubTitle)
void cClientHandle::SendSetRawSubTitle(const AString & a_SubTitle)
{
- m_Protocol->SendSetRawSubTitle(a_SubTitle);
+ m_Protocol.SendSetRawSubTitle(a_SubTitle);
}
@@ -2776,7 +2570,7 @@ void cClientHandle::SendSetRawSubTitle(const AString & a_SubTitle)
void cClientHandle::SendSetTitle(const cCompositeChat & a_Title)
{
- m_Protocol->SendSetTitle(a_Title);
+ m_Protocol.SendSetTitle(a_Title);
}
@@ -2785,7 +2579,7 @@ void cClientHandle::SendSetTitle(const cCompositeChat & a_Title)
void cClientHandle::SendSetRawTitle(const AString & a_Title)
{
- m_Protocol->SendSetRawTitle(a_Title);
+ m_Protocol.SendSetRawTitle(a_Title);
}
@@ -2794,7 +2588,7 @@ void cClientHandle::SendSetRawTitle(const AString & a_Title)
void cClientHandle::SendSoundEffect(const AString & a_SoundName, double a_X, double a_Y, double a_Z, float a_Volume, float a_Pitch)
{
- m_Protocol->SendSoundEffect(a_SoundName, a_X, a_Y, a_Z, a_Volume, a_Pitch);
+ m_Protocol.SendSoundEffect(a_SoundName, a_X, a_Y, a_Z, a_Volume, a_Pitch);
}
@@ -2803,7 +2597,7 @@ void cClientHandle::SendSoundEffect(const AString & a_SoundName, double a_X, dou
void cClientHandle::SendSoundParticleEffect(const EffectID a_EffectID, int a_SrcX, int a_SrcY, int a_SrcZ, int a_Data)
{
- m_Protocol->SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data);
+ m_Protocol.SendSoundParticleEffect(a_EffectID, a_SrcX, a_SrcY, a_SrcZ, a_Data);
}
@@ -2812,7 +2606,7 @@ void cClientHandle::SendSoundParticleEffect(const EffectID a_EffectID, int a_Src
void cClientHandle::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
{
- m_Protocol->SendSpawnFallingBlock(a_FallingBlock);
+ m_Protocol.SendSpawnFallingBlock(a_FallingBlock);
}
@@ -2821,7 +2615,7 @@ void cClientHandle::SendSpawnFallingBlock(const cFallingBlock & a_FallingBlock)
void cClientHandle::SendSpawnMob(const cMonster & a_Mob)
{
- m_Protocol->SendSpawnMob(a_Mob);
+ m_Protocol.SendSpawnMob(a_Mob);
}
@@ -2830,7 +2624,7 @@ void cClientHandle::SendSpawnMob(const cMonster & a_Mob)
void cClientHandle::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType, int a_ObjectData, Byte a_Yaw, Byte a_Pitch)
{
- m_Protocol->SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch);
+ m_Protocol.SendSpawnObject(a_Entity, a_ObjectType, a_ObjectData, a_Yaw, a_Pitch);
}
@@ -2839,7 +2633,7 @@ void cClientHandle::SendSpawnObject(const cEntity & a_Entity, char a_ObjectType,
void cClientHandle::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleType, char a_VehicleSubType) // VehicleSubType is specific to Minecarts
{
- m_Protocol->SendSpawnVehicle(a_Vehicle, a_VehicleType, a_VehicleSubType);
+ m_Protocol.SendSpawnVehicle(a_Vehicle, a_VehicleType, a_VehicleSubType);
}
@@ -2848,7 +2642,7 @@ void cClientHandle::SendSpawnVehicle(const cEntity & a_Vehicle, char a_VehicleTy
void cClientHandle::SendStatistics(const cStatManager & a_Manager)
{
- m_Protocol->SendStatistics(a_Manager);
+ m_Protocol.SendStatistics(a_Manager);
}
@@ -2857,7 +2651,7 @@ void cClientHandle::SendStatistics(const cStatManager & a_Manager)
void cClientHandle::SendTabCompletionResults(const AStringVector & a_Results)
{
- m_Protocol->SendTabCompletionResults(a_Results);
+ m_Protocol.SendTabCompletionResults(a_Results);
}
@@ -2866,7 +2660,7 @@ void cClientHandle::SendTabCompletionResults(const AStringVector & a_Results)
void cClientHandle::SendTeleportEntity(const cEntity & a_Entity)
{
- m_Protocol->SendTeleportEntity(a_Entity);
+ m_Protocol.SendTeleportEntity(a_Entity);
}
@@ -2875,7 +2669,7 @@ void cClientHandle::SendTeleportEntity(const cEntity & a_Entity)
void cClientHandle::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
{
- m_Protocol->SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ);
+ m_Protocol.SendThunderbolt(a_BlockX, a_BlockY, a_BlockZ);
}
@@ -2884,7 +2678,7 @@ void cClientHandle::SendThunderbolt(int a_BlockX, int a_BlockY, int a_BlockZ)
void cClientHandle::SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_FadeOutTicks)
{
- m_Protocol->SendTitleTimes(a_FadeInTicks, a_DisplayTicks, a_FadeOutTicks);
+ m_Protocol.SendTitleTimes(a_FadeInTicks, a_DisplayTicks, a_FadeOutTicks);
}
@@ -2893,22 +2687,7 @@ void cClientHandle::SendTitleTimes(int a_FadeInTicks, int a_DisplayTicks, int a_
void cClientHandle::SendTimeUpdate(Int64 a_WorldAge, Int64 a_TimeOfDay, bool a_DoDaylightCycle)
{
- m_Protocol->SendTimeUpdate(a_WorldAge, a_TimeOfDay, a_DoDaylightCycle);
-}
-
-
-
-
-
-void cClientHandle::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
-{
- // Remove the chunk from the list of chunks sent to the client:
- {
- cCSLock Lock(m_CSChunkLists);
- m_SentChunks.remove(cChunkCoords(a_ChunkX, a_ChunkZ));
- }
-
- m_Protocol->SendUnloadChunk(a_ChunkX, a_ChunkZ);
+ m_Protocol.SendTimeUpdate(a_WorldAge, a_TimeOfDay, a_DoDaylightCycle);
}
@@ -2916,7 +2695,7 @@ void cClientHandle::SendUnloadChunk(int a_ChunkX, int a_ChunkZ)
void cClientHandle::SendUpdateBlockEntity(cBlockEntity & a_BlockEntity)
{
- m_Protocol->SendUpdateBlockEntity(a_BlockEntity);
+ m_Protocol.SendUpdateBlockEntity(a_BlockEntity);
}
@@ -2928,7 +2707,7 @@ void cClientHandle::SendUpdateSign(
const AString & a_Line1, const AString & a_Line2, const AString & a_Line3, const AString & a_Line4
)
{
- m_Protocol->SendUpdateSign(
+ m_Protocol.SendUpdateSign(
a_BlockX, a_BlockY, a_BlockZ,
a_Line1, a_Line2, a_Line3, a_Line4
);
@@ -2940,7 +2719,7 @@ void cClientHandle::SendUpdateSign(
void cClientHandle::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_BlockY, int a_BlockZ)
{
- m_Protocol->SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ);
+ m_Protocol.SendUseBed(a_Entity, a_BlockX, a_BlockY, a_BlockZ);
}
@@ -2948,7 +2727,7 @@ void cClientHandle::SendUseBed(const cEntity & a_Entity, int a_BlockX, int a_Blo
void cClientHandle::SendWeather(eWeather a_Weather)
{
- m_Protocol->SendWeather(a_Weather);
+ m_Protocol.SendWeather(a_Weather);
}
@@ -2957,7 +2736,7 @@ void cClientHandle::SendWeather(eWeather a_Weather)
void cClientHandle::SendWholeInventory(const cWindow & a_Window)
{
- m_Protocol->SendWholeInventory(a_Window);
+ m_Protocol.SendWholeInventory(a_Window);
}
@@ -2966,7 +2745,7 @@ void cClientHandle::SendWholeInventory(const cWindow & a_Window)
void cClientHandle::SendWindowClose(const cWindow & a_Window)
{
- m_Protocol->SendWindowClose(a_Window);
+ m_Protocol.SendWindowClose(a_Window);
}
@@ -2975,7 +2754,7 @@ void cClientHandle::SendWindowClose(const cWindow & a_Window)
void cClientHandle::SendWindowOpen(const cWindow & a_Window)
{
- m_Protocol->SendWindowOpen(a_Window);
+ m_Protocol.SendWindowOpen(a_Window);
}
@@ -2984,7 +2763,7 @@ void cClientHandle::SendWindowOpen(const cWindow & a_Window)
void cClientHandle::SendWindowProperty(const cWindow & a_Window, short a_Property, short a_Value)
{
- m_Protocol->SendWindowProperty(a_Window, a_Property, a_Value);
+ m_Protocol.SendWindowProperty(a_Window, a_Property, a_Value);
}
@@ -3011,15 +2790,19 @@ void cClientHandle::SetUsername( const AString & a_Username)
void cClientHandle::SetViewDistance(int a_ViewDistance)
{
+ ASSERT(cRoot::Get()->GetServer()->IsInTickThread());
+ ASSERT(m_Player != nullptr);
+
m_RequestedViewDistance = a_ViewDistance;
LOGD("%s is requesting ViewDistance of %d!", GetUsername().c_str(), m_RequestedViewDistance);
- // Set the current view distance based on the requested VD and world max VD:
- cWorld * world = m_Player->GetWorld();
- if (world != nullptr)
- {
- m_CurrentViewDistance = Clamp(a_ViewDistance, cClientHandle::MIN_VIEW_DISTANCE, world->GetMaxViewDistance());
- }
+ cWorld * World = m_Player->GetWorld();
+ World->QueueTask(
+ [a_ViewDistance, ClientHandle = shared_from_this()](cWorld & a_World)
+ {
+ ClientHandle->m_CurrentViewDistance = Clamp(a_ViewDistance, cClientHandle::MIN_VIEW_DISTANCE, a_World.GetMaxViewDistance());
+ }
+ );
}
@@ -3035,40 +2818,6 @@ bool cClientHandle::HasPluginChannel(const AString & a_PluginChannel)
-bool cClientHandle::WantsSendChunk(int a_ChunkX, int a_ChunkZ)
-{
- if (m_State >= csDestroying)
- {
- return false;
- }
-
- cCSLock Lock(m_CSChunkLists);
- return m_ChunksToSend.find(cChunkCoords(a_ChunkX, a_ChunkZ)) != m_ChunksToSend.end();
-}
-
-
-
-
-
-void cClientHandle::AddWantedChunk(int a_ChunkX, int a_ChunkZ)
-{
- if (m_State >= csDestroying)
- {
- return;
- }
-
- LOGD("Adding chunk [%d, %d] to wanted chunks for client %p", a_ChunkX, a_ChunkZ, static_cast<void *>(this));
- cCSLock Lock(m_CSChunkLists);
- if (m_ChunksToSend.find(cChunkCoords(a_ChunkX, a_ChunkZ)) == m_ChunksToSend.end())
- {
- m_ChunksToSend.emplace(a_ChunkX, a_ChunkZ);
- }
-}
-
-
-
-
-
void cClientHandle::PacketBufferFull(void)
{
// Too much data in the incoming queue, the server is probably too busy, kick the client:
@@ -3107,33 +2856,18 @@ void cClientHandle::SocketClosed(void)
{
// The socket has been closed for any reason
- if (!m_Username.empty()) // Ignore client pings
- {
- LOGD("Client %s @ %s disconnected", m_Username.c_str(), m_IPString.c_str());
- cRoot::Get()->GetPluginManager()->CallHookDisconnect(*this, "Player disconnected");
- }
- if (m_Player != nullptr)
- {
- m_Player->GetWorld()->QueueTask([this](cWorld & World)
+ cRoot::Get()->GetServer()->QueueTask(
+ [ClientHandle = shared_from_this()]
{
- UNUSED(World);
- Destroy();
- });
- }
- else
- {
- Destroy();
- }
-}
-
-
-
-
+ if (!ClientHandle->GetUsername().empty()) // Ignore client pings
+ {
+ LOGD("Client %s @ %s disconnected", ClientHandle->GetUsername().c_str(), ClientHandle->GetIPString().c_str());
+ cRoot::Get()->GetPluginManager()->CallHookDisconnect(*ClientHandle, "Player disconnected");
+ }
-void cClientHandle::SetSelf(cClientHandlePtr a_Self)
-{
- ASSERT(m_Self == nullptr);
- m_Self = a_Self;
+ ClientHandle->Destroy();
+ }
+ );
}
@@ -3142,6 +2876,7 @@ void cClientHandle::SetSelf(cClientHandlePtr a_Self)
void cClientHandle::OnLinkCreated(cTCPLinkPtr a_Link)
{
+ cCSLock Lock(m_CSLink);
m_Link = a_Link;
}
@@ -3165,10 +2900,6 @@ void cClientHandle::OnReceivedData(const char * a_Data, size_t a_Length)
void cClientHandle::OnRemoteClosed(void)
{
- {
- cCSLock Lock(m_CSOutgoingData);
- m_Link.reset();
- }
SocketClosed();
}
@@ -3181,10 +2912,6 @@ void cClientHandle::OnError(int a_ErrorCode, const AString & a_ErrorMsg)
LOGD("An error has occurred on client link for %s @ %s: %d (%s). Client disconnected.",
m_Username.c_str(), m_IPString.c_str(), a_ErrorCode, a_ErrorMsg.c_str()
);
- {
- cCSLock Lock(m_CSOutgoingData);
- m_Link.reset();
- }
SocketClosed();
}