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,30 +154,23 @@ void cClientHandle::Destroy(void)
m_Link.reset(); 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 if (!SetState(csDestroying))
bool WasAddedToWorld = false;
{
cCSLock Lock(m_CSState);
WasAddedToWorld = (m_State >= csAuthenticated);
if (m_State >= csDestroying)
{ {
// Already called // Already called
LOGD("%s: client %p, \"%s\" already destroyed, bailing out", __FUNCTION__, static_cast<void *>(this), m_Username.c_str()); LOGD("%s: client %p, \"%s\" already destroyed, bailing out", __FUNCTION__, static_cast<void *>(this), m_Username.c_str());
return; return;
} }
m_State = csDestroying;
}
LOGD("%s: destroying client %p, \"%s\" @ %s", __FUNCTION__, static_cast<void *>(this), m_Username.c_str(), m_IPString.c_str()); LOGD("%s: destroying client %p, \"%s\" @ %s", __FUNCTION__, static_cast<void *>(this), m_Username.c_str(), m_IPString.c_str());
auto player = m_Player; auto player = m_Player;
m_Self.reset(); 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); return;
m_State = csDestroyed; // Tick thread is allowed to call destructor async at any time after this
} }
if (player != nullptr)
{
// Atomically decrement player count (in world or server thread) // Atomically decrement player count (in world or server thread)
cRoot::Get()->GetServer()->PlayerDestroyed(); cRoot::Get()->GetServer()->PlayerDestroyed();
@ -187,10 +180,10 @@ void cClientHandle::Destroy(void)
player->StopEveryoneFromTargetingMe(); player->StopEveryoneFromTargetingMe();
player->SetIsTicking(false); player->SetIsTicking(false);
if (WasAddedToWorld) if (!m_PlayerPtr)
{ {
// If ownership was transferred, our own smart pointer should be unset // If our own smart pointer is unset, player has been transferred to world
ASSERT(!m_PlayerPtr); ASSERT(world->IsPlayerReferencedInWorldOrChunk(*player));
m_PlayerPtr = world->RemovePlayer(*player); m_PlayerPtr = world->RemovePlayer(*player);
@ -200,13 +193,11 @@ void cClientHandle::Destroy(void)
else else
{ {
// If ownership was not transferred, our own smart pointer should be valid and RemovePlayer's should not // If ownership was not transferred, our own smart pointer should be valid and RemovePlayer's should not
ASSERT(m_PlayerPtr);
ASSERT(!world->IsPlayerReferencedInWorldOrChunk(*player)); 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()->BroadcastPlayerListsAddPlayer(*m_Player);
cRoot::Get()->SendPlayerLists(m_Player); cRoot::Get()->SendPlayerLists(m_Player);
m_State = csAuthenticated; SetState(csAuthenticated);
} }
// Query player team // 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 // 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 // disconnect packet data is sent in the tick thread so the connection
// is closed there after the data is sent. // 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: // Queue self for destruction:
cCSLock lock(m_CSState); SetState(csQueuedForDestruction);
m_State = 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) void cClientHandle::ProcessProtocolInOut(void)
{ {
// Process received network data: // Process received network data:

View File

@ -591,6 +591,10 @@ private:
/** Called right after the instance is created to store its SharedPtr inside. */ /** Called right after the instance is created to store its SharedPtr inside. */
void SetSelf(cClientHandlePtr a_Self); 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. /** Processes the data in the network input and output buffers.
Called by both Tick() and ServerTick(). */ Called by both Tick() and ServerTick(). */
void ProcessProtocolInOut(void); void ProcessProtocolInOut(void);