diff options
author | Tiger Wang <ziwei.tiger@hotmail.co.uk> | 2016-08-21 17:44:25 +0200 |
---|---|---|
committer | Tiger Wang <ziwei.tiger@hotmail.co.uk> | 2016-08-21 17:44:25 +0200 |
commit | 71035a8706aa89d6cd12f25fea5191fb35787406 (patch) | |
tree | b7bcd071756ce9cc0ac620292fc4f80225913471 /src/ClientHandle.cpp | |
parent | test (diff) | |
download | cuberite-71035a8706aa89d6cd12f25fea5191fb35787406.tar cuberite-71035a8706aa89d6cd12f25fea5191fb35787406.tar.gz cuberite-71035a8706aa89d6cd12f25fea5191fb35787406.tar.bz2 cuberite-71035a8706aa89d6cd12f25fea5191fb35787406.tar.lz cuberite-71035a8706aa89d6cd12f25fea5191fb35787406.tar.xz cuberite-71035a8706aa89d6cd12f25fea5191fb35787406.tar.zst cuberite-71035a8706aa89d6cd12f25fea5191fb35787406.zip |
Diffstat (limited to 'src/ClientHandle.cpp')
-rw-r--r-- | src/ClientHandle.cpp | 72 |
1 files changed, 49 insertions, 23 deletions
diff --git a/src/ClientHandle.cpp b/src/ClientHandle.cpp index 8ae81f645..f4aa3e0c5 100644 --- a/src/ClientHandle.cpp +++ b/src/ClientHandle.cpp @@ -104,24 +104,7 @@ cClientHandle::~cClientHandle() { ASSERT(m_State == eState::csDestroyed); // Has Destroy() been called? - if (m_Player != nullptr) - { - cWorld * World = m_Player->GetWorld(); - - // 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) - { - a_World.RemovePlayer(m_Player); - a_World.BroadcastPlayerListRemovePlayer(*m_Player); - m_Player->Destroy(); - } - ); - } + // Note: don't handle player destruction here because we don't own them, so problems arise during shutdown LOGD("Deletied client \"%s\" at %p", GetUsername().c_str(), static_cast<void *>(this)); } @@ -144,6 +127,38 @@ void cClientHandle::Destroy(void) m_Link.reset(); } + if (m_Player != nullptr) + { + cWorld * World = m_Player->GetWorld(); + + // Upon Destroy, the world pointer of a valid player SHALL NOT be null. + // Guaranteed by Destroy and Authenticate being called in the same thread + ASSERT(World != nullptr); + + // We do not leak a player object as Authenticate checks to see if Destroy was called before creating a player + World->QueueTask( + [this](cWorld & a_World) + { + if (m_Player->GetParentChunk() == nullptr) + { + // Player not yet initialised; calling Destroy is not valid + LOGWARN("ParentChunk null"); + return; + } + + if (!cRoot::Get()->GetPluginManager()->CallHookPlayerDestroyed(*m_Player)) + { + cRoot::Get()->BroadcastChatLeave(Printf("%s has left the game", m_Player->GetName().c_str())); + LOGINFO("Player %s has left the game", m_Player->GetName().c_str()); + } + + a_World.RemovePlayer(m_Player); + a_World.BroadcastPlayerListRemovePlayer(*m_Player); + m_Player->Destroy(); + } + ); + } + RemoveFromWorld(); LOGD("%s: client %p, \"%s\"", __FUNCTION__, static_cast<void *>(this), m_Username.c_str()); @@ -309,8 +324,11 @@ void cClientHandle::Kick(const AString & a_Reason) void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID, const Json::Value & a_Properties) { + ASSERT(cRoot::Get()->GetServer()->IsInTickThread()); + if (m_State != eState::csAuthenticating) { + // TODO: is this necessary? return; } @@ -348,12 +366,22 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID, } m_Player->SetIP(m_IPString); + // So Destroy always has a valid world for a valid player + // Additionally, plugins expect world to be set + m_Player->SetWorld(World); + 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); + // We're in the task to create the player - any other QueueTask will execute in the next tick + // So, check to see cClientHandle::Destroy has not been called (State >= IsDestroying where State is monotonic) + if (Client->IsDestroying() || Client->IsDestroyed()) + { + // Destroy called - proceeding now means we may create a player object that's never deleted + LOGWARN("Destroyed"); + return; + } if (!cRoot::Get()->GetPluginManager()->CallHookPlayerJoined(*Player)) { @@ -386,9 +414,7 @@ void cClientHandle::Authenticate(const AString & a_Name, const AString & a_UUID, cRoot::Get()->BroadcastPlayerListsAddPlayer(*Player); cRoot::Get()->SendPlayerLists(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 + // Note: cEnttiy::Initialize takes ownership of the player object Player->Initialize(std::move(PlayerPtr.value), *World); World->AddPlayer(Player); |