diff --git a/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js b/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js index 2f55db9347..d690a34353 100644 --- a/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js +++ b/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js @@ -1,22 +1,26 @@ var g_IsConnecting = false; var g_GameType; // "server" or "client" +var g_IsRejoining = false; +var g_GameAttributes; // used when rejoining +var g_PlayerAssignments; // used when rejoining + function init(multiplayerGameType) { - switch (multiplayerGameType) - { - case "join": - getGUIObjectByName("pageJoin").hidden = false; - getGUIObjectByName("pageHost").hidden = true; - break; - case "host": - getGUIObjectByName("pageJoin").hidden = true; - getGUIObjectByName("pageHost").hidden = false; - break; - default: - error("Unrecognised multiplayer game type : " + multiplayerGameType); - break; - } + switch (multiplayerGameType) + { + case "join": + getGUIObjectByName("pageJoin").hidden = false; + getGUIObjectByName("pageHost").hidden = true; + break; + case "host": + getGUIObjectByName("pageJoin").hidden = true; + getGUIObjectByName("pageHost").hidden = false; + break; + default: + error("Unrecognised multiplayer game type : " + multiplayerGameType); + break; + } } function cancelSetup() @@ -30,6 +34,7 @@ function startConnectionStatus(type) { g_GameType = type; g_IsConnecting = true; + g_IsRejoining = false; getGUIObjectByName("connectionStatus").caption = "Connecting to server..."; } @@ -46,33 +51,92 @@ function onTick() log("Net message: "+uneval(message)); - switch (message.type) + // If we're rejoining an active game, we don't want to actually display + // the game setup screen, so perform similar processing to gamesetup.js + // in this screen + if (g_IsRejoining) { - case "netstatus": - switch (message.status) + switch (message.type) { - case "connected": - getGUIObjectByName("connectionStatus").caption = "Registering with server..."; + case "netstatus": + switch (message.status) + { + case "disconnected": + cancelSetup(); + reportDisconnect(message.reason); + return; + + default: + error("Unrecognised netstatus type "+message.status); + break; + } break; - case "authenticated": - Engine.PopGuiPage(); - Engine.PushGuiPage("page_gamesetup.xml", { "type": g_GameType }); - return; // don't process any more messages - leave them for the game GUI loop + case "gamesetup": + g_GameAttributes = message.data; + break; - case "disconnected": - cancelSetup(); - reportDisconnect(message.reason); - return; + case "players": + g_PlayerAssignments = message.hosts; + break; + + case "start": + Engine.SwitchGuiPage("page_loading.xml", { + "attribs": g_GameAttributes, + "isNetworked" : true, + "playerAssignments": g_PlayerAssignments + }); + break; + + case "chat": + // Ignore, since we have nowhere to display chat messages + break; default: - error("Unrecognised netstatus type "+message.status); + error("Unrecognised net message type "+message.type); + } + } + else + { + // Not rejoining - just trying to connect to server + + switch (message.type) + { + case "netstatus": + switch (message.status) + { + case "connected": + getGUIObjectByName("connectionStatus").caption = "Registering with server..."; + break; + + case "authenticated": + if (message.rejoining) + { + getGUIObjectByName("connectionStatus").caption = "Game has already started - rejoining..."; + g_IsRejoining = true; + return; // we'll process the game setup messages in the next tick + } + else + { + Engine.PopGuiPage(); + Engine.PushGuiPage("page_gamesetup.xml", { "type": g_GameType }); + return; // don't process any more messages - leave them for the game GUI loop + } + + case "disconnected": + cancelSetup(); + reportDisconnect(message.reason); + return; + + default: + error("Unrecognised netstatus type "+message.status); + break; + } + break; + default: + error("Unrecognised net message type "+message.type); break; } - break; - default: - error("Unrecognised net message type "+message.type); - break; } } } diff --git a/binaries/data/mods/public/gui/session/messages.js b/binaries/data/mods/public/gui/session/messages.js index 1f216879a6..dcca02c998 100644 --- a/binaries/data/mods/public/gui/session/messages.js +++ b/binaries/data/mods/public/gui/session/messages.js @@ -74,6 +74,10 @@ function handleNetMessage(message) obj.caption = "Waiting for other players to connect..."; obj.hidden = false; break; + case "join_syncing": + obj.caption = "Synchronising gameplay with other players..."; + obj.hidden = false; + break; case "active": obj.caption = ""; obj.hidden = true; @@ -116,6 +120,9 @@ function handleNetMessage(message) { // Update the cached player data, so we can display the correct name updatePlayerDataAdd(g_Players, host, message.hosts[host]); + + // Tell the user about the connection + addChatMessage({ "type": "connect", "guid": host }, message.hosts); } } @@ -167,14 +174,19 @@ function submitChatInput() toggleChatWindow(); } -function addChatMessage(msg) +function addChatMessage(msg, playerAssignments) { + // Default to global assignments, but allow overriding for when reporting + // new players joining + if (!playerAssignments) + playerAssignments = g_PlayerAssignments; + var playerColor, username; - if (g_PlayerAssignments[msg.guid]) + if (playerAssignments[msg.guid]) { - var n = g_PlayerAssignments[msg.guid].player; + var n = playerAssignments[msg.guid].player; playerColor = g_Players[n].color.r + " " + g_Players[n].color.g + " " + g_Players[n].color.b; - username = escapeText(g_PlayerAssignments[msg.guid].name); + username = escapeText(playerAssignments[msg.guid].name); } else { @@ -188,6 +200,9 @@ function addChatMessage(msg) switch (msg.type) { + case "connect": + formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] has joined the game."; + break; case "disconnect": formatted = "[color=\"" + playerColor + "\"]" + username + "[/color] has left the game."; break; diff --git a/binaries/data/mods/public/gui/session/utility_functions.js b/binaries/data/mods/public/gui/session/utility_functions.js index 313e14119e..326d60917c 100644 --- a/binaries/data/mods/public/gui/session/utility_functions.js +++ b/binaries/data/mods/public/gui/session/utility_functions.js @@ -71,6 +71,7 @@ function updatePlayerDataAdd(players, hostGuid, playerAssignment) { players[playerAssignment.player].guid = hostGuid; players[playerAssignment.player].name = playerAssignment.name; + players[playerAssignment.player].offline = false; } } diff --git a/source/network/NetClient.cpp b/source/network/NetClient.cpp index 543addda15..c76e5831bc 100644 --- a/source/network/NetClient.cpp +++ b/source/network/NetClient.cpp @@ -294,6 +294,9 @@ void CNetClient::LoadFinished() { if (!m_JoinSyncBuffer.empty()) { + // We're rejoining a game, and just finished loading the initial map, + // so deserialize the saved game state now + std::string state; DecompressZLib(m_JoinSyncBuffer, state, true); @@ -309,11 +312,18 @@ void CNetClient::LoadFinished() ENSURE(ok); m_ClientTurnManager->ResetState(turn, turn); - } - CScriptValRooted msg; - GetScriptInterface().Eval("({'type':'netstatus','status':'waiting_for_players'})", msg); - PushGuiMessage(msg); + CScriptValRooted msg; + GetScriptInterface().Eval("({'type':'netstatus','status':'join_syncing'})", msg); + PushGuiMessage(msg); + } + else + { + // Connecting at the start of a game, so we'll wait for other players to finish loading + CScriptValRooted msg; + GetScriptInterface().Eval("({'type':'netstatus','status':'waiting_for_players'})", msg); + PushGuiMessage(msg); + } CLoadedGameMessage loaded; loaded.m_CurrentTurn = m_ClientTurnManager->GetCurrentTurn(); @@ -371,12 +381,15 @@ bool CNetClient::OnAuthenticate(void* context, CFsmEvent* event) CAuthenticateResultMessage* message = (CAuthenticateResultMessage*)event->GetParamRef(); - LOGMESSAGE(L"Net: Authentication result: host=%d, %ls", message->m_HostID, message->m_Message.c_str() ); + LOGMESSAGE(L"Net: Authentication result: host=%d, %ls", message->m_HostID, message->m_Message.c_str()); + + bool isRejoining = (message->m_Code == ARC_OK_REJOINING); client->m_HostID = message->m_HostID; CScriptValRooted msg; client->GetScriptInterface().Eval("({'type':'netstatus','status':'authenticated'})", msg); + client->GetScriptInterface().SetProperty(msg.get(), "rejoining", isRejoining); client->PushGuiMessage(msg); return true; diff --git a/source/network/NetMessages.h b/source/network/NetMessages.h index 123e70d055..e38919b991 100644 --- a/source/network/NetMessages.h +++ b/source/network/NetMessages.h @@ -69,9 +69,8 @@ enum NetMessageType enum AuthenticateResultCode { ARC_OK, + ARC_OK_REJOINING, ARC_PASSWORD_INVALID, - ARC_NICK_TAKEN, - ARC_NICK_INVALID, }; #endif // NETMESSAGES_H diff --git a/source/network/NetServer.cpp b/source/network/NetServer.cpp index 953d993617..85409d564a 100644 --- a/source/network/NetServer.cpp +++ b/source/network/NetServer.cpp @@ -665,7 +665,7 @@ bool CNetServerWorker::OnAuthenticate(void* context, CFsmEvent* event) session->SetHostID(newHostID); CAuthenticateResultMessage authenticateResult; - authenticateResult.m_Code = ARC_OK; + authenticateResult.m_Code = isRejoining ? ARC_OK_REJOINING : ARC_OK; authenticateResult.m_HostID = newHostID; authenticateResult.m_Message = L"Logged in"; session->SendMessage(&authenticateResult); diff --git a/source/ps/Game.h b/source/ps/Game.h index d1f47748c7..99e39b8f56 100644 --- a/source/ps/Game.h +++ b/source/ps/Game.h @@ -65,12 +65,6 @@ class CGame CNetTurnManager* m_TurnManager; public: - enum ENetStatus - { - NET_WAITING_FOR_CONNECT, /// we have loaded the game; waiting for other players to finish loading - NET_NORMAL /// running the game - }; - CGame(bool disableGraphics = false); ~CGame();