diff options
Diffstat (limited to '')
-rw-r--r-- | src/Server.cpp | 129 |
1 files changed, 74 insertions, 55 deletions
diff --git a/src/Server.cpp b/src/Server.cpp index ba469bd3e..33d94e639 100644 --- a/src/Server.cpp +++ b/src/Server.cpp @@ -153,16 +153,6 @@ cServer::cServer(void) : -void cServer::ClientMovedToWorld(const cClientHandle * a_Client) -{ - cCSLock Lock(m_CSClients); - m_ClientsToRemove.push_back(const_cast<cClientHandle *>(a_Client)); -} - - - - - void cServer::PlayerCreated(const cPlayer * a_Player) { UNUSED(a_Player); @@ -300,10 +290,12 @@ void cServer::PrepareKeys(void) cTCPLink::cCallbacksPtr cServer::OnConnectionAccepted(const AString & a_RemoteIPAddress) { LOGD("Client \"%s\" connected!", a_RemoteIPAddress.c_str()); - cClientHandlePtr NewHandle = std::make_shared<cClientHandle>(a_RemoteIPAddress, m_ClientViewDistance); - NewHandle->SetSelf(NewHandle); - cCSLock Lock(m_CSClients); - m_Clients.push_back(NewHandle); + + auto NewHandle = std::make_shared<cClientHandle>(a_RemoteIPAddress, m_ClientViewDistance); + { + cCSLock Lock(m_CSClients); + m_Clients.push_back(NewHandle); + } return NewHandle; } @@ -330,9 +322,16 @@ bool cServer::Tick(float a_Dt) // Let the Root process all the queued commands: cRoot::Get()->TickCommands(); - // Tick all clients not yet assigned to a world: + // Tick all clients: TickClients(a_Dt); + // Process queued tasks: + TickQueuedTasks(); + + // cClientHandle::Destroy guarantees that no more data will be sent after it is called, and this may be from TickQueuedTasks, therefore, + // remove destroyed clients, after processing queued tasks: + ReleaseDestroyedClients(); + if (!m_bRestarting) { return true; @@ -351,41 +350,49 @@ bool cServer::Tick(float a_Dt) void cServer::TickClients(float a_Dt) { - cClientHandlePtrs RemoveClients; + cCSLock Lock(m_CSClients); + for (const auto & Client : m_Clients) { - cCSLock Lock(m_CSClients); + Client->Tick(a_Dt); + } +} - // Remove clients that have moved to a world (the world will be ticking them from now on) - for (auto itr = m_ClientsToRemove.begin(), end = m_ClientsToRemove.end(); itr != end; ++itr) - { - for (auto itrC = m_Clients.begin(), endC = m_Clients.end(); itrC != endC; ++itrC) - { - if (itrC->get() == *itr) - { - m_Clients.erase(itrC); - break; - } - } - } // for itr - m_ClientsToRemove[] - m_ClientsToRemove.clear(); - // Tick the remaining clients, take out those that have been destroyed into RemoveClients - for (auto itr = m_Clients.begin(); itr != m_Clients.end();) - { - if ((*itr)->IsDestroyed()) + + + +void cServer::ReleaseDestroyedClients(void) +{ + cCSLock Lock(m_CSClients); + m_Clients.erase( + std::remove_if( + m_Clients.begin(), + m_Clients.end(), + [](const decltype(m_Clients)::value_type & a_Client) { - // Delete the client later, when CS is not held, to avoid deadlock: https://forum.cuberite.org/thread-374.html - RemoveClients.push_back(*itr); - itr = m_Clients.erase(itr); - continue; + return a_Client->IsDestroyed(); } - (*itr)->ServerTick(a_Dt); - ++itr; - } // for itr - m_Clients[] + ), + m_Clients.end() + ); +} + + + + + +void cServer::TickQueuedTasks(void) +{ + decltype(m_Tasks) Tasks; + { + cCSLock Lock(m_CSTasks); + std::swap(Tasks, m_Tasks); } - // Delete the clients that have been destroyed - RemoveClients.clear(); + for (const auto & Task : Tasks) + { + Task(); + } } @@ -495,7 +502,7 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac { class WorldCallback : public cWorldListCallback { - virtual bool Item(cWorld * a_World) override + virtual bool Item(cWorld * a_EntityWorld) override { class EntityCallback : public cEntityCallback { @@ -507,8 +514,15 @@ void cServer::ExecuteConsoleCommand(const AString & a_Cmd, cCommandOutputCallbac } return false; } - } EC; - a_World->ForEachEntity(EC); + }; + + a_EntityWorld->QueueTask( + [](cWorld & a_World) + { + EntityCallback EC; + a_World.ForEachEntity(EC); + } + ); return false; } } WC; @@ -646,6 +660,16 @@ void cServer::BindBuiltInConsoleCommands(void) void cServer::Shutdown(void) { + { + // Notify all clients of the shutdown: + cCSLock Lock(m_CSClients); + for (const auto & Client : m_Clients) + { + Client->StopPlayerDestructionManagement(); + // TODO: send "Server shut down: kthnxbai!" + } + } + // Stop listening on all sockets: for (auto srv: m_ServerHandles) { @@ -653,19 +677,14 @@ void cServer::Shutdown(void) } m_ServerHandles.clear(); + // At this point, OnRemoteClosed will eventually be called for the clienthandle before it is deleted + // No need to manually call Destroy + // Notify the tick thread and wait for it to terminate: m_bRestarting = true; m_RestartEvent.Wait(); cRoot::Get()->SaveAllChunks(); - - // Remove all clients: - cCSLock Lock(m_CSClients); - for (auto itr = m_Clients.begin(); itr != m_Clients.end(); ++itr) - { - (*itr)->Destroy(); - } - m_Clients.clear(); } @@ -695,7 +714,7 @@ void cServer::AuthenticateUser(int a_ClientID, const AString & a_Name, const ASt { if ((*itr)->GetUniqueID() == a_ClientID) { - (*itr)->Authenticate(a_Name, a_UUID, a_Properties); + QueueTask(std::bind(&cClientHandle::Authenticate, *itr, a_Name, a_UUID, a_Properties)); return; } } // for itr - m_Clients[] |