1
0

cClientHandle: Only allow m_State to increase (#4533)

* cClientHandle: Only allow m_State to increase

* WasAddedToWorld was incorrect if kicked

* Rewrite cClient::Destroy with a guard clause
This commit is contained in:
peterbell10 2020-03-28 10:44:44 +00:00 committed by GitHub
parent ae4d387f1d
commit 1bc24055d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 50 additions and 41 deletions

View File

@ -154,58 +154,49 @@ void cClientHandle::Destroy(void)
m_Link.reset();
}
// Temporary (#3115-will-fix): variable to keep track of whether the client authenticated and had the opportunity to have ownership transferred to the world
bool WasAddedToWorld = false;
if (!SetState(csDestroying))
{
cCSLock Lock(m_CSState);
WasAddedToWorld = (m_State >= csAuthenticated);
if (m_State >= csDestroying)
{
// Already called
LOGD("%s: client %p, \"%s\" already destroyed, bailing out", __FUNCTION__, static_cast<void *>(this), m_Username.c_str());
return;
}
m_State = csDestroying;
// Already called
LOGD("%s: client %p, \"%s\" already destroyed, bailing out", __FUNCTION__, static_cast<void *>(this), m_Username.c_str());
return;
}
LOGD("%s: destroying client %p, \"%s\" @ %s", __FUNCTION__, static_cast<void *>(this), m_Username.c_str(), m_IPString.c_str());
auto player = m_Player;
m_Self.reset();
SetState(csDestroyed); // Tick thread is allowed to call destructor async at any time after this
if (player == nullptr)
{
cCSLock lock(m_CSState);
m_State = csDestroyed; // Tick thread is allowed to call destructor async at any time after this
return;
}
if (player != nullptr)
{
// Atomically decrement player count (in world or server thread)
cRoot::Get()->GetServer()->PlayerDestroyed();
// Atomically decrement player count (in world or server thread)
cRoot::Get()->GetServer()->PlayerDestroyed();
auto world = player->GetWorld();
if (world != nullptr)
auto world = player->GetWorld();
if (world != nullptr)
{
player->StopEveryoneFromTargetingMe();
player->SetIsTicking(false);
if (!m_PlayerPtr)
{
player->StopEveryoneFromTargetingMe();
player->SetIsTicking(false);
// If our own smart pointer is unset, player has been transferred to world
ASSERT(world->IsPlayerReferencedInWorldOrChunk(*player));
if (WasAddedToWorld)
{
// If ownership was transferred, our own smart pointer should be unset
ASSERT(!m_PlayerPtr);
m_PlayerPtr = world->RemovePlayer(*player);
m_PlayerPtr = world->RemovePlayer(*player);
// And RemovePlayer should have returned a valid smart pointer
ASSERT(m_PlayerPtr);
}
else
{
// If ownership was not transferred, our own smart pointer should be valid and RemovePlayer's should not
ASSERT(m_PlayerPtr);
ASSERT(!world->IsPlayerReferencedInWorldOrChunk(*player));
}
// And RemovePlayer should have returned a valid smart pointer
ASSERT(m_PlayerPtr);
}
else
{
// If ownership was not transferred, our own smart pointer should be valid and RemovePlayer's should not
ASSERT(!world->IsPlayerReferencedInWorldOrChunk(*player));
}
player->RemoveClientHandle();
}
player->RemoveClientHandle();
}
@ -433,7 +424,7 @@ void cClientHandle::FinishAuthenticate(const AString & a_Name, const cUUID & a_U
cRoot::Get()->BroadcastPlayerListsAddPlayer(*m_Player);
cRoot::Get()->SendPlayerLists(m_Player);
m_State = csAuthenticated;
SetState(csAuthenticated);
}
// Query player team
@ -2542,7 +2533,7 @@ void cClientHandle::SendDisconnect(const AString & a_Reason)
// csKicked means m_Link will be shut down on the next tick. The
// disconnect packet data is sent in the tick thread so the connection
// is closed there after the data is sent.
m_State = csKicked;
SetState(csKicked);
}
}
@ -3339,8 +3330,7 @@ void cClientHandle::SocketClosed(void)
}
// Queue self for destruction:
cCSLock lock(m_CSState);
m_State = csQueuedForDestruction;
SetState(csQueuedForDestruction);
}
@ -3357,6 +3347,21 @@ void cClientHandle::SetSelf(cClientHandlePtr a_Self)
bool cClientHandle::SetState(eState a_NewState)
{
cCSLock Lock(m_CSState);
if (a_NewState < m_State)
{
return false; // Can only advance the state machine
}
m_State = a_NewState;
return true;
}
void cClientHandle::ProcessProtocolInOut(void)
{
// Process received network data:

View File

@ -591,6 +591,10 @@ private:
/** Called right after the instance is created to store its SharedPtr inside. */
void SetSelf(cClientHandlePtr a_Self);
/** Called to update m_State.
Only succeeds if a_NewState > m_State, otherwise returns false. */
bool SetState(eState a_NewState);
/** Processes the data in the network input and output buffers.
Called by both Tick() and ServerTick(). */
void ProcessProtocolInOut(void);