Ticket #1950: pausing2.patch

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

     
    149149    let messages = [];
    150150    let maxTextWidth = 0;
    151151
    152     // TODO: Players who paused the game should be added here
    153 
    154152    // Add network warnings
    155153    if (Engine.ConfigDB_GetValue("user", "overlay.netwarnings") == "true")
    156154    {
  • binaries/data/mods/public/gui/credits/texts/programming.json

     
    5555            {"nick": "dumbo"},
    5656            {"nick": "dvangennip", "name": "Doménique"},
    5757            {"nick": "Echelon9", "name": "Rhys Kidd"},
     58            {"nick": "echotangoecho"},
    5859            {"nick": "eihrul", "name": "Lee Salzman"},
    5960            {"nick": "elexis", "name": "Alexander Heinsius"},
    6061            {"nick": "EmjeR", "name": "Matthijs de Rijk"},
  • binaries/data/mods/public/gui/session/menu.js

     
    682682    Engine.SetPaused(true);
    683683}
    684684
     685/**
     686 * Resume the game in single player mode.
     687 */
    685688function resumeGame()
    686689{
     690    if (g_IsNetworked)
     691        return;
     692
    687693    Engine.GetGUIObjectByName("pauseButtonText").caption = translate("Pause");
    688694    Engine.GetGUIObjectByName("pauseOverlay").hidden = true;
    689695    Engine.SetPaused(false);
     
    696702
    697703    closeOpenDialogs();
    698704
     705    if (g_IsNetworked)
     706    {
     707        if (g_PausingClients.indexOf(Engine.GetPlayerGUID()) == -1)
     708        {
     709            g_PausingClients.push(Engine.GetPlayerGUID());
     710            Engine.GetGUIObjectByName("pauseButtonText").caption = translate("Resume");
     711            Engine.MultiplayerPause(true);
     712        }
     713        else
     714        {
     715            g_PausingClients.splice(g_PausingClients.indexOf(Engine.GetPlayerGUID()), 1);
     716            Engine.GetGUIObjectByName("pauseButtonText").caption = translate("Pause");
     717            Engine.MultiplayerPause(false);
     718        }
     719
     720        updatePauseOverlayMultiplayer();
     721        return;
     722    }
     723
    699724    let pauseOverlay = Engine.GetGUIObjectByName("pauseOverlay");
    700725
    701726    Engine.SetPaused(pauseOverlay.hidden);
  • 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 => handlePlayerPausedMessage(msg),
    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 }) }),
     
    461462        if (message.hosts[guid])
    462463            continue;
    463464
     465        if (g_PausingClients.indexOf(guid) != -1)
     466        {
     467            g_PausingClients.splice(g_PausingClients.indexOf(guid), 1);
     468            updatePauseOverlayMultiplayer();
     469        }
     470
    464471        addChatMessage({ "type": "disconnect", "guid": guid });
    465472
    466473        for (let id in g_Players)
     
    496503    }
    497504}
    498505
     506function handlePlayerPausedMessage(message)
     507{
     508    // Update list of pausing players.
     509    if (message.pause)
     510        g_PausingClients.push(message.guid);
     511    else
     512        g_PausingClients.splice(g_PausingClients.indexOf(message.guid), 1);
     513
     514    Engine.SetPaused(message.pause != 0);
     515
     516    updatePauseOverlayMultiplayer();
     517}
     518
    499519function updateChatAddressees()
    500520{
    501521    let addressees = [
  • binaries/data/mods/public/gui/session/session.js

     
    3838var g_HasRejoined = false;
    3939
    4040/**
     41 * The list of GUIDs of players who have currently paused the game, if the game is networked.
     42 */
     43var g_PausingClients = [];
     44
     45/**
    4146 * The playerID selected in the change perspective tool.
    4247 */
    4348var g_ViewedPlayer = Engine.GetPlayerID();
     
    403408    Engine.GetGUIObjectByName("summaryButton").enabled = g_IsObserver;
    404409}
    405410
     411function updatePauseOverlayMultiplayer()
     412{
     413    // Hide the pause overlay and return if no one paused.
     414    if (!g_PausingClients.length)
     415    {
     416        Engine.GetGUIObjectByName("multiplayerPauseOverlay").hidden = true;
     417        return;
     418    }
     419
     420    // Display "Click to Resume Game" if the player paused, and set the action for this.
     421    if (g_PausingClients.indexOf(Engine.GetPlayerGUID()) != -1)
     422    {
     423        Engine.GetGUIObjectByName("multiplayerPauseMessage").hidden = false;
     424        Engine.GetGUIObjectByName("multiplayerPauseOverlay").onPress = togglePause;
     425    }
     426    else
     427    {
     428        Engine.GetGUIObjectByName("multiplayerPauseMessage").hidden = true;
     429        Engine.GetGUIObjectByName("multiplayerPauseOverlay").onPress = function() {};
     430    }
     431
     432    var playerNames = [];
     433    g_PausingClients.forEach(guid => {
     434        playerNames.push(colorizePlayernameByGUID(guid));
     435    });
     436    Engine.GetGUIObjectByName("multiplayerPauseText").caption = translate("Game Paused by") + " " + playerNames.join(", ");
     437
     438    Engine.GetGUIObjectByName("multiplayerPauseOverlay").hidden = false;
     439}
     440
    406441function reportPerformance(time)
    407442{
    408443    let settings = Engine.GetMapSettings();
  • binaries/data/mods/public/gui/session/session.xml

     
    192192    </object>
    193193
    194194    <!-- ================================  ================================ -->
     195    <!-- Multiplayer Pause Overlay -->
     196    <!-- ================================  ================================ -->
     197    <object type="button"
     198        name="multiplayerPauseOverlay"
     199        size="0 0 100% 100%"
     200        tooltip_style="sessionToolTip"
     201        hidden="true"
     202        z="0"
     203    >
     204        <object size="0 0 100% 100%" type="image" sprite="devCommandsBackground" ghost="true" z="0"/>
     205        <object name="multiplayerPauseText" size="50%-256 50%-60 50%+256 50%+60" type="text" style="PauseText" ghost="true" z="0"/>
     206        <object name="multiplayerPauseMessage" size="50%-128 50%+60 50%+128 50%+70" type="text" style="PauseMessageText" ghost="true" z="0" hidden="true">
     207            <translatableAttribute id="caption">Click to Resume Game</translatableAttribute>
     208        </object>
     209    </object>
     210
     211    <!-- ================================  ================================ -->
    195212    <!-- Notification Area -->
    196213    <!-- ================================  ================================ -->
    197214    <object name="notificationPanel" type="image" size="50%-300 60 50%+300 120" ghost="true">
  • source/gui/scripting/ScriptFunctions.cpp

     
    786786#endif
    787787}
    788788
     789// Pauses the game and notifies the server of the pause.
     790void MultiplayerPause(ScriptInterface::CxPrivate* pCxPrivate, bool pause)
     791{
     792    SetPaused(pCxPrivate, pause);
     793   
     794    ENSURE(g_NetClient);
     795    g_NetClient->SendPausedMessage(pause);
     796}
     797
    789798// Return the global frames-per-second value.
    790799// params:
    791800// returns: FPS [int]
     
    10881097    scriptInterface.RegisterFunction<void, &ExitProgram>("Exit");
    10891098    scriptInterface.RegisterFunction<bool, &IsPaused>("IsPaused");
    10901099    scriptInterface.RegisterFunction<void, bool, &SetPaused>("SetPaused");
     1100    scriptInterface.RegisterFunction<void, bool, &MultiplayerPause>("MultiplayerPause");
    10911101    scriptInterface.RegisterFunction<int, &GetFps>("GetFPS");
    10921102    scriptInterface.RegisterFunction<std::wstring, int, &GetBuildTimestamp>("GetBuildTimestamp");
    10931103    scriptInterface.RegisterFunction<JS::Value, std::wstring, &ReadJSONFile>("ReadJSONFile");
  • source/network/NetClient.cpp

     
    121121    AddTransition(NCS_INGAME, (uint)NMT_KICKED, NCS_INGAME, (void*)&OnKicked, context);
    122122    AddTransition(NCS_INGAME, (uint)NMT_CLIENT_TIMEOUT, NCS_INGAME, (void*)&OnClientTimeout, context);
    123123    AddTransition(NCS_INGAME, (uint)NMT_CLIENT_PERFORMANCE, NCS_INGAME, (void*)&OnClientPerformance, context);
     124    AddTransition(NCS_INGAME, (uint)NMT_CLIENT_PAUSED, NCS_INGAME, (void*)&OnClientPaused, context);
    124125    AddTransition(NCS_INGAME, (uint)NMT_CHAT, NCS_INGAME, (void*)&OnChat, context);
    125126    AddTransition(NCS_INGAME, (uint)NMT_GAME_SETUP, NCS_INGAME, (void*)&OnGameSetup, context);
    126127    AddTransition(NCS_INGAME, (uint)NMT_PLAYER_ASSIGNMENT, NCS_INGAME, (void*)&OnPlayerAssignment, context);
     
    340341    SendMessage(&rejoinedMessage);
    341342}
    342343
     344void CNetClient::SendPausedMessage(bool pause)
     345{
     346    CClientPausedMessage pausedMessage;
     347    pausedMessage.m_Pause = pause;
     348    SendMessage(&pausedMessage);
     349}
     350
    343351bool CNetClient::HandleMessage(CNetMessage* message)
    344352{
    345353    // Handle non-FSM messages first
     
    725733    return true;
    726734}
    727735
     736bool CNetClient::OnClientPaused(void *context, CFsmEvent *event)
     737{
     738    ENSURE(event->GetType() == (uint)NMT_CLIENT_PAUSED);
     739
     740    CNetClient* client = (CNetClient*)context;
     741    JSContext* cx = client->GetScriptInterface().GetContext();
     742    CClientPausedMessage* message = (CClientPausedMessage*)event->GetParamRef();
     743
     744    JS::RootedValue msg(cx);
     745    client->GetScriptInterface().Eval("({ 'type':'paused' })", &msg);
     746    client->GetScriptInterface().SetProperty(msg, "pause", message->m_Pause);
     747    client->GetScriptInterface().SetProperty(msg, "guid", message->m_GUID);
     748    client->PushGuiMessage(msg);
     749
     750    return true;
     751}
     752
    728753bool CNetClient::OnLoadedGame(void* context, CFsmEvent* event)
    729754{
    730755    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             0x01010011      // Arbitrary protocol
     31#define PS_PROTOCOL_VERSION             0x01010012      // 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,
     
    182183    NMT_END_ARRAY()
    183184END_NMT_CLASS()
    184185
     186START_NMT_CLASS_(ClientPaused, NMT_CLIENT_PAUSED)
     187    NMT_FIELD(CStr, m_GUID)
     188    NMT_FIELD_INT(m_Pause, u8, 1)
     189END_NMT_CLASS()
     190
    185191START_NMT_CLASS_(LoadedGame, NMT_LOADED_GAME)
    186192    NMT_FIELD_INT(m_CurrentTurn, u32, 4)
    187193END_NMT_CLASS()
  • source/network/NetServer.cpp

     
    661661    session->AddTransition(NSS_JOIN_SYNCING, (uint)NMT_LOADED_GAME, NSS_INGAME, (void*)&OnJoinSyncingLoadedGame, context);
    662662
    663663    session->AddTransition(NSS_INGAME, (uint)NMT_REJOINED, NSS_INGAME, (void*)&OnRejoined, context);
     664    session->AddTransition(NSS_INGAME, (uint)NMT_CLIENT_PAUSED, NSS_INGAME, (void*)&OnClientPaused, context);
    664665    session->AddTransition(NSS_INGAME, (uint)NMT_CONNECTION_LOST, NSS_UNCONNECTED, (void*)&OnDisconnect, context);
    665666    session->AddTransition(NSS_INGAME, (uint)NMT_CHAT, NSS_INGAME, (void*)&OnChat, context);
    666667    session->AddTransition(NSS_INGAME, (uint)NMT_SIMULATION_COMMAND, NSS_INGAME, (void*)&OnInGame, context);
     
    704705
    705706void CNetServerWorker::OnUserLeave(CNetServerSession* session)
    706707{
     708    std::vector<CStr>::iterator pausing = std::find(m_PausingPlayers.begin(), m_PausingPlayers.end(), session->GetGUID());
     709    if (pausing != m_PausingPlayers.end())
     710    {
     711        // Unpause for the leaving player.
     712        m_PausingPlayers.erase(pausing);
     713    }
     714
    707715    RemovePlayer(session->GetGUID());
    708716
    709717    if (m_ServerTurnManager && session->GetCurrState() != NSS_JOIN_SYNCING)
     
    11741182    loaded.m_CurrentTurn = readyTurn;
    11751183    session->SendMessage(&loaded);
    11761184
     1185    // Send all pausing players to the client.
     1186    for (const CStr& guid : server.m_PausingPlayers)
     1187    {
     1188        CClientPausedMessage pausedMessage;
     1189        pausedMessage.m_GUID = guid;
     1190        pausedMessage.m_Pause = true;
     1191        session->SendMessage(&pausedMessage);
     1192    }
     1193
    11771194    return true;
    11781195}
    11791196
     
    12061223    return true;
    12071224}
    12081225
     1226bool CNetServerWorker::OnClientPaused(void *context, CFsmEvent *event)
     1227{
     1228    ENSURE(event->GetType() == (uint)NMT_CLIENT_PAUSED);
     1229
     1230    CNetServerSession* session = (CNetServerSession*)context;
     1231    CNetServerWorker& server = session->GetServer();
     1232
     1233    CClientPausedMessage* message = (CClientPausedMessage*)event->GetParamRef();
     1234
     1235    message->m_GUID = session->GetGUID();
     1236
     1237    // Update the list of pausing players.
     1238    std::vector<CStr>::iterator player = std::find(server.m_PausingPlayers.begin(), server.m_PausingPlayers.end(), session->GetGUID());
     1239
     1240    if (message->m_Pause)
     1241    {
     1242        if (player != server.m_PausingPlayers.end())
     1243            return true;
     1244
     1245        server.m_PausingPlayers.push_back(session->GetGUID());
     1246    }
     1247    else
     1248    {
     1249        if (player == server.m_PausingPlayers.end())
     1250            return true;
     1251
     1252        server.m_PausingPlayers.erase(player);
     1253    }
     1254
     1255    // Send messages to clients that are in game, and are not the client who paused.
     1256    for (CNetServerSession* session : server.m_Sessions)
     1257    {
     1258        if (session->GetCurrState() == NSS_INGAME && message->m_GUID != session->GetGUID())
     1259            session->SendMessage(message);
     1260    }
     1261
     1262    return true;
     1263}
     1264
    12091265void CNetServerWorker::CheckGameLoadStatus(CNetServerSession* changedSession)
    12101266{
    12111267    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<CStr> 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;