Ticket #1950: pausing6.patch

File pausing6.patch, 20.7 KB (added by echotangoecho, 8 years ago)
  • binaries/data/mods/public/gui/common/functions_global_object.js

     
    139139    let messages = [];
    140140    let maxTextWidth = 0;
    141141
    142     // TODO: Players who paused the game should be added here
    143 
    144142    // Add network warnings
    145143    if (Engine.ConfigDB_GetValue("user", "overlay.netwarnings") == "true")
    146144    {
  • binaries/data/mods/public/gui/credits/texts/programming.json

     
    5757            {"nick": "dumbo"},
    5858            {"nick": "dvangennip", "name": "Doménique"},
    5959            {"nick": "Echelon9", "name": "Rhys Kidd"},
     60            {"nick": "echotangoecho"},
    6061            {"nick": "eihrul", "name": "Lee Salzman"},
    6162            {"nick": "elexis", "name": "Alexander Heinsius"},
    6263            {"nick": "EmjeR", "name": "Matthijs de Rijk"},
  • binaries/data/mods/public/gui/loading/loading.js

     
    105105
    106106    // Restore default cursor.
    107107    Engine.SetCursor("arrow-default");
    108 
    109     // Notify the other clients that we have finished the loading screen
    110     if (g_Data.isNetworked && g_Data.isRejoining)
    111         Engine.SendNetworkRejoined();
    112108}
  • binaries/data/mods/public/gui/session/menu.js

     
    666666}
    667667
    668668/**
    669  * Pause the game in single player mode.
     669 * Pause or resume the game.
     670 *
     671 * @param explicit true if the player explicitly wants to pause or resume.
     672 * If this argument isn't set, a multiplayer game won't be paused and the pause overlay
     673 * won't be shown in single player.
    670674 */
    671 function pauseGame()
     675function pauseGame(pause = true, explicit = false)
    672676{
     677    if (g_IsNetworked && !explicit)
     678        return;
     679
     680    if (explicit)
     681        g_Paused = pause;
     682
     683    Engine.SetPaused(g_Paused || pause, explicit);
     684
    673685    if (g_IsNetworked)
     686    {
     687        setClientPauseState(Engine.GetPlayerGUID(), g_Paused);
    674688        return;
     689    }
    675690
    676     Engine.GetGUIObjectByName("pauseButtonText").caption = translate("Resume");
    677     Engine.GetGUIObjectByName("pauseOverlay").hidden = false;
    678     Engine.SetPaused(true);
     691    updatePauseOverlay();
    679692}
    680693
    681 function resumeGame()
     694/**
     695 * Resume the game.
     696 *
     697 * @param explicit true if the player explicitly wants to resume the game.
     698 * If this argument isn't set, a multiplayer game won't be resumed and the pause overlay won't
     699 * be closed in single player.
     700 */
     701function resumeGame(explicit = false)
    682702{
    683     Engine.GetGUIObjectByName("pauseButtonText").caption = translate("Pause");
    684     Engine.GetGUIObjectByName("pauseOverlay").hidden = true;
    685     Engine.SetPaused(false);
     703    pauseGame(false, explicit);
    686704}
    687705
     706/**
     707 * Called when the current player toggles a pause button.
     708 */
    688709function togglePause()
    689710{
    690711    if (!Engine.GetGUIObjectByName("pauseButton").enabled)
     
    692713
    693714    closeOpenDialogs();
    694715
    695     let pauseOverlay = Engine.GetGUIObjectByName("pauseOverlay");
     716    pauseGame(!g_Paused, true);
     717}
    696718
    697     Engine.SetPaused(pauseOverlay.hidden);
    698     Engine.GetGUIObjectByName("pauseButtonText").caption = pauseOverlay.hidden ? translate("Resume") : translate("Pause");
     719/**
     720 * Called when a client pauses or resumes in a multiplayer game.
     721 */
     722function setClientPauseState(guid, paused)
     723{
     724    // Update the list of pausing clients.
     725    let index = g_PausingClients.indexOf(guid)
     726    if (paused && index == -1)
     727        g_PausingClients.push(guid);
     728    else if (!paused && index != -1)
     729        g_PausingClients.splice(index, 1);
    699730
    700     pauseOverlay.hidden = !pauseOverlay.hidden;
     731    updatePauseOverlay(g_PausingClients.length);
     732
     733    Engine.SetPaused(!!g_PausingClients.length, false);
    701734}
    702735
     736/**
     737 * Update the pause overlay.
     738 */
     739function updatePauseOverlay()
     740{
     741    Engine.GetGUIObjectByName("pauseOverlay").hidden = !(g_Paused || g_PausingClients.length);
     742    Engine.GetGUIObjectByName("pauseButtonText").caption = g_Paused ? translate("Resume") : translate("Pause");
     743    Engine.GetGUIObjectByName("resumeMessage").hidden = !g_Paused;
     744    Engine.GetGUIObjectByName("pausedByText").hidden = !g_IsNetworked;
     745    Engine.GetGUIObjectByName("pausedByText").caption = sprintf(translate("Paused by %(players)s"),
     746        { "players": g_PausingClients.map(guid => colorizePlayernameByGUID(guid)).join(translate(", ")) });
     747    Engine.GetGUIObjectByName("pauseOverlay").onPress = g_Paused ? togglePause : function() {};
     748}
     749
    703750function openManual()
    704751{
    705752    closeOpenDialogs();
  • binaries/data/mods/public/gui/session/messages.js

     
    3131    "netstatus": msg => handleNetStatusMessage(msg),
    3232    "netwarn": msg => addNetworkWarning(msg),
    3333    "players": msg => handlePlayerAssignmentsMessage(msg),
     34    "paused": msg => setClientPauseState(msg.guid, msg.pause),
    3435    "rejoined": msg => addChatMessage({ "type": "rejoined", "guid": msg.guid }),
    3536    "kicked": msg => addChatMessage({ "type": "system", "text": sprintf(translate("%(username)s has been kicked"), { "username": msg.username }) }),
    3637    "banned": msg => addChatMessage({ "type": "system", "text": sprintf(translate("%(username)s has been banned"), { "username": msg.username }) }),
     
    449450
    450451    if (message.status == "disconnected")
    451452    {
     453        // Hide the pause overlay, and pause animations.
     454        Engine.GetGUIObjectByName("pauseOverlay").hidden = true;
     455        Engine.SetPaused(true, false);
     456
    452457        g_Disconnected = true;
    453458        closeOpenDialogs();
    454459    }
     460    if (message.status == "active" && g_HasRejoined)
     461    {
     462        // We want to notify the server that we have finished rejoining.
     463        Engine.SendNetworkRejoined();
     464    }
    455465}
    456466
    457467function handlePlayerAssignmentsMessage(message)
     
    462472        if (message.hosts[guid])
    463473            continue;
    464474
     475        setClientPauseState(guid, false);
     476
    465477        addChatMessage({ "type": "disconnect", "guid": guid });
    466478
    467479        for (let id in g_Players)
  • binaries/data/mods/public/gui/session/session.js

     
    3838var g_HasRejoined = false;
    3939
    4040/**
     41 * True if the current player has paused the game explicitly.
     42 */
     43var g_Paused = false;
     44
     45/**
     46 * The list of GUIDs of players who have currently paused the game, if the game is networked.
     47 */
     48var g_PausingClients = [];
     49
     50/**
    4151 * The playerID selected in the change perspective tool.
    4252 */
    4353var g_ViewedPlayer = Engine.GetPlayerID();
     
    440450    global.music.setState(global.music.states.DEFEAT);
    441451
    442452    if (!leaveGameAfterResign)
    443         resumeGame();
     453        resumeGame(true);
    444454}
    445455
    446456/**
     
    809819    // Setup tooltip
    810820    let tooltip = "[font=\"sans-bold-16\"]" + template.name.specific + "[/font]";
    811821    let healthLabel = "[font=\"sans-bold-13\"]" + translate("Health:") + "[/font]";
    812     tooltip += "\n" + sprintf(translate("%(label)s %(current)s / %(max)s"), { 
     822    tooltip += "\n" + sprintf(translate("%(label)s %(current)s / %(max)s"), {
    813823        "label": healthLabel,
    814824        "current": Math.ceil(heroState.hitpoints),
    815825        "max": Math.ceil(heroState.maxHitpoints)
  • binaries/data/mods/public/gui/session/session.xml

     
    174174    <!-- ================================  ================================ -->
    175175    <!-- Pause Overlay -->
    176176    <!-- ================================  ================================ -->
    177     <object type="button"
    178         name="pauseOverlay"
    179         size="0 0 100% 100%"
    180         tooltip_style="sessionToolTip"
    181         hidden="true"
    182         z="0"
    183     >
     177    <object type="button" name="pauseOverlay" size="0 0 100% 100%" tooltip_style="sessionToolTip" hidden="true" z="0">
    184178        <object size="0 0 100% 100%" type="image" sprite="devCommandsBackground" ghost="true" z="0"/>
    185         <object size="50%-128 50%-20 50%+128 50%+20" type="text" style="PauseText" ghost="true" z="0">
     179        <object size="50%-128 40%-20 50%+128 40%+20" type="text" style="PauseText" ghost="true" z="0">
    186180            <translatableAttribute id="caption">Game Paused</translatableAttribute>
    187181        </object>
    188         <object size="50%-128 50%+20 50%+128 50%+30" type="text" style="PauseMessageText" ghost="true" z="0">
     182        <object name="resumeMessage" size="50%-128 40%+20 50%+128 40%+40" type="text" style="ResumeMessageText" ghost="true" z="0">
    189183            <translatableAttribute id="caption">Click to Resume Game</translatableAttribute>
    190184        </object>
     185        <object name="pausedByText" size="30% 40%+50 70% 100%" type="text" style="PausedByText" ghost="true" hidden="true" z="0"/>
    191186        <action on="Press">togglePause();</action>
    192187    </object>
    193188
     
    261256    <!-- Trade Window -->
    262257    <!-- ================================  ================================ -->
    263258    <include file="gui/session/trade_window.xml"/>
    264    
     259
    265260    <!-- ================================  ================================ -->
    266261    <!-- Top Panel  -->
    267262    <!-- ================================  ================================ -->
     
    305300        <!-- ================================  ================================ -->
    306301        <!-- Supplemental Details Panel (Left of Selection Details) -->
    307302        <!-- ================================  ================================ -->
    308         <object 
     303        <object
    309304            size="50%-304 100%-170 50%-110 100%"
    310305            name="supplementalSelectionDetails"
    311306            type="image"
  • binaries/data/mods/public/gui/session/styles.xml

     
    1212       text_valign="center"
    1313    />
    1414
    15     <style name="PauseMessageText"
     15    <style name="ResumeMessageText"
    1616       font="sans-bold-12"
    1717       textcolor="white"
    1818       text_align="center"
     
    1919       text_valign="center"
    2020    />
    2121
     22    <style name="PausedByText"
     23       font="sans-bold-16"
     24       textcolor="white"
     25       text_align="center"
     26       text_valign="top"
     27    />
     28
    2229    <style name="BuildNameText"
    2330       font="sans-stroke-12"
    2431       textcolor="white"
     
    116123       text_valign="center"
    117124       ghost="true"
    118125    />
    119    
     126
    120127    <style name="StatsTextCentered"
    121128       font="sans-stroke-12"
    122129       textcolor="white"
     
    124131       text_valign="center"
    125132       ghost="true"
    126133    />
    127    
     134
    128135    <style name="StatsTextRight"
    129136       font="sans-stroke-12"
    130137       textcolor="white"
     
    132139       text_valign="center"
    133140       ghost="true"
    134141    />
    135    
     142
    136143    <style name="CarryingTextRight"
    137144       font="sans-bold-stroke-13"
    138145       textcolor="white"
     
    140147       text_valign="center"
    141148       ghost="true"
    142149    />
    143    
     150
    144151    <style name="SpecificNameCentered"
    145152       font="sans-bold-stroke-13"
    146153       textcolor="gold"
     
    148155       text_valign="center"
    149156       ghost="true"
    150157    />
    151    
     158
    152159    <style name="GenericNameCentered"
    153160       font="sans-stroke-12"
    154161       textcolor="white"
     
    156163       text_valign="center"
    157164       ghost="true"
    158165    />
    159    
     166
    160167    <style name="SettingsText"
    161168       font="sans-stroke-16"
    162169       textcolor="white"
  • source/gui/scripting/ScriptFunctions.cpp

     
    778778}
    779779
    780780// Pause/unpause the game
    781 void SetPaused(ScriptInterface::CxPrivate* pCxPrivate, bool pause)
     781void SetPaused(ScriptInterface::CxPrivate* pCxPrivate, bool pause, bool sendMessage)
    782782{
    783783    if (!g_Game)
    784784    {
     
    790790    if (g_SoundManager)
    791791        g_SoundManager->Pause(pause);
    792792#endif
     793
     794    if (g_NetClient && sendMessage)
     795        g_NetClient->SendPausedMessage(pause);
    793796}
    794797
    795798// Return the global frames-per-second value.
     
    10931096    scriptInterface.RegisterFunction<JS::Value, &GetProfilerState>("GetProfilerState");
    10941097    scriptInterface.RegisterFunction<void, &ExitProgram>("Exit");
    10951098    scriptInterface.RegisterFunction<bool, &IsPaused>("IsPaused");
    1096     scriptInterface.RegisterFunction<void, bool, &SetPaused>("SetPaused");
     1099    scriptInterface.RegisterFunction<void, bool, bool, &SetPaused>("SetPaused");
    10971100    scriptInterface.RegisterFunction<int, &GetFps>("GetFPS");
    10981101    scriptInterface.RegisterFunction<std::wstring, int, &GetBuildTimestamp>("GetBuildTimestamp");
    10991102    scriptInterface.RegisterFunction<JS::Value, std::wstring, &ReadJSONFile>("ReadJSONFile");
  • source/network/NetClient.cpp

     
    122122    AddTransition(NCS_INGAME, (uint)NMT_KICKED, NCS_INGAME, (void*)&OnKicked, context);
    123123    AddTransition(NCS_INGAME, (uint)NMT_CLIENT_TIMEOUT, NCS_INGAME, (void*)&OnClientTimeout, context);
    124124    AddTransition(NCS_INGAME, (uint)NMT_CLIENT_PERFORMANCE, NCS_INGAME, (void*)&OnClientPerformance, context);
     125    AddTransition(NCS_INGAME, (uint)NMT_CLIENT_PAUSED, NCS_INGAME, (void*)&OnClientPaused, context);
    125126    AddTransition(NCS_INGAME, (uint)NMT_CHAT, NCS_INGAME, (void*)&OnChat, context);
    126127    AddTransition(NCS_INGAME, (uint)NMT_GAME_SETUP, NCS_INGAME, (void*)&OnGameSetup, context);
    127128    AddTransition(NCS_INGAME, (uint)NMT_PLAYER_ASSIGNMENT, NCS_INGAME, (void*)&OnPlayerAssignment, context);
     
    341342    SendMessage(&rejoinedMessage);
    342343}
    343344
     345void CNetClient::SendPausedMessage(bool pause)
     346{
     347    CClientPausedMessage pausedMessage;
     348    pausedMessage.m_Pause = pause;
     349    SendMessage(&pausedMessage);
     350}
     351
    344352bool CNetClient::HandleMessage(CNetMessage* message)
    345353{
    346354    // Handle non-FSM messages first
     
    727735    return true;
    728736}
    729737
     738bool CNetClient::OnClientPaused(void *context, CFsmEvent *event)
     739{
     740    ENSURE(event->GetType() == (uint)NMT_CLIENT_PAUSED);
     741
     742    CNetClient* client = (CNetClient*)context;
     743    JSContext* cx = client->GetScriptInterface().GetContext();
     744    CClientPausedMessage* message = (CClientPausedMessage*)event->GetParamRef();
     745
     746    JS::RootedValue msg(cx);
     747    client->GetScriptInterface().Eval("({ 'type':'paused' })", &msg);
     748    client->GetScriptInterface().SetProperty(msg, "pause", message->m_Pause != 0);
     749    client->GetScriptInterface().SetProperty(msg, "guid", message->m_GUID);
     750    client->PushGuiMessage(msg);
     751
     752    return true;
     753}
     754
    730755bool CNetClient::OnLoadedGame(void* context, CFsmEvent* event)
    731756{
    732757    ENSURE(event->GetType() == (uint)NMT_LOADED_GAME);
  • source/network/NetClient.h

     
    197197     */
    198198    void SendRejoinedMessage();
    199199
     200    /**
     201     * Call when the client has paused or unpaused the game.
     202     */
     203    void SendPausedMessage(bool pause);
     204
    200205private:
    201206    // Net message / FSM transition handlers
    202207    static bool OnConnect(void* context, CFsmEvent* event);
     
    215220    static bool OnKicked(void* context, CFsmEvent* event);
    216221    static bool OnClientTimeout(void* context, CFsmEvent* event);
    217222    static bool OnClientPerformance(void* context, CFsmEvent* event);
     223    static bool OnClientPaused(void* context, CFsmEvent* event);
    218224    static bool OnLoadedGame(void* context, CFsmEvent* event);
    219225
    220226    /**
  • source/network/NetMessage.cpp

     
    147147        pNewMessage = new CClientPerformanceMessage;
    148148        break;
    149149
     150    case NMT_CLIENT_PAUSED:
     151        pNewMessage = new CClientPausedMessage;
     152        break;
     153
    150154    case NMT_LOADED_GAME:
    151155        pNewMessage = new CLoadedGameMessage;
    152156        break;
  • source/network/NetMessages.h

     
    2828
    2929#define PS_PROTOCOL_MAGIC               0x5073013f      // 'P', 's', 0x01, '?'
    3030#define PS_PROTOCOL_MAGIC_RESPONSE      0x50630121      // 'P', 'c', 0x01, '!'
    31 #define PS_PROTOCOL_VERSION             0x01010012      // Arbitrary protocol
     31#define PS_PROTOCOL_VERSION             0x01010013      // Arbitrary protocol
    3232#define PS_DEFAULT_PORT                 0x5073          // 'P', 's'
    3333
    3434// Defines the list of message types. The order of the list must not change.
     
    6262
    6363    NMT_CLIENT_TIMEOUT,
    6464    NMT_CLIENT_PERFORMANCE,
     65    NMT_CLIENT_PAUSED,
    6566
    6667    NMT_LOADED_GAME,
    6768    NMT_GAME_START,
     
    183184    NMT_END_ARRAY()
    184185END_NMT_CLASS()
    185186
     187START_NMT_CLASS_(ClientPaused, NMT_CLIENT_PAUSED)
     188    NMT_FIELD(CStr, m_GUID)
     189    NMT_FIELD_INT(m_Pause, u8, 1)
     190END_NMT_CLASS()
     191
    186192START_NMT_CLASS_(LoadedGame, NMT_LOADED_GAME)
    187193    NMT_FIELD_INT(m_CurrentTurn, u32, 4)
    188194END_NMT_CLASS()
  • source/network/NetServer.cpp

     
    655655    session->AddTransition(NSS_JOIN_SYNCING, (uint)NMT_LOADED_GAME, NSS_INGAME, (void*)&OnJoinSyncingLoadedGame, context);
    656656
    657657    session->AddTransition(NSS_INGAME, (uint)NMT_REJOINED, NSS_INGAME, (void*)&OnRejoined, context);
     658    session->AddTransition(NSS_INGAME, (uint)NMT_CLIENT_PAUSED, NSS_INGAME, (void*)&OnClientPaused, context);
    658659    session->AddTransition(NSS_INGAME, (uint)NMT_CONNECTION_LOST, NSS_UNCONNECTED, (void*)&OnDisconnect, context);
    659660    session->AddTransition(NSS_INGAME, (uint)NMT_CHAT, NSS_INGAME, (void*)&OnChat, context);
    660661    session->AddTransition(NSS_INGAME, (uint)NMT_SIMULATION_COMMAND, NSS_INGAME, (void*)&OnInGame, context);
     
    698699
    699700void CNetServerWorker::OnUserLeave(CNetServerSession* session)
    700701{
     702    std::vector<CStr>::iterator pausing = std::find(m_PausingPlayers.begin(), m_PausingPlayers.end(), session->GetGUID());
     703    if (pausing != m_PausingPlayers.end())
     704        m_PausingPlayers.erase(pausing);
     705
    701706    RemovePlayer(session->GetGUID());
    702707
    703708    if (m_ServerTurnManager && session->GetCurrState() != NSS_JOIN_SYNCING)
     
    11861191
    11871192    server.Broadcast(message);
    11881193
     1194    // Send all pausing players to the rejoined client.
     1195    for (const CStr& guid : server.m_PausingPlayers)
     1196    {
     1197        CClientPausedMessage pausedMessage;
     1198        pausedMessage.m_GUID = guid;
     1199        pausedMessage.m_Pause = true;
     1200        session->SendMessage(&pausedMessage);
     1201    }
     1202
    11891203    return true;
    11901204}
    11911205
     
    12011215    return true;
    12021216}
    12031217
     1218bool CNetServerWorker::OnClientPaused(void *context, CFsmEvent *event)
     1219{
     1220    ENSURE(event->GetType() == (uint)NMT_CLIENT_PAUSED);
     1221
     1222    CNetServerSession* session = (CNetServerSession*)context;
     1223    CNetServerWorker& server = session->GetServer();
     1224
     1225    CClientPausedMessage* message = (CClientPausedMessage*)event->GetParamRef();
     1226
     1227    message->m_GUID = session->GetGUID();
     1228
     1229    // Update the list of pausing players.
     1230    std::vector<CStr>::iterator player = std::find(server.m_PausingPlayers.begin(), server.m_PausingPlayers.end(), session->GetGUID());
     1231
     1232    if (message->m_Pause)
     1233    {
     1234        if (player != server.m_PausingPlayers.end())
     1235            return true;
     1236
     1237        server.m_PausingPlayers.push_back(session->GetGUID());
     1238    }
     1239    else
     1240    {
     1241        if (player == server.m_PausingPlayers.end())
     1242            return true;
     1243
     1244        server.m_PausingPlayers.erase(player);
     1245    }
     1246
     1247    // Send messages to clients that are in game, and are not the client who paused.
     1248    for (CNetServerSession* session : server.m_Sessions)
     1249    {
     1250        if (session->GetCurrState() == NSS_INGAME && message->m_GUID != session->GetGUID())
     1251            session->SendMessage(message);
     1252    }
     1253
     1254    return true;
     1255}
     1256
    12041257void CNetServerWorker::CheckGameLoadStatus(CNetServerSession* changedSession)
    12051258{
    12061259    for (const CNetServerSession* session : m_Sessions)
  • source/network/NetServer.h

     
    274274    static bool OnJoinSyncingLoadedGame(void* context, CFsmEvent* event);
    275275    static bool OnRejoined(void* context, CFsmEvent* event);
    276276    static bool OnDisconnect(void* context, CFsmEvent* event);
     277    static bool OnClientPaused(void* context, CFsmEvent* event);
    277278
    278279    void CheckGameLoadStatus(CNetServerSession* changedSession);
    279280
     
    316317    std::vector<u32> m_BannedIPs;
    317318    std::vector<CStrW> m_BannedPlayers;
    318319
     320    /**
     321     * Holds the GUIDs of all currently paused players.
     322     */
     323    std::vector<CStr> m_PausingPlayers;
     324
    319325    u32 m_NextHostID;
    320326
    321327    CNetServerTurnManager* m_ServerTurnManager;