diff --git a/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js b/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js index 0bad36c5e0..abe79b0205 100644 --- a/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js +++ b/binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js @@ -111,6 +111,7 @@ function pollAndHandleNetworkClient() Engine.SwitchGuiPage("page_loading.xml", { "attribs": g_GameAttributes, "isNetworked" : true, + "isRejoining" : g_IsRejoining, "playerAssignments": g_PlayerAssignments }); break; diff --git a/binaries/data/mods/public/gui/loading/loading.js b/binaries/data/mods/public/gui/loading/loading.js index b3169fb8ac..141af431c1 100644 --- a/binaries/data/mods/public/gui/loading/loading.js +++ b/binaries/data/mods/public/gui/loading/loading.js @@ -33,13 +33,11 @@ function init(data) Engine.GetGUIObjectByName("tipImage").sprite = sprite? sprite : ""; } else - { error("Failed to find any matching tips for the loading screen.") - } // janwas: main loop now sets progress / description, but that won't // happen until the first timeslice completes, so set initial values. - var loadingMapName = Engine.GetGUIObjectByName ("loadingMapName"); + var loadingMapName = Engine.GetGUIObjectByName("loadingMapName"); if (data) { @@ -48,15 +46,15 @@ function init(data) { case "skirmish": case "scenario": - loadingMapName.caption = sprintf(translate("Loading “%(map)s”"), {map: mapName}); + loadingMapName.caption = sprintf(translate("Loading “%(map)s”"), { "map": mapName }); break; case "random": - loadingMapName.caption = sprintf(translate("Generating “%(map)s”"), {map: mapName}); + loadingMapName.caption = sprintf(translate("Generating “%(map)s”"), { "map": mapName }); break; default: - error(sprintf("Unknown map type: %(mapType)s", { mapType: data.attribs.mapType })); + error("Unknown map type: " + data.attribs.mapType); } } @@ -68,49 +66,47 @@ function init(data) Engine.GetGUIObjectByName("quoteText").caption = translate(quoteArray[getRandom(0, quoteArray.length-1)]); } -// ==================================================================== function displayProgress() { // Make the progessbar finish a little early so that the user can actually see it finish - if (g_Progress < 100) - { - // Show 100 when it is really 99 - var progress = g_Progress + 1; + if (g_Progress >= 100) + return; - Engine.GetGUIObjectByName("progressbar").caption = progress; // display current progress - Engine.GetGUIObjectByName("progressText").caption = progress + "%"; + // Show 100 when it is really 99 + var progress = g_Progress + 1; - // Displays detailed loading info rather than a percent - // Engine.GetGUIObjectByName("progressText").caption = g_LoadDescription; // display current progess details + Engine.GetGUIObjectByName("progressbar").caption = progress; // display current progress + Engine.GetGUIObjectByName("progressText").caption = progress + "%"; - // Keep curved right edge of progress bar in sync with the rest of the progress bar - var middle = Engine.GetGUIObjectByName("progressbar"); - var rightSide = Engine.GetGUIObjectByName("progressbar_right"); + // Displays detailed loading info rather than a percent + // Engine.GetGUIObjectByName("progressText").caption = g_LoadDescription; // display current progess details - var middleLength = (middle.size.right - middle.size.left) - (END_PIECE_WIDTH / 2); - var increment = Math.round(progress * middleLength / 100); + // Keep curved right edge of progress bar in sync with the rest of the progress bar + var middle = Engine.GetGUIObjectByName("progressbar"); + var rightSide = Engine.GetGUIObjectByName("progressbar_right"); - var size = rightSide.size; - size.left = increment; - size.right = increment + END_PIECE_WIDTH; - rightSide.size = size; - } + var middleLength = (middle.size.right - middle.size.left) - (END_PIECE_WIDTH / 2); + var increment = Math.round(progress * middleLength / 100); + + var size = rightSide.size; + size.left = increment; + size.right = increment + END_PIECE_WIDTH; + rightSide.size = size; } -// ==================================================================== +/** + * This is a reserved function name that is executed by the engine when it is ready + * to start the game (i.e. loading progress has reached 100%). + */ function reallyStartGame() { - // Stop the music -// if (global.curr_music) -// global.curr_music.fade(-1, 0.0, 5.0); // fade to 0 over 5 seconds - - - // This is a reserved function name that is executed by the engine when it is ready - // to start the game (i.e. loading progress has reached 100%). - // Switch GUI from loading screen to game session. Engine.SwitchGuiPage("page_session.xml", g_Data); // Restore default cursor. Engine.SetCursor("arrow-default"); + + // Notify the other clients that we have finished the loading screen + if (g_Data.isNetworked && g_Data.isRejoining) + Engine.SendNetworkRejoined(); } diff --git a/binaries/data/mods/public/gui/session/messages.js b/binaries/data/mods/public/gui/session/messages.js index 28e35fddd4..ffb8ba97b4 100644 --- a/binaries/data/mods/public/gui/session/messages.js +++ b/binaries/data/mods/public/gui/session/messages.js @@ -252,7 +252,7 @@ function handleNetMessage(message) // Find and report all leavings for (var host in g_PlayerAssignments) { - if (! message.hosts[host]) + if (!message.hosts[host]) { // Tell the user about the disconnection addChatMessage({ "type": "disconnect", "guid": host }); @@ -265,7 +265,7 @@ function handleNetMessage(message) // Find and report all joinings for (var host in message.hosts) { - if (! g_PlayerAssignments[host]) + if (!g_PlayerAssignments[host]) { // Update the cached player data, so we can display the correct name updatePlayerDataAdd(g_Players, host, message.hosts[host]); @@ -293,6 +293,10 @@ function handleNetMessage(message) addChatMessage({ "type": "message", "guid": message.guid, "text": message.text, "translate": true }); break; + case "rejoined": + addChatMessage({ "type": "rejoined", "guid": message.guid}); + break; + // To prevent errors, ignore these message types that occur during autostart case "gamesetup": case "start": @@ -432,11 +436,14 @@ function addChatMessage(msg, playerAssignments) switch (msg.type) { case "connect": - formatted = sprintf(translate("%(player)s has joined the game."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" }); + formatted = sprintf(translate("%(player)s is starting to rejoin the game."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" }); break; case "disconnect": formatted = sprintf(translate("%(player)s has left the game."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" }); break; + case "rejoined": + formatted = sprintf(translate("%(player)s has rejoined the game."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" }); + break; case "defeat": // In singleplayer, the local player is "You". "You has" is incorrect. if (!g_IsNetworked && msg.player == Engine.GetPlayerID()) @@ -445,29 +452,31 @@ function addChatMessage(msg, playerAssignments) formatted = sprintf(translate("%(player)s has been defeated."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" }); break; case "diplomacy": - var status = (msg.status == "ally" ? "allied" : (msg.status == "enemy" ? "at war" : "neutral")); + var message; if (msg.player == Engine.GetPlayerID()) { [username, playerColor] = getUsernameAndColor(msg.player1); if (msg.status == "ally") - formatted = sprintf(translate("You are now allied with %(player)s."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" }); + message = translate("You are now allied with %(player)s."); else if (msg.status == "enemy") - formatted = sprintf(translate("You are now at war with %(player)s."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" }); + message = translate("You are now at war with %(player)s."); else // (msg.status == "neutral") - formatted = sprintf(translate("You are now neutral with %(player)s."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" }); + message = translate("You are now neutral with %(player)s."); } else if (msg.player1 == Engine.GetPlayerID()) { [username, playerColor] = getUsernameAndColor(msg.player); if (msg.status == "ally") - formatted = sprintf(translate("%(player)s is now allied with you."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" }); + message = translate("%(player)s is now allied with you."); else if (msg.status == "enemy") - formatted = sprintf(translate("%(player)s is now at war with you."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" }); + message = translate("%(player)s is now at war with you."); else // (msg.status == "neutral") - formatted = sprintf(translate("%(player)s is now neutral with you."), { player: "[color=\"" + playerColor + "\"]" + username + "[/color]" }); + message = translate("%(player)s is now neutral with you."); } else // No need for other players to know of this. return; + + formatted = sprintf(message, { "player": '[color="'+ playerColor + '"]' + username + '[/color]' }); break; case "tribute": if (msg.player != Engine.GetPlayerID()) diff --git a/source/gui/scripting/ScriptFunctions.cpp b/source/gui/scripting/ScriptFunctions.cpp index f2a6003008..aae0f44d7a 100644 --- a/source/gui/scripting/ScriptFunctions.cpp +++ b/source/gui/scripting/ScriptFunctions.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2014 Wildfire Games. +/* Copyright (C) 2015 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -395,6 +395,13 @@ void SendNetworkReady(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), int messag g_NetClient->SendReadyMessage(message); } +void SendNetworkRejoined(ScriptInterface::CxPrivate* UNUSED(pCxPrivate)) +{ + ENSURE(g_NetClient); + + g_NetClient->SendRejoinedMessage(); +} + JS::Value GetAIs(ScriptInterface::CxPrivate* pCxPrivate) { return ICmpAIManager::GetAIs(*(pCxPrivate->pScriptInterface)); @@ -956,6 +963,7 @@ void GuiScriptingInit(ScriptInterface& scriptInterface) scriptInterface.RegisterFunction("ClearAllPlayerReady"); scriptInterface.RegisterFunction("SendNetworkChat"); scriptInterface.RegisterFunction("SendNetworkReady"); + scriptInterface.RegisterFunction("SendNetworkRejoined"); scriptInterface.RegisterFunction("GetAIs"); scriptInterface.RegisterFunction("GetEngineInfo"); diff --git a/source/network/NetClient.cpp b/source/network/NetClient.cpp index 57a9857bcd..358c1a981f 100644 --- a/source/network/NetClient.cpp +++ b/source/network/NetClient.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2011 Wildfire Games. +/* Copyright (C) 2015 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -109,6 +109,7 @@ CNetClient::CNetClient(CGame* game) : AddTransition(NCS_LOADING, (uint)NMT_PLAYER_ASSIGNMENT, NCS_LOADING, (void*)&OnPlayerAssignment, context); AddTransition(NCS_LOADING, (uint)NMT_LOADED_GAME, NCS_INGAME, (void*)&OnLoadedGame, context); + AddTransition(NCS_INGAME, (uint)NMT_REJOINED, NCS_INGAME, (void*)&OnRejoined, context); AddTransition(NCS_INGAME, (uint)NMT_CHAT, NCS_INGAME, (void*)&OnChat, context); AddTransition(NCS_INGAME, (uint)NMT_GAME_SETUP, NCS_INGAME, (void*)&OnGameSetup, context); AddTransition(NCS_INGAME, (uint)NMT_PLAYER_ASSIGNMENT, NCS_INGAME, (void*)&OnPlayerAssignment, context); @@ -286,6 +287,12 @@ void CNetClient::SendReadyMessage(const int status) SendMessage(&readyStatus); } +void CNetClient::SendRejoinedMessage() +{ + CRejoinedMessage rejoinedMessage; + SendMessage(&rejoinedMessage); +} + bool CNetClient::HandleMessage(CNetMessage* message) { // Handle non-FSM messages first @@ -584,6 +591,22 @@ bool CNetClient::OnJoinSyncEndCommandBatch(void* context, CFsmEvent* event) return true; } +bool CNetClient::OnRejoined(void *context, CFsmEvent* event) +{ + ENSURE(event->GetType() == (uint)NMT_REJOINED); + + CNetClient* client = (CNetClient*)context; + JSContext* cx = client->GetScriptInterface().GetContext(); + + CRejoinedMessage* message = (CRejoinedMessage*)event->GetParamRef(); + JS::RootedValue msg(cx); + client->GetScriptInterface().Eval("({'type':'rejoined'})", &msg); + client->GetScriptInterface().SetProperty(msg, "guid", std::string(message->m_GUID), false); + client->PushGuiMessage(msg); + + return true; +} + bool CNetClient::OnLoadedGame(void* context, CFsmEvent* event) { ENSURE(event->GetType() == (uint)NMT_LOADED_GAME); diff --git a/source/network/NetClient.h b/source/network/NetClient.h index fac1534ef7..4269f8cf5d 100644 --- a/source/network/NetClient.h +++ b/source/network/NetClient.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011 Wildfire Games. +/* Copyright (C) 2015 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -180,6 +180,12 @@ public: void SendReadyMessage(const int status); + /** + * Call when the client has rejoined a running match and finished + * the loading screen. + */ + void SendRejoinedMessage(); + private: // Net message / FSM transition handlers static bool OnConnect(void* context, CFsmEvent* event); @@ -194,6 +200,7 @@ private: static bool OnGameStart(void* context, CFsmEvent* event); static bool OnJoinSyncStart(void* context, CFsmEvent* event); static bool OnJoinSyncEndCommandBatch(void* context, CFsmEvent* event); + static bool OnRejoined(void* context, CFsmEvent* event); static bool OnLoadedGame(void* context, CFsmEvent* event); /** diff --git a/source/network/NetMessage.cpp b/source/network/NetMessage.cpp index daa892c279..1dd31d5105 100644 --- a/source/network/NetMessage.cpp +++ b/source/network/NetMessage.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2011 Wildfire Games. +/* Copyright (C) 2015 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -131,6 +131,10 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData, pNewMessage = new CJoinSyncStartMessage; break; + case NMT_REJOINED: + pNewMessage = new CRejoinedMessage; + break; + case NMT_LOADED_GAME: pNewMessage = new CLoadedGameMessage; break; @@ -174,7 +178,7 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData, case NMT_CHAT: pNewMessage = new CChatMessage; break; - + case NMT_READY: pNewMessage = new CReadyMessage; break; diff --git a/source/network/NetMessages.h b/source/network/NetMessages.h index 68cf24da95..83ca5bb351 100644 --- a/source/network/NetMessages.h +++ b/source/network/NetMessages.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2011 Wildfire Games. +/* Copyright (C) 2015 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -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 0x01010005 // Arbitrary protocol +#define PS_PROTOCOL_VERSION 0x01010006 // Arbitrary protocol #define PS_DEFAULT_PORT 0x5073 // 'P', 's' // Defines the list of message types. The order of the list must not change. @@ -57,6 +57,8 @@ enum NetMessageType NMT_JOIN_SYNC_START, + NMT_REJOINED, + NMT_LOADED_GAME, NMT_GAME_START, NMT_END_COMMAND_BATCH, @@ -155,6 +157,10 @@ END_NMT_CLASS() START_NMT_CLASS_(JoinSyncStart, NMT_JOIN_SYNC_START) END_NMT_CLASS() +START_NMT_CLASS_(Rejoined, NMT_REJOINED) + NMT_FIELD(CStr8, m_GUID) +END_NMT_CLASS() + START_NMT_CLASS_(LoadedGame, NMT_LOADED_GAME) NMT_FIELD_INT(m_CurrentTurn, u32, 4) END_NMT_CLASS() diff --git a/source/network/NetServer.cpp b/source/network/NetServer.cpp index b3e58d842a..1b85f016a0 100644 --- a/source/network/NetServer.cpp +++ b/source/network/NetServer.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2014 Wildfire Games. +/* Copyright (C) 2015 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -593,6 +593,7 @@ void CNetServerWorker::SetupSession(CNetServerSession* session) 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_CONNECTION_LOST, NSS_UNCONNECTED, (void*)&OnDisconnect, context); session->AddTransition(NSS_INGAME, (uint)NMT_CHAT, NSS_INGAME, (void*)&OnChat, context); session->AddTransition(NSS_INGAME, (uint)NMT_SIMULATION_COMMAND, NSS_INGAME, (void*)&OnInGame, context); @@ -988,6 +989,23 @@ bool CNetServerWorker::OnJoinSyncingLoadedGame(void* context, CFsmEvent* event) return true; } +bool CNetServerWorker::OnRejoined(void* context, CFsmEvent* event) +{ + // A client has finished rejoining and the loading screen disappeared. + ENSURE(event->GetType() == (uint)NMT_REJOINED); + + CNetServerSession* session = (CNetServerSession*)context; + CNetServerWorker& server = session->GetServer(); + + CRejoinedMessage* message = (CRejoinedMessage*)event->GetParamRef(); + + message->m_GUID = session->GetGUID(); + + server.Broadcast(message); + + return true; +} + bool CNetServerWorker::OnDisconnect(void* context, CFsmEvent* event) { ENSURE(event->GetType() == (uint)NMT_CONNECTION_LOST); diff --git a/source/network/NetServer.h b/source/network/NetServer.h index 7ba3cb52dd..a82af8242c 100644 --- a/source/network/NetServer.h +++ b/source/network/NetServer.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2013 Wildfire Games. +/* Copyright (C) 2015 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -262,6 +262,7 @@ private: static bool OnReady(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 OnDisconnect(void* context, CFsmEvent* event); void CheckGameLoadStatus(CNetServerSession* changedSession); diff --git a/source/ps/Game.cpp b/source/ps/Game.cpp index a83e57b203..753521809b 100644 --- a/source/ps/Game.cpp +++ b/source/ps/Game.cpp @@ -235,6 +235,9 @@ PSRETURN CGame::ReallyStartGame() if (CRenderer::IsInitialised()) Render(); + if (g_NetClient) + g_NetClient->LoadFinished(); + // Call the reallyStartGame GUI function, but only if it exists if (g_GUI && g_GUI->HasPages()) { @@ -243,9 +246,6 @@ PSRETURN CGame::ReallyStartGame() g_GUI->GetActiveGUI()->GetScriptInterface()->CallFunctionVoid(global, "reallyStartGame"); } - if (g_NetClient) - g_NetClient->LoadFinished(); - debug_printf("GAME STARTED, ALL INIT COMPLETE\n"); // The call tree we've built for pregame probably isn't useful in-game.