From 8c3837987bd5f74563790c15a1d52755383135ae Mon Sep 17 00:00:00 2001 From: madmaxoft Date: Wed, 14 Aug 2013 10:24:34 +0200 Subject: Player counts are now properly handled. Fixes #80 --- source/Bindings.cpp | 6 +++--- source/Bindings.h | 2 +- source/ClientHandle.cpp | 38 +++++++++++++++++++++++++++++++++++++- source/ClientHandle.h | 4 ++++ source/Player.cpp | 15 ++++++--------- source/Server.cpp | 45 +++++++++++++++++++++++++++++++++++++++++++++ source/Server.h | 14 ++++++++++++-- 7 files changed, 108 insertions(+), 16 deletions(-) (limited to 'source') diff --git a/source/Bindings.cpp b/source/Bindings.cpp index 8feb070e1..a0ad85156 100644 --- a/source/Bindings.cpp +++ b/source/Bindings.cpp @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 08/13/13 23:07:52. +** Generated automatically by tolua++-1.0.92 on 08/14/13 08:14:03. */ #ifndef __cplusplus @@ -11295,14 +11295,14 @@ static int tolua_AllToLua_cServer_GetNumPlayers00(lua_State* tolua_S) #ifndef TOLUA_RELEASE tolua_Error tolua_err; if ( - !tolua_isusertype(tolua_S,1,"const cServer",0,&tolua_err) || + !tolua_isusertype(tolua_S,1,"cServer",0,&tolua_err) || !tolua_isnoobj(tolua_S,2,&tolua_err) ) goto tolua_lerror; else #endif { - const cServer* self = (const cServer*) tolua_tousertype(tolua_S,1,0); + cServer* self = (cServer*) tolua_tousertype(tolua_S,1,0); #ifndef TOLUA_RELEASE if (!self) tolua_error(tolua_S,"invalid 'self' in function 'GetNumPlayers'", NULL); #endif diff --git a/source/Bindings.h b/source/Bindings.h index a8d39e32f..0211e0eeb 100644 --- a/source/Bindings.h +++ b/source/Bindings.h @@ -1,6 +1,6 @@ /* ** Lua binding: AllToLua -** Generated automatically by tolua++-1.0.92 on 08/13/13 23:07:53. +** Generated automatically by tolua++-1.0.92 on 08/14/13 08:14:04. */ /* Exported function */ diff --git a/source/ClientHandle.cpp b/source/ClientHandle.cpp index fe78ef2b6..32bfb00f5 100644 --- a/source/ClientHandle.cpp +++ b/source/ClientHandle.cpp @@ -275,7 +275,7 @@ void cClientHandle::Authenticate(void) void cClientHandle::StreamChunks(void) { - if ((m_State < csAuthenticating) || (m_State >= csDestroying)) + if ((m_State < csAuthenticated) || (m_State >= csDestroying)) { return; } @@ -1314,6 +1314,42 @@ void cClientHandle::SendData(const char * a_Data, int a_Size) +void cClientHandle::MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket) +{ + ASSERT(m_Player != NULL); + + if (a_SendRespawnPacket) + { + SendRespawn(); + } + + cWorld * World = m_Player->GetWorld(); + + // Remove all associated chunks: + cChunkCoordsList Chunks; + { + cCSLock Lock(m_CSChunkLists); + std::swap(Chunks, m_LoadedChunks); + m_ChunksToSend.clear(); + } + for (cChunkCoordsList::iterator itr = Chunks.begin(), end = Chunks.end(); itr != end; ++itr) + { + World->RemoveChunkClient(itr->m_ChunkX, itr->m_ChunkZ, this); + m_Protocol->SendUnloadChunk(itr->m_ChunkX, itr->m_ChunkZ); + } // for itr - Chunks[] + + // Do NOT stream new chunks, the new world runs its own tick thread and may deadlock + // Instead, the chunks will be streamed when the client is moved to the new world's Tick list, + // by setting state to csAuthenticated + m_State = csAuthenticated; + m_LastStreamedChunkX = 0x7fffffff; + m_LastStreamedChunkZ = 0x7fffffff; +} + + + + + bool cClientHandle::CheckBlockInteractionsRate(void) { ASSERT(m_Player != NULL); diff --git a/source/ClientHandle.h b/source/ClientHandle.h index 1f40cc8d2..4dcb188b3 100644 --- a/source/ClientHandle.h +++ b/source/ClientHandle.h @@ -32,6 +32,7 @@ class cRedstone; class cWindow; class cFallingBlock; class cItemHandler; +class cWorld; @@ -194,6 +195,9 @@ public: void SendData(const char * a_Data, int a_Size); + /// Called when the player moves into a different world; queues sreaming the new chunks + void MoveToWorld(cWorld & a_World, bool a_SendRespawnPacket); + private: int m_ViewDistance; // Number of chunks the player can see in each direction; 4 is the minimum ( http://wiki.vg/Protocol_FAQ#.E2.80.A6all_connecting_clients_spasm_and_jerk_uncontrollably.21 ) diff --git a/source/Player.cpp b/source/Player.cpp index 365a0396f..34980d2f6 100644 --- a/source/Player.cpp +++ b/source/Player.cpp @@ -100,6 +100,8 @@ cPlayer::cPlayer(cClientHandle* a_Client, const AString & a_PlayerName) m_LastJumpHeight = (float)(GetPosY()); m_LastGroundHeight = (float)(GetPosY()); m_Stance = GetPosY() + 1.62; + + cRoot::Get()->GetServer()->PlayerCreated(this); } @@ -1120,20 +1122,15 @@ bool cPlayer::MoveToWorld(const char * a_WorldName) m_ClientHandle->RemoveFromAllChunks(); m_World->RemoveEntity(this); + // If the dimension is different, we can send the respawn packet + // http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02 + m_ClientHandle->MoveToWorld(*World, (OldDimension != World->GetDimension())); + // Add player to all the necessary parts of the new world SetWorld(World); World->AddEntity(this); World->AddPlayer(this); - // If the dimension is different, we can send the respawn packet - // http://wiki.vg/Protocol#0x09 says "don't send if dimension is the same" as of 2013_07_02 - if (OldDimension != World->GetDimension()) - { - m_ClientHandle->SendRespawn(); - } - - // Stream the new chunks: - m_ClientHandle->StreamChunks(); return true; } diff --git a/source/Server.cpp b/source/Server.cpp index 31a1925a8..c01222e5a 100644 --- a/source/Server.cpp +++ b/source/Server.cpp @@ -172,10 +172,34 @@ void cServer::ClientMovedToWorld(const cClientHandle * a_Client) +void cServer::PlayerCreated(const cPlayer * a_Player) +{ + // To avoid deadlocks, the player count is not handled directly, but rather posted onto the tick thread + cCSLock Lock(m_CSPlayerCountDiff); + m_PlayerCountDiff += 1; +} + + + + + +void cServer::PlayerDestroyed(const cPlayer * a_Player) +{ + // To avoid deadlocks, the player count is not handled directly, but rather posted onto the tick thread + cCSLock Lock(m_CSPlayerCountDiff); + m_PlayerCountDiff -= 1; +} + + + + + bool cServer::InitServer(cIniFile & a_SettingsIni) { m_Description = a_SettingsIni.GetValue ("Server", "Description", "MCServer! - In C++!").c_str(); m_MaxPlayers = a_SettingsIni.GetValueI("Server", "MaxPlayers", 100); + m_PlayerCount = 0; + m_PlayerCountDiff = 0; if (m_bIsConnected) { @@ -254,6 +278,16 @@ bool cServer::InitServer(cIniFile & a_SettingsIni) +int cServer::GetNumPlayers(void) +{ + cCSLock Lock(m_CSPlayerCount); + return m_PlayerCount; +} + + + + + void cServer::PrepareKeys(void) { // TODO: Save and load key for persistence across sessions @@ -310,6 +344,17 @@ void cServer::OnConnectionAccepted(cSocket & a_Socket) bool cServer::Tick(float a_Dt) { + // Apply the queued playercount adjustments (postponed to avoid deadlocks) + int PlayerCountDiff = 0; + { + cCSLock Lock(m_CSPlayerCountDiff); + std::swap(PlayerCountDiff, m_PlayerCountDiff); + } + { + cCSLock Lock(m_CSPlayerCount); + m_PlayerCount += PlayerCountDiff; + } + cRoot::Get()->TickCommands(); TickClients(a_Dt); diff --git a/source/Server.h b/source/Server.h index 6f8576ece..87e472465 100644 --- a/source/Server.h +++ b/source/Server.h @@ -43,7 +43,7 @@ public: // tolua_export // Player counts: int GetMaxPlayers(void) const {return m_MaxPlayers; } - int GetNumPlayers(void) const { return m_NumPlayers; } + int GetNumPlayers(void); void SetMaxPlayers(int a_MaxPlayers) { m_MaxPlayers = a_MaxPlayers; } // tolua_end @@ -78,6 +78,12 @@ public: // tolua_export /// Don't tick a_Client anymore, it will be ticked from its cPlayer instead void ClientMovedToWorld(const cClientHandle * a_Client); + /// Notifies the server that a player was created; the server uses this to adjust the number of players + void PlayerCreated(const cPlayer * a_Player); + + /// Notifies the server that a player was destroyed; the server uses this to adjust the number of players + void PlayerDestroyed(const cPlayer * a_Player); + CryptoPP::RSA::PrivateKey & GetPrivateKey(void) { return m_PrivateKey; } CryptoPP::RSA::PublicKey & GetPublicKey (void) { return m_PublicKey; } @@ -135,6 +141,11 @@ private: cClientHandleList m_Clients; ///< Clients that are connected to the server and not yet assigned to a cWorld cClientHandleList m_ClientsToRemove; ///< Clients that have just been moved into a world and are to be removed from m_Clients in the next Tick() + cCriticalSection m_CSPlayerCount; ///< Locks the m_PlayerCount + int m_PlayerCount; ///< Number of players currently playing in the server + cCriticalSection m_CSPlayerCountDiff; ///< Locks the m_PlayerCountDiff + int m_PlayerCountDiff; ///< Adjustment to m_PlayerCount to be applied in the Tick thread + cSocketThreads m_SocketThreads; int m_ClientViewDistance; // The default view distance for clients; settable in Settings.ini @@ -150,7 +161,6 @@ private: AString m_Description; int m_MaxPlayers; - int m_NumPlayers; cTickThread m_TickThread; cEvent m_RestartEvent; -- cgit v1.2.3