summaryrefslogtreecommitdiffstats
path: root/src/Server.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/Server.cpp129
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[]