Ticket #1950: pausing_3_wip.patch

File pausing_3_wip.patch, 20.4 KB (added by elexis, 8 years ago)

Rewrote all JS code to be less ugly. Needs the removal of XML duplicates and some fixing.

  • binaries/data/mods/public/gui/common/functions_global_object.js

    function updateCounters()  
    147147function displayGamestateNotifications()
    148148{
    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    {
    157155        let netwarnings = getNetworkWarnings();
    158156        messages = messages.concat(netwarnings.messages);
  • binaries/data/mods/public/gui/credits/texts/programming.json

     
    5454            {"nick": "Deiz"},
    5555            {"nick": "Dietger", "name": "Dietger van Antwerpen"},
    5656            {"nick": "dumbo"},
    5757            {"nick": "dvangennip", "name": "Doménique"},
    5858            {"nick": "Echelon9", "name": "Rhys Kidd"},
     59            {"nick": "echotangoecho"},
    5960            {"nick": "eihrul", "name": "Lee Salzman"},
    6061            {"nick": "elexis", "name": "Alexander Heinsius"},
    6162            {"nick": "EmjeR", "name": "Matthijs de Rijk"},
    6263            {"nick": "EMontana"},
    6364            {"nick": "ericb"},
  • binaries/data/mods/public/gui/session/menu.js

    function openStrucTree()  
    668668        "callback": "resumeGame",
    669669    });
    670670}
    671671
    672672/**
    673  * Pause the game in single player mode.
     673 * Pause or resume the game.
     674 *
     675 * @param avoidMultiplayerPause - Pause if a message box is opened in a singleplayer game, but don't pause a multiplayer match for that.
    674676 */
    675 function pauseGame()
     677function pauseGame(pause = true, avoidMultiplayerPause = true)
    676678{
    677     if (g_IsNetworked)
     679    if (g_IsNetworked && avoidMultiplayerPause)
    678680        return;
    679681
    680     Engine.GetGUIObjectByName("pauseButtonText").caption = translate("Resume");
    681     Engine.GetGUIObjectByName("pauseOverlay").hidden = false;
    682     Engine.SetPaused(true);
     682    g_Paused = pause;
     683    Engine.SetPaused(pause, true);
     684    updatePauseOverlay();
    683685}
    684686
    685687function resumeGame()
    686688{
    687     Engine.GetGUIObjectByName("pauseButtonText").caption = translate("Pause");
    688     Engine.GetGUIObjectByName("pauseOverlay").hidden = true;
    689     Engine.SetPaused(false);
     689    resumeGame();
    690690}
    691691
     692/**
     693 * Called when the current player explicitly wants to pause.
     694 */
    692695function togglePause()
    693696{
    694697    if (!Engine.GetGUIObjectByName("pauseButton").enabled)
    695698        return;
    696699
    697700    closeOpenDialogs();
    698701
    699     let pauseOverlay = Engine.GetGUIObjectByName("pauseOverlay");
     702    g_Paused = !g_Paused;
     703
     704    if (g_IsNetworked)
     705        setClientPauseState(Engine.GetPlayerGUID(), g_Paused);
     706
     707    pauseGame(g_Paused, false);
     708}
     709
     710function setClientPauseState(guid, paused)
     711{
     712    if (paused)
     713        g_PausingClients.push(guid);
     714    else
     715        g_PausingClients.splice(g_PausingClients.indexOf(guid), 1);
    700716
    701     Engine.SetPaused(pauseOverlay.hidden);
    702     Engine.GetGUIObjectByName("pauseButtonText").caption = pauseOverlay.hidden ? translate("Resume") : translate("Pause");
     717    updatePauseOverlay();
     718    Engine.SetPaused(paused, false);
     719}
     720
     721function updatePauseOverlay()
     722{
     723    Engine.GetGUIObjectByName("pauseButtonText").caption = g_Paused ? translate("Pause") : translate("Resume");
     724    Engine.GetGUIObjectByName("multiplayerPauseText").caption = translate("Game paused by") + " " +
     725        g_PausingClients.map(guid => colorizePlayernameByGUID(guid)).join(translate(", "));
    703726
    704     pauseOverlay.hidden = !pauseOverlay.hidden;
     727    Engine.GetGUIObjectByName("multiplayerPauseOverlay").hidden = !g_PausingClients.length;
     728    Engine.GetGUIObjectByName("multiplayerResumeMessage").hidden = !g_Paused;
     729    Engine.GetGUIObjectByName("pauseOverlay").hidden = !g_Paused;
    705730}
    706731
    707732function openManual()
    708733{
    709734    closeOpenDialogs();
  • binaries/data/mods/public/gui/session/messages.js

    var g_ChatTimers = [];  
    2929 */
    3030var g_NetMessageTypes = {
    3131    "netstatus": msg => handleNetStatusMessage(msg),
    3232    "netwarn": msg => addNetworkWarning(msg),
    3333    "players": msg => handlePlayerAssignmentsMessage(msg),
     34    "paused": msg => setClientPauseState(message.guid, message.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 }) }),
    3738    "chat": msg => addChatMessage({ "type": "message", "guid": msg.guid, "text": msg.text }),
    3839    "aichat": msg => addChatMessage({ "type": "message", "guid": msg.guid, "text": msg.text, "translate": true }),
    function handlePlayerAssignmentsMessage(  
    459460    for (let guid in g_PlayerAssignments)
    460461    {
    461462        if (message.hosts[guid])
    462463            continue;
    463464
     465        setClientPauseState(guid, false);
     466
    464467        addChatMessage({ "type": "disconnect", "guid": guid });
    465468
    466469        for (let id in g_Players)
    467470            if (g_Players[id].guid == guid)
    468471                g_Players[id].offline = true;
  • binaries/data/mods/public/gui/session/session.js

    var g_IsObserver = false;  
    3636 * True if the current user has rejoined (or joined the game after it started).
    3737 */
    3838var g_HasRejoined = false;
    3939
    4040/**
     41 * True if the current player has paused the game.
     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();
    4454
    4555/**
  • binaries/data/mods/public/gui/session/session.xml

     
    190190        </object>
    191191        <action on="Press">togglePause();</action>
    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="multiplayerResumeMessage" 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        <action on="Press">togglePause();</action>
     210    </object>
     211
     212    <!-- ================================  ================================ -->
    195213    <!-- Notification Area -->
    196214    <!-- ================================  ================================ -->
    197215    <object name="notificationPanel" type="image" size="50%-300 60 50%+300 120" ghost="true">
    198216        <object name="notificationText" size="0 0 100% 100%" type="text" style="notificationPanel" ghost="true"/>
    199217    </object>
  • source/gui/scripting/ScriptFunctions.cpp

    bool IsPaused(ScriptInterface::CxPrivate  
    770770
    771771    return g_Game->m_Paused;
    772772}
    773773
    774774// Pause/unpause the game
    775 void SetPaused(ScriptInterface::CxPrivate* pCxPrivate, bool pause)
     775void SetPaused(ScriptInterface::CxPrivate* pCxPrivate, bool pause, bool sendMessage)
    776776{
    777777    if (!g_Game)
    778778    {
    779779        JS_ReportError(pCxPrivate->pScriptInterface->GetContext(), "Game is not started");
    780780        return;
    void SetPaused(ScriptInterface::CxPrivat  
    782782    g_Game->m_Paused = pause;
    783783#if CONFIG2_AUDIO
    784784    if (g_SoundManager)
    785785        g_SoundManager->Pause(pause);
    786786#endif
     787
     788    if (g_NetClient && sendMessage)
     789        g_NetClient->SendPausedMessage(pause);
    787790}
    788791
    789792// Return the global frames-per-second value.
    790793// params:
    791794// returns: FPS [int]
    void GuiScriptingInit(ScriptInterface& s  
    10851088    scriptInterface.RegisterFunction<bool, std::string, &HotkeyIsPressed_>("HotkeyIsPressed");
    10861089    scriptInterface.RegisterFunction<void, std::wstring, &DisplayErrorDialog>("DisplayErrorDialog");
    10871090    scriptInterface.RegisterFunction<JS::Value, &GetProfilerState>("GetProfilerState");
    10881091    scriptInterface.RegisterFunction<void, &ExitProgram>("Exit");
    10891092    scriptInterface.RegisterFunction<bool, &IsPaused>("IsPaused");
    1090     scriptInterface.RegisterFunction<void, bool, &SetPaused>("SetPaused");
     1093    scriptInterface.RegisterFunction<void, bool, bool, &SetPaused>("SetPaused");
    10911094    scriptInterface.RegisterFunction<int, &GetFps>("GetFPS");
    10921095    scriptInterface.RegisterFunction<std::wstring, int, &GetBuildTimestamp>("GetBuildTimestamp");
    10931096    scriptInterface.RegisterFunction<JS::Value, std::wstring, &ReadJSONFile>("ReadJSONFile");
    10941097    scriptInterface.RegisterFunction<void, std::wstring, JS::HandleValue, &WriteJSONFile>("WriteJSONFile");
    10951098    scriptInterface.RegisterFunction<bool, std::string, &TemplateExists>("TemplateExists");
  • source/network/NetClient.cpp

    CNetClient::CNetClient(CGame* game, bool  
    120120
    121121    AddTransition(NCS_INGAME, (uint)NMT_REJOINED, NCS_INGAME, (void*)&OnRejoined, context);
    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);
    128129    AddTransition(NCS_INGAME, (uint)NMT_SIMULATION_COMMAND, NCS_INGAME, (void*)&OnInGame, context);
    129130    AddTransition(NCS_INGAME, (uint)NMT_SYNC_ERROR, NCS_INGAME, (void*)&OnInGame, context);
    void CNetClient::SendRejoinedMessage()  
    339340{
    340341    CRejoinedMessage rejoinedMessage;
    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
    347355
    348356    Status status = m_Session->GetFileTransferer().HandleMessageReceive(message);
    bool CNetClient::OnClientPerformance(voi  
    725733    }
    726734
    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);
    733758
    734759    CNetClient* client = (CNetClient*)context;
  • source/network/NetClient.h

    public:  
    195195     * Call when the client has rejoined a running match and finished
    196196     * the loading screen.
    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);
    203208    static bool OnHandshake(void* context, CFsmEvent* event);
    204209    static bool OnHandshakeResponse(void* context, CFsmEvent* event);
    private:  
    213218    static bool OnJoinSyncEndCommandBatch(void* context, CFsmEvent* event);
    214219    static bool OnRejoined(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    /**
    221227     * Take ownership of a session object, and use it for all network communication.
    222228     */
  • source/network/NetMessage.cpp

    CNetMessage* CNetMessageFactory::CreateM  
    145145
    146146    case NMT_CLIENT_PERFORMANCE:
    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;
    153157
    154158    case NMT_SERVER_HANDSHAKE:
  • source/network/NetMessages.h

     
    2626#include "ps/CStr.h"
    2727#include "scriptinterface/ScriptVal.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.
    3535// The message types having a negative value are used internally and not sent
    3636// over the network. The message types used for network communication have
    enum NetMessageType  
    6060    NMT_REJOINED,
    6161    NMT_KICKED,
    6262
    6363    NMT_CLIENT_TIMEOUT,
    6464    NMT_CLIENT_PERFORMANCE,
     65    NMT_CLIENT_PAUSED,
    6566
    6667    NMT_LOADED_GAME,
    6768    NMT_GAME_START,
    6869    NMT_END_COMMAND_BATCH,
    6970    NMT_SYNC_CHECK, // OOS-detection hash checking
    START_NMT_CLASS_(ClientPerformance, NMT_  
    181182        NMT_FIELD(CStr8, m_GUID)
    182183        NMT_FIELD_INT(m_MeanRTT, u32, 4)
    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()
    189195
    190196START_NMT_CLASS_(GameStart, NMT_GAME_START)
  • source/network/NetServer.cpp

    void CNetServerWorker::SetupSession(CNet  
    653653
    654654    session->AddTransition(NSS_JOIN_SYNCING, (uint)NMT_CONNECTION_LOST, NSS_UNCONNECTED, (void*)&OnDisconnect, context);
    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);
    661662    session->AddTransition(NSS_INGAME, (uint)NMT_SYNC_CHECK, NSS_INGAME, (void*)&OnInGame, context);
    662663    session->AddTransition(NSS_INGAME, (uint)NMT_END_COMMAND_BATCH, NSS_INGAME, (void*)&OnInGame, context);
    void CNetServerWorker::OnUserJoin(CNetSe  
    696697    session->SendMessage(&assignMessage);
    697698}
    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)
    704709        m_ServerTurnManager->UninitialiseClient(session->GetHostID()); // TODO: only for non-observers
    705710
    bool CNetServerWorker::OnJoinSyncingLoad  
    11671172    // Tell the client that everything has finished loading and it should start now
    11681173    CLoadedGameMessage loaded;
    11691174    loaded.m_CurrentTurn = readyTurn;
    11701175    session->SendMessage(&loaded);
    11711176
     1177    // Send all pausing players to the client.
     1178    for (const CStr& guid : server.m_PausingPlayers)
     1179    {
     1180        CClientPausedMessage pausedMessage;
     1181        pausedMessage.m_GUID = guid;
     1182        pausedMessage.m_Pause = true;
     1183        session->SendMessage(&pausedMessage);
     1184    }
     1185
    11721186    return true;
    11731187}
    11741188
    11751189bool CNetServerWorker::OnRejoined(void* context, CFsmEvent* event)
    11761190{
    bool CNetServerWorker::OnDisconnect(void  
    11991213    server.OnUserLeave(session);
    12001214
    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)
    12071260    {
    12081261        if (session != changedSession && session->GetCurrState() != NSS_INGAME)
  • source/network/NetServer.h

    private:  
    272272    static bool OnReady(void* context, CFsmEvent* event);
    273273    static bool OnLoadedGame(void* context, CFsmEvent* event);
    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
    280281    void ConstructPlayerAssignmentMessage(CPlayerAssignmentMessage& message);
    281282
    private:  
    314315    CStrW m_WelcomeMessage;
    315316
    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;
    322328
    323329    CStr m_HostGUID;