From 248a48d88a5dfd7deaf7d76420b1f8b78da03810 Mon Sep 17 00:00:00 2001 From: elexis Date: Sat, 4 Jun 2016 12:08:30 +0000 Subject: [PATCH] Major network cleanup. Patch by Imarok. Access the server from the client only, not from the GUI (except for autostarted games). Thereby lay the foundation for clients to setup the game (refs #3806) and dedicated hosting (refs #3556). Doesn't transfer nor remove the SetTurnLength showcase from 0ebe3486b6. This was SVN commit r18322. --- .../data/mods/public/gui/common/network.js | 4 +- .../mods/public/gui/gamesetup/gamesetup.js | 1 - source/gui/scripting/ScriptFunctions.cpp | 34 ++--- source/network/NetClient.cpp | 35 +++++ source/network/NetClient.h | 13 ++ source/network/NetMessage.cpp | 8 ++ source/network/NetMessages.h | 12 +- source/network/NetServer.cpp | 122 ++++++++++++------ source/network/NetServer.h | 35 +---- 9 files changed, 168 insertions(+), 96 deletions(-) diff --git a/binaries/data/mods/public/gui/common/network.js b/binaries/data/mods/public/gui/common/network.js index 39e6e18ac9..d3863fa17d 100644 --- a/binaries/data/mods/public/gui/common/network.js +++ b/binaries/data/mods/public/gui/common/network.js @@ -89,7 +89,9 @@ function reportDisconnect(reason, wasConnected) function kickPlayer(username, ban) { - if (!Engine.KickPlayer(username, ban)) + if (g_IsController) + Engine.KickPlayer(username, ban); + else addChatMessage({ "type": "system", "text": sprintf(ban ? translate("Could not ban %(name)s.") : translate("Could not kick %(name)s."), { diff --git a/binaries/data/mods/public/gui/gamesetup/gamesetup.js b/binaries/data/mods/public/gui/gamesetup/gamesetup.js index d59c80aca6..b0750128df 100644 --- a/binaries/data/mods/public/gui/gamesetup/gamesetup.js +++ b/binaries/data/mods/public/gui/gamesetup/gamesetup.js @@ -656,7 +656,6 @@ function handleReadyMessage(message) return; g_PlayerAssignments[message.guid].status = +message.status == 1; - Engine.SetNetworkPlayerStatus(message.guid, +message.status); updateReadyUI(); } diff --git a/source/gui/scripting/ScriptFunctions.cpp b/source/gui/scripting/ScriptFunctions.cpp index 3a73dd59cf..74256b44cb 100644 --- a/source/gui/scripting/ScriptFunctions.cpp +++ b/source/gui/scripting/ScriptFunctions.cpp @@ -246,8 +246,8 @@ JS::Value GetEngineInfo(ScriptInterface::CxPrivate* pCxPrivate) void StartNetworkGame(ScriptInterface::CxPrivate* UNUSED(pCxPrivate)) { - ENSURE(g_NetServer); - g_NetServer->StartGame(); + ENSURE(g_NetClient); + g_NetClient->SendStartGameMessage(); } void StartGame(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue attribs, int playerID) @@ -330,14 +330,15 @@ void SaveGamePrefix(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& void SetNetworkGameAttributes(ScriptInterface::CxPrivate* pCxPrivate, JS::HandleValue attribs1) { - ENSURE(g_NetServer); + ENSURE(g_NetClient); + //TODO: This is a workaround because we need to pass a MutableHandle to a JSAPI functions somewhere // (with no obvious reason). JSContext* cx = pCxPrivate->pScriptInterface->GetContext(); JSAutoRequest rq(cx); JS::RootedValue attribs(cx, attribs1); - g_NetServer->UpdateGameAttributes(&attribs, *(pCxPrivate->pScriptInterface)); + g_NetClient->SendGameSetupMessage(&attribs, *(pCxPrivate->pScriptInterface)); } void StartNetworkHost(ScriptInterface::CxPrivate* pCxPrivate, const std::wstring& playerName) @@ -400,12 +401,11 @@ std::string GetPlayerGUID(ScriptInterface::CxPrivate* UNUSED(pCxPrivate)) return g_NetClient->GetGUID(); } -bool KickPlayer(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const CStrW& playerName, bool ban) +void KickPlayer(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const CStrW& playerName, bool ban) { - if (!g_NetServer) - return false; + ENSURE(g_NetClient); - return g_NetServer->KickPlayer(playerName, ban); + g_NetClient->SendKickPlayerMessage(playerName, ban); } JS::Value PollNetworkClient(ScriptInterface::CxPrivate* pCxPrivate) @@ -423,23 +423,16 @@ JS::Value PollNetworkClient(ScriptInterface::CxPrivate* pCxPrivate) void AssignNetworkPlayer(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int playerID, const std::string& guid) { - ENSURE(g_NetServer); + ENSURE(g_NetClient); - g_NetServer->AssignPlayer(playerID, guid); -} - -void SetNetworkPlayerStatus(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const std::string& guid, int ready) -{ - ENSURE(g_NetServer); - - g_NetServer->SetPlayerReady(guid, ready); + g_NetClient->SendAssignPlayerMessage(playerID, guid); } void ClearAllPlayerReady (ScriptInterface::CxPrivate* UNUSED(pCxPrivate)) { - ENSURE(g_NetServer); + ENSURE(g_NetClient); - g_NetServer->ClearAllPlayerReady(); + g_NetClient->SendClearAllReadyMessage(); } void SendNetworkChat(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const std::wstring& message) @@ -1038,11 +1031,10 @@ void GuiScriptingInit(ScriptInterface& scriptInterface) scriptInterface.RegisterFunction("StartNetworkJoin"); scriptInterface.RegisterFunction("DisconnectNetworkGame"); scriptInterface.RegisterFunction("GetPlayerGUID"); - scriptInterface.RegisterFunction("KickPlayer"); + scriptInterface.RegisterFunction("KickPlayer"); scriptInterface.RegisterFunction("PollNetworkClient"); scriptInterface.RegisterFunction("SetNetworkGameAttributes"); scriptInterface.RegisterFunction("AssignNetworkPlayer"); - scriptInterface.RegisterFunction("SetNetworkPlayerStatus"); scriptInterface.RegisterFunction("ClearAllPlayerReady"); scriptInterface.RegisterFunction("SendNetworkChat"); scriptInterface.RegisterFunction("SendNetworkReady"); diff --git a/source/network/NetClient.cpp b/source/network/NetClient.cpp index abfa8d0eea..52ff56840f 100644 --- a/source/network/NetClient.cpp +++ b/source/network/NetClient.cpp @@ -323,6 +323,21 @@ void CNetClient::HandleDisconnect(u32 reason) SetCurrState(NCS_UNCONNECTED); } +void CNetClient::SendGameSetupMessage(JS::MutableHandleValue attrs, ScriptInterface& scriptInterface) +{ + CGameSetupMessage gameSetup(scriptInterface); + gameSetup.m_Data = attrs; + SendMessage(&gameSetup); +} + +void CNetClient::SendAssignPlayerMessage(const int playerID, const CStr& guid) +{ + CAssignPlayerMessage assignPlayer; + assignPlayer.m_PlayerID = playerID; + assignPlayer.m_GUID = guid; + SendMessage(&assignPlayer); +} + void CNetClient::SendChatMessage(const std::wstring& text) { CChatMessage chat; @@ -337,12 +352,32 @@ void CNetClient::SendReadyMessage(const int status) SendMessage(&readyStatus); } +void CNetClient::SendClearAllReadyMessage() +{ + CClearAllReadyMessage clearAllReady; + SendMessage(&clearAllReady); +} + +void CNetClient::SendStartGameMessage() +{ + CGameStartMessage gameStart; + SendMessage(&gameStart); +} + void CNetClient::SendRejoinedMessage() { CRejoinedMessage rejoinedMessage; SendMessage(&rejoinedMessage); } +void CNetClient::SendKickPlayerMessage(const CStrW& playerName, bool ban) +{ + CKickedMessage kickPlayer; + kickPlayer.m_Name = playerName; + kickPlayer.m_Ban = ban; + SendMessage(&kickPlayer); +} + void CNetClient::SendPausedMessage(bool pause) { CClientPausedMessage pausedMessage; diff --git a/source/network/NetClient.h b/source/network/NetClient.h index facc76f1d2..8ae945c317 100644 --- a/source/network/NetClient.h +++ b/source/network/NetClient.h @@ -187,16 +187,29 @@ public: */ void LoadFinished(); + void SendGameSetupMessage(JS::MutableHandleValue attrs, ScriptInterface& scriptInterface); + + void SendAssignPlayerMessage(const int playerID, const CStr& guid); + void SendChatMessage(const std::wstring& text); void SendReadyMessage(const int status); + void SendClearAllReadyMessage(); + + void SendStartGameMessage(); + /** * Call when the client has rejoined a running match and finished * the loading screen. */ void SendRejoinedMessage(); + /** + * Call to kick/ban a client + */ + void SendKickPlayerMessage(const CStrW& playerName, bool ban); + /** * Call when the client has paused or unpaused the game. */ diff --git a/source/network/NetMessage.cpp b/source/network/NetMessage.cpp index 1439082715..8b2adef615 100644 --- a/source/network/NetMessage.cpp +++ b/source/network/NetMessage.cpp @@ -203,6 +203,14 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData, pNewMessage = new CSimulationMessage(scriptInterface); break; + case NMT_CLEAR_ALL_READY: + pNewMessage = new CClearAllReadyMessage; + break; + + case NMT_ASSIGN_PLAYER: + pNewMessage = new CAssignPlayerMessage; + break; + default: LOGERROR("CNetMessageFactory::CreateMessage(): Unknown message type '%d' received", header.GetType()); break; diff --git a/source/network/NetMessages.h b/source/network/NetMessages.h index b775d678eb..3cadec83c8 100644 --- a/source/network/NetMessages.h +++ b/source/network/NetMessages.h @@ -28,7 +28,7 @@ #define PS_PROTOCOL_MAGIC 0x5073013f // 'P', 's', 0x01, '?' #define PS_PROTOCOL_MAGIC_RESPONSE 0x50630121 // 'P', 'c', 0x01, '!' -#define PS_PROTOCOL_VERSION 0x01010013 // Arbitrary protocol +#define PS_PROTOCOL_VERSION 0x01010014 // Arbitrary protocol #define PS_DEFAULT_PORT 0x5073 // 'P', 's' // Defines the list of message types. The order of the list must not change. @@ -50,7 +50,9 @@ enum NetMessageType NMT_CHAT, NMT_READY, + NMT_CLEAR_ALL_READY, NMT_GAME_SETUP, + NMT_ASSIGN_PLAYER, NMT_PLAYER_ASSIGNMENT, NMT_FILE_TRANSFER_REQUEST, @@ -136,6 +138,9 @@ START_NMT_CLASS_(Ready, NMT_READY) NMT_FIELD_INT(m_Status, u8, 1) END_NMT_CLASS() +START_NMT_CLASS_(ClearAllReady, NMT_CLEAR_ALL_READY) +END_NMT_CLASS() + START_NMT_CLASS_(PlayerAssignment, NMT_PLAYER_ASSIGNMENT) NMT_START_ARRAY(m_Hosts) NMT_FIELD(CStr, m_GUID) @@ -218,6 +223,11 @@ START_NMT_CLASS_(SyncError, NMT_SYNC_ERROR) NMT_END_ARRAY() END_NMT_CLASS() +START_NMT_CLASS_(AssignPlayer, NMT_ASSIGN_PLAYER) + NMT_FIELD_INT(m_PlayerID, i8, 1) + NMT_FIELD(CStr, m_GUID) +END_NMT_CLASS() + END_NMTS() #else diff --git a/source/network/NetServer.cpp b/source/network/NetServer.cpp index 1c36f1b6fe..df999befeb 100644 --- a/source/network/NetServer.cpp +++ b/source/network/NetServer.cpp @@ -404,10 +404,7 @@ bool CNetServerWorker::RunStep() JSContext* cx = m_ScriptInterface->GetContext(); JSAutoRequest rq(cx); - std::vector> newAssignPlayer; std::vector newStartGame; - std::vector> newPlayerReady; - std::vector newPlayerResetReady; std::vector newGameAttributes; std::vector newTurnLength; @@ -418,22 +415,10 @@ bool CNetServerWorker::RunStep() return false; newStartGame.swap(m_StartGameQueue); - newPlayerReady.swap(m_PlayerReadyQueue); - newPlayerResetReady.swap(m_PlayerResetReadyQueue); - newAssignPlayer.swap(m_AssignPlayerQueue); newGameAttributes.swap(m_GameAttributesQueue); newTurnLength.swap(m_TurnLengthQueue); } - for (const std::pair& p : newAssignPlayer) - AssignPlayer(p.first, p.second); - - for (const std::pair& p : newPlayerReady) - SetPlayerReady(p.first, p.second); - - if (!newPlayerResetReady.empty()) - ClearAllPlayerReady(); - if (!newGameAttributes.empty()) { JS::RootedValue gameAttributesVal(cx); @@ -649,12 +634,19 @@ void CNetServerWorker::SetupSession(CNetServerSession* session) session->AddTransition(NSS_PREGAME, (uint)NMT_CONNECTION_LOST, NSS_UNCONNECTED, (void*)&OnDisconnect, context); session->AddTransition(NSS_PREGAME, (uint)NMT_CHAT, NSS_PREGAME, (void*)&OnChat, context); session->AddTransition(NSS_PREGAME, (uint)NMT_READY, NSS_PREGAME, (void*)&OnReady, context); + session->AddTransition(NSS_PREGAME, (uint)NMT_CLEAR_ALL_READY, NSS_PREGAME, (void*)&OnClearAllReady, context); + session->AddTransition(NSS_PREGAME, (uint)NMT_GAME_SETUP, NSS_PREGAME, (void*)&OnGameSetup, context); + session->AddTransition(NSS_PREGAME, (uint)NMT_ASSIGN_PLAYER, NSS_PREGAME, (void*)&OnAssignPlayer, context); + session->AddTransition(NSS_PREGAME, (uint)NMT_KICKED, NSS_PREGAME, (void*)&OnKickPlayer, context); + session->AddTransition(NSS_PREGAME, (uint)NMT_GAME_START, NSS_PREGAME, (void*)&OnStartGame, context); session->AddTransition(NSS_PREGAME, (uint)NMT_LOADED_GAME, NSS_INGAME, (void*)&OnLoadedGame, context); + session->AddTransition(NSS_JOIN_SYNCING, (uint)NMT_KICKED, NSS_JOIN_SYNCING, (void*)&OnKickPlayer, context); session->AddTransition(NSS_JOIN_SYNCING, (uint)NMT_CONNECTION_LOST, NSS_UNCONNECTED, (void*)&OnDisconnect, context); session->AddTransition(NSS_JOIN_SYNCING, (uint)NMT_LOADED_GAME, NSS_INGAME, (void*)&OnJoinSyncingLoadedGame, context); session->AddTransition(NSS_INGAME, (uint)NMT_REJOINED, NSS_INGAME, (void*)&OnRejoined, context); + session->AddTransition(NSS_INGAME, (uint)NMT_KICKED, NSS_INGAME, (void*)&OnKickPlayer, context); session->AddTransition(NSS_INGAME, (uint)NMT_CLIENT_PAUSED, NSS_INGAME, (void*)&OnClientPaused, context); session->AddTransition(NSS_INGAME, (uint)NMT_CONNECTION_LOST, NSS_UNCONNECTED, (void*)&OnDisconnect, context); session->AddTransition(NSS_INGAME, (uint)NMT_CHAT, NSS_INGAME, (void*)&OnChat, context); @@ -784,7 +776,7 @@ void CNetServerWorker::ClearAllPlayerReady() SendPlayerAssignments(); } -bool CNetServerWorker::KickPlayer(const CStrW& playerName, const bool ban) +void CNetServerWorker::KickPlayer(const CStrW& playerName, const bool ban) { // Find the user with that name std::vector::iterator it = std::find_if(m_Sessions.begin(), m_Sessions.end(), @@ -792,7 +784,7 @@ bool CNetServerWorker::KickPlayer(const CStrW& playerName, const bool ban) // and return if no one or the host has that name if (it == m_Sessions.end() || (*it)->GetGUID() == m_HostGUID) - return false; + return; if (ban) { @@ -814,8 +806,6 @@ bool CNetServerWorker::KickPlayer(const CStrW& playerName, const bool ban) kickedMessage.m_Name = playerName; kickedMessage.m_Ban = ban; Broadcast(&kickedMessage); - - return true; } void CNetServerWorker::AssignPlayer(int playerID, const CStr& guid) @@ -1108,6 +1098,61 @@ bool CNetServerWorker::OnReady(void* context, CFsmEvent* event) message->m_GUID = session->GetGUID(); server.Broadcast(message); + server.SetPlayerReady(message->m_GUID, message->m_Status); + + return true; +} + +bool CNetServerWorker::OnClearAllReady(void* context, CFsmEvent* event) +{ + ENSURE(event->GetType() == (uint)NMT_CLEAR_ALL_READY); + + CNetServerSession* session = (CNetServerSession*)context; + CNetServerWorker& server = session->GetServer(); + + if (session->GetGUID() == server.m_HostGUID) + server.ClearAllPlayerReady(); + + return true; +} + +bool CNetServerWorker::OnGameSetup(void* context, CFsmEvent* event) +{ + ENSURE(event->GetType() == (uint)NMT_GAME_SETUP); + + CNetServerSession* session = (CNetServerSession*)context; + CNetServerWorker& server = session->GetServer(); + + if (session->GetGUID() == server.m_HostGUID) + { + CGameSetupMessage* message = (CGameSetupMessage*)event->GetParamRef(); + server.UpdateGameAttributes(&(message->m_Data)); + } + return true; +} + +bool CNetServerWorker::OnAssignPlayer(void* context, CFsmEvent* event) +{ + ENSURE(event->GetType() == (uint)NMT_ASSIGN_PLAYER); + CNetServerSession* session = (CNetServerSession*)context; + CNetServerWorker& server = session->GetServer(); + + if (session->GetGUID() == server.m_HostGUID) + { + CAssignPlayerMessage* message = (CAssignPlayerMessage*)event->GetParamRef(); + server.AssignPlayer(message->m_PlayerID, message->m_GUID); + } + return true; +} + +bool CNetServerWorker::OnStartGame(void* context, CFsmEvent* event) +{ + ENSURE(event->GetType() == (uint)NMT_GAME_START); + CNetServerSession* session = (CNetServerSession*)context; + CNetServerWorker& server = session->GetServer(); + + if (session->GetGUID() == server.m_HostGUID) + server.StartGame(); return true; } @@ -1204,6 +1249,21 @@ bool CNetServerWorker::OnRejoined(void* context, CFsmEvent* event) return true; } +bool CNetServerWorker::OnKickPlayer(void* context, CFsmEvent* event) +{ + ENSURE(event->GetType() == (uint)NMT_KICKED); + + CNetServerSession* session = (CNetServerSession*)context; + CNetServerWorker& server = session->GetServer(); + + if (session->GetGUID() == server.m_HostGUID) + { + CKickedMessage* message = (CKickedMessage*)event->GetParamRef(); + server.KickPlayer(message->m_Name, message->m_Ban); + } + return true; +} + bool CNetServerWorker::OnDisconnect(void* context, CFsmEvent* event) { ENSURE(event->GetType() == (uint)NMT_CONNECTION_LOST); @@ -1372,30 +1432,6 @@ bool CNetServer::SetupConnection() return m_Worker->SetupConnection(); } -bool CNetServer::KickPlayer(const CStrW& playerName, const bool ban) -{ - CScopeLock lock(m_Worker->m_WorkerMutex); - return m_Worker->KickPlayer(playerName, ban); -} - -void CNetServer::AssignPlayer(int playerID, const CStr& guid) -{ - CScopeLock lock(m_Worker->m_WorkerMutex); - m_Worker->m_AssignPlayerQueue.emplace_back(playerID, guid); -} - -void CNetServer::SetPlayerReady(const CStr& guid, int ready) -{ - CScopeLock lock(m_Worker->m_WorkerMutex); - m_Worker->m_PlayerReadyQueue.emplace_back(guid, ready); -} - -void CNetServer::ClearAllPlayerReady() -{ - CScopeLock lock(m_Worker->m_WorkerMutex); - m_Worker->m_PlayerResetReadyQueue.push_back(false); -} - void CNetServer::StartGame() { CScopeLock lock(m_Worker->m_WorkerMutex); diff --git a/source/network/NetServer.h b/source/network/NetServer.h index 75de82f907..a38844af0e 100644 --- a/source/network/NetServer.h +++ b/source/network/NetServer.h @@ -115,31 +115,6 @@ public: */ bool SetupConnection(); - /** - * Call from the GUI to update the player assignments. - * The given GUID will be (re)assigned to the given player ID. - * Any player currently using that ID will be unassigned. - * The changes will be asynchronously propagated to all clients. - */ - void AssignPlayer(int playerID, const CStr& guid); - - /** - * Call from the GUI to update the player readiness. - * The changes will be asynchronously propagated to all clients. - */ - void SetPlayerReady(const CStr& guid, int ready); - - /** - * Call from the GUI to set the all player readiness to 0. - * The changes will be asynchronously propagated to all clients. - */ - void ClearAllPlayerReady(); - - /** - * Disconnects a player from gamesetup or session. - */ - bool KickPlayer(const CStrW& playerName, const bool ban); - /** * Call from the GUI to asynchronously notify all clients that they should start loading the game. */ @@ -190,7 +165,7 @@ public: /** * Disconnects a player from gamesetup or session. */ - bool KickPlayer(const CStrW& playerName, const bool ban); + void KickPlayer(const CStrW& playerName, const bool ban); /** * Send a message to all clients who have completed the full connection process @@ -270,9 +245,14 @@ private: static bool OnInGame(void* context, CFsmEvent* event); static bool OnChat(void* context, CFsmEvent* event); static bool OnReady(void* context, CFsmEvent* event); + static bool OnClearAllReady(void* context, CFsmEvent* event); + static bool OnGameSetup(void* context, CFsmEvent* event); + static bool OnAssignPlayer(void* context, CFsmEvent* event); + static bool OnStartGame(void* context, CFsmEvent* event); static bool OnLoadedGame(void* context, CFsmEvent* event); static bool OnJoinSyncingLoadedGame(void* context, CFsmEvent* event); static bool OnRejoined(void* context, CFsmEvent* event); + static bool OnKickPlayer(void* context, CFsmEvent* event); static bool OnDisconnect(void* context, CFsmEvent* event); static bool OnClientPaused(void* context, CFsmEvent* event); @@ -367,10 +347,7 @@ private: bool m_Shutdown; // protected by m_WorkerMutex // Queues for messages sent by the game thread: - std::vector> m_AssignPlayerQueue; // protected by m_WorkerMutex std::vector m_StartGameQueue; // protected by m_WorkerMutex - std::vector> m_PlayerReadyQueue; // protected by m_WorkerMutex - std::vector m_PlayerResetReadyQueue; // protected by m_WorkerMutex std::vector m_GameAttributesQueue; // protected by m_WorkerMutex std::vector m_TurnLengthQueue; // protected by m_WorkerMutex };