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 };