Ticket #3264: t3264_show_network_warnings_WIP_v1.patch

File t3264_show_network_warnings_WIP_v1.patch, 26.5 KB (added by elexis, 8 years ago)

Shows a warning in the top right corner of the screen in gamesetup and session if a client is losing the connection or having a bad ping (< turn length).

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

     
    1 /*
    2     DESCRIPTION : Contains global GUI functions, which will later be accessible from every GUI script/file.
    3     NOTES       : So far, only the message box-related functions are implemented.
    4 */
     1/**
     2 * Contains global GUI functions accessible from every GUI script/file.
     3 * File will be reloaded when switching pages or showing message boxes!
     4 */
     5
     6/**
     7 * Number of milliseconds to display network warnings.
     8 */
     9var g_NetworkWarningTimeout = 3000;
     10
     11/**
     12 * Currently displayed network warnings. At most one message per user.
     13 */
     14var g_NetworkWarnings = {};
    515
    616// *******************************************
    717// messageBox
    818// *******************************************
    919// @params:     int mbWidth, int mbHeight, string mbMessage, string mbTitle, int mbMode, arr mbButtonCaptions, function mbBtnCode, var mbCallbackArgs
    function updateCounters()  
    138148
    139149    var dataCounter = Engine.GetGUIObjectByName("dataCounter");
    140150    dataCounter.caption = caption;
    141151    dataCounter.size = sprintf("100%%-100 40 100%%-5 %(bottom)s", { bottom: 40 + 14 * linesCount });
    142152}
     153
     154/**
     155 * @param msg - GUI message sent by NetServer or NetClient
     156 */
     157function addNetworkWarning(msg)
     158{
     159    let text;
     160
     161    switch (msg.warntype)
     162    {
     163    case "server-timeout":
     164        text = sprintf(translate("Losing connection to server (%(seconds)s)"), {
     165            "seconds": msg.lastReceivedTime
     166        });
     167        break;
     168    case "client-timeout":
     169        text = sprintf(translate("%(player)s losing connection (%(seconds)s)"), {
     170            "player": colorizePlayernameByGUID(msg.guid),
     171            "seconds": msg.lastReceivedTime
     172        });
     173        break;
     174    case "server-latency":
     175        text = sprintf(translate("Bad connection to server (%(milliseconds)sms)"), {
     176            "milliseconds": msg.meanRTT
     177        });
     178        break;
     179    case "client-latency":
     180        text = sprintf(translate("Bad connection to %(player)s (%(milliseconds)sms)"), {
     181            "player": colorizePlayernameByGUID(msg.guid),
     182            "milliseconds": msg.meanRTT
     183        });
     184        break;
     185    }
     186
     187    g_NetworkWarnings[msg.guid || "server"] = {
     188        "added": Date.now(),
     189        "text": text
     190    };
     191}
     192/**
     193 * Displays the most recent network warning of each client onscreen.
     194 */
     195function displayNetworkWarnings()
     196{
     197    // Remove old messages
     198    for (let guid in g_NetworkWarnings)
     199        if (Date.now() - g_NetworkWarnings[guid].added > g_NetworkWarningTimeout)
     200            delete g_NetworkWarnings[guid];
     201
     202    let messages = Object.keys(g_NetworkWarnings).map(guid => g_NetworkWarnings[guid].text);
     203
     204    let networkWarnings = Engine.GetGUIObjectByName("networkWarnings");
     205    networkWarnings.caption = messages.join("\n");
     206    networkWarnings.size = "100%-410 40 100%-110 "+ (40 + 14 * messages.length);
     207}
  • binaries/data/mods/public/gui/common/global.xml

     
    1212
    1313    <object>
    1414
    1515        <!--
    1616        ==========================================
     17        - NETWORK WARNINGS
     18        ==========================================
     19        -->
     20        <object name="networkWarnings"
     21            type="text"
     22            ghost="true"
     23            z="199"
     24            size="100%-410 40 100%-95 40"
     25            font="mono-stroke-10"
     26            textcolor="orange"
     27            text_align="center"
     28            text_valign="top"
     29            sprite="color: 0 0 0 100"
     30            >
     31            <action on="Tick">
     32                displayNetworkWarnings();
     33            </action>
     34        </object>
     35
     36        <!--
     37        ==========================================
    1738        - FPS & REAL TIME & GAME TIME COUNTER
    1839        ==========================================
    1940        -->
    2041        <object name="dataCounter"
    2142            type="text"
  • binaries/data/mods/public/gui/gamesetup/gamesetup.js

    const g_MapPath = {  
    2626/**
    2727 * Processes a CNetMessage (see NetMessage.h, NetMessages.h) sent by the CNetServer.
    2828 */
    2929const g_NetMessageTypes = {
    3030    "netstatus": msg => handleNetStatusMessage(msg),
     31    "netwarn": msg => addNetworkWarning(msg),
    3132    "gamesetup": msg => handleGamesetupMessage(msg),
    3233    "players": msg => handlePlayerAssignmentMessage(msg),
    3334    "ready": msg => handleReadyMessage(msg),
    3435    "start": msg => handleGamestartMessage(msg),
    3536    "kicked": msg => addChatMessage({ "type": "kicked", "username": msg.username }),
  • binaries/data/mods/public/gui/gamesetup/gamesetup_mp.js

    function pollAndHandleNetworkClient()  
    124124                    "playerAssignments": g_PlayerAssignments
    125125                });
    126126                break;
    127127
    128128            case "chat":
    129                 // Ignore, since we have nowhere to display chat messages
     129                break;
     130
     131            case "netwarn":
    130132                break;
    131133
    132134            default:
    133135                error(sprintf("Unrecognised net message type %(messageType)s", { messageType: message.type }));
    134136            }
    function pollAndHandleNetworkClient()  
    167169                default:
    168170                    error(sprintf("Unrecognised netstatus type %(netType)s", { netType: message.status }));
    169171                    break;
    170172                }
    171173                break;
     174
     175            case "netwarn":
     176                break;
     177
    172178            default:
    173179                error(sprintf("Unrecognised net message type %(messageType)s", { messageType: message.type }));
    174180                break;
    175181            }
    176182        }
  • binaries/data/mods/public/gui/session/messages.js

    var g_ChatTimers = [];  
    2727/**
    2828 * Handle all netmessage types that can occur.
    2929 */
    3030var g_NetMessageTypes = {
    3131    "netstatus": msg => handleNetStatusMessage(msg),
     32    "netwarn": msg => addNetworkWarning(msg),
    3233    "players": msg => handlePlayerAssignmentsMessage(msg),
    3334    "rejoined": msg => addChatMessage({ "type": "rejoined", "guid": msg.guid }),
    3435    "kicked": msg => addChatMessage({ "type": "system", "text": sprintf(translate("%(username)s has been kicked"), { "username": msg.username }) }),
    3536    "banned": msg => addChatMessage({ "type": "system", "text": sprintf(translate("%(username)s has been banned"), { "username": msg.username }) }),
    3637    "chat": msg => addChatMessage({ "type": "message", "guid": msg.guid, "text": msg.text }),
  • source/network/NetClient.cpp

     
    1 /* Copyright (C) 2015 Wildfire Games.
     1/* Copyright (C) 2016 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    55 * it under the terms of the GNU General Public License as published by
    66 * the Free Software Foundation, either version 2 of the License, or
    private:  
    6868
    6969CNetClient::CNetClient(CGame* game) :
    7070    m_Session(NULL),
    7171    m_UserName(L"anonymous"),
    7272    m_GUID(ps_generate_guid()), m_HostID((u32)-1), m_ClientTurnManager(NULL), m_Game(game),
    73     m_GameAttributes(game->GetSimulation2()->GetScriptInterface().GetContext())
     73    m_GameAttributes(game->GetSimulation2()->GetScriptInterface().GetContext()),
     74    m_LastConnectionCheck(0)
    7475{
    7576    m_Game->SetTurnManager(NULL); // delete the old local turn manager so we don't accidentally use it
    7677
    7778    void* context = this;
    7879
    CNetClient::CNetClient(CGame* game) :  
    9293    AddTransition(NCS_PREGAME, (uint)NMT_CHAT, NCS_PREGAME, (void*)&OnChat, context);
    9394    AddTransition(NCS_PREGAME, (uint)NMT_READY, NCS_PREGAME, (void*)&OnReady, context);
    9495    AddTransition(NCS_PREGAME, (uint)NMT_GAME_SETUP, NCS_PREGAME, (void*)&OnGameSetup, context);
    9596    AddTransition(NCS_PREGAME, (uint)NMT_PLAYER_ASSIGNMENT, NCS_PREGAME, (void*)&OnPlayerAssignment, context);
    9697    AddTransition(NCS_PREGAME, (uint)NMT_KICKED, NCS_PREGAME, (void*)&OnKicked, context);
     98    AddTransition(NCS_PREGAME, (uint)NMT_CLIENT_TIMEOUT, NCS_PREGAME, (void*)&OnClientTimeout, context);
     99    AddTransition(NCS_PREGAME, (uint)NMT_CLIENT_PERFORMANCE, NCS_PREGAME, (void*)&OnClientPerformance, context);
    97100    AddTransition(NCS_PREGAME, (uint)NMT_GAME_START, NCS_LOADING, (void*)&OnGameStart, context);
    98101    AddTransition(NCS_PREGAME, (uint)NMT_JOIN_SYNC_START, NCS_JOIN_SYNCING, (void*)&OnJoinSyncStart, context);
    99102
    100103    AddTransition(NCS_JOIN_SYNCING, (uint)NMT_CHAT, NCS_JOIN_SYNCING, (void*)&OnChat, context);
    101104    AddTransition(NCS_JOIN_SYNCING, (uint)NMT_GAME_SETUP, NCS_JOIN_SYNCING, (void*)&OnGameSetup, context);
    CNetClient::CNetClient(CGame* game) :  
    110113    AddTransition(NCS_LOADING, (uint)NMT_PLAYER_ASSIGNMENT, NCS_LOADING, (void*)&OnPlayerAssignment, context);
    111114    AddTransition(NCS_LOADING, (uint)NMT_LOADED_GAME, NCS_INGAME, (void*)&OnLoadedGame, context);
    112115
    113116    AddTransition(NCS_INGAME, (uint)NMT_REJOINED, NCS_INGAME, (void*)&OnRejoined, context);
    114117    AddTransition(NCS_INGAME, (uint)NMT_KICKED, NCS_INGAME, (void*)&OnKicked, context);
     118    AddTransition(NCS_INGAME, (uint)NMT_CLIENT_TIMEOUT, NCS_INGAME, (void*)&OnClientTimeout, context);
     119    AddTransition(NCS_INGAME, (uint)NMT_CLIENT_PERFORMANCE, NCS_INGAME, (void*)&OnClientPerformance, context);
    115120    AddTransition(NCS_INGAME, (uint)NMT_CHAT, NCS_INGAME, (void*)&OnChat, context);
    116121    AddTransition(NCS_INGAME, (uint)NMT_GAME_SETUP, NCS_INGAME, (void*)&OnGameSetup, context);
    117122    AddTransition(NCS_INGAME, (uint)NMT_PLAYER_ASSIGNMENT, NCS_INGAME, (void*)&OnPlayerAssignment, context);
    118123    AddTransition(NCS_INGAME, (uint)NMT_SIMULATION_COMMAND, NCS_INGAME, (void*)&OnInGame, context);
    119124    AddTransition(NCS_INGAME, (uint)NMT_SYNC_ERROR, NCS_INGAME, (void*)&OnInGame, context);
    void CNetClient::DestroyConnection()  
    168173    SAFE_DELETE(m_Session);
    169174}
    170175
    171176void CNetClient::Poll()
    172177{
    173     if (m_Session)
    174         m_Session->Poll();
     178    if (!m_Session)
     179        return;
     180
     181    CheckServerConnection();
     182    m_Session->Poll();
     183}
     184
     185void CNetClient::CheckServerConnection()
     186{
     187    // Send net-warnings at most once per second
     188    u32 now = std::time(nullptr);
     189    if (now <= m_LastConnectionCheck)
     190        return;
     191    m_LastConnectionCheck = now;
     192
     193    JSContext* cx = GetScriptInterface().GetContext();
     194
     195    // Report if we are losing the connection to the server
     196    u32 lastReceived = m_Session->GetLastReceivedTime() / 1000;
     197    if (lastReceived > NETWORK_WARNING_TIMEOUT)
     198    {
     199        JS::RootedValue msg(cx);
     200        GetScriptInterface().Eval("({ 'type':'netwarn', 'warntype': 'server-timeout' })", &msg);
     201        GetScriptInterface().SetProperty(msg, "lastReceivedTime", lastReceived);
     202        PushGuiMessage(msg);
     203        return;
     204    }
     205
     206    // Report if we have a bad ping to the server
     207    u32 meanRTT = m_Session->GetMeanRTT();
     208    if (m_ClientTurnManager && meanRTT >  m_ClientTurnManager->GetCurrentTurnLength() * NETWORK_WARNING_LATENCY)
     209    {
     210        JS::RootedValue msg(cx);
     211        GetScriptInterface().Eval("({ 'type':'netwarn', 'warntype': 'server-latency' })", &msg);
     212        GetScriptInterface().SetProperty(msg, "meanRTT", meanRTT);
     213        PushGuiMessage(msg);
     214    }
    175215}
    176216
    177217void CNetClient::Flush()
    178218{
    179219    if (m_Session)
    bool CNetClient::OnKicked(void *context,  
    625665    client->PushGuiMessage(msg);
    626666
    627667    return true;
    628668}
    629669
     670bool CNetClient::OnClientTimeout(void *context, CFsmEvent* event)
     671{
     672    ENSURE(event->GetType() == (uint)NMT_CLIENT_TIMEOUT);
     673
     674    CNetClient* client = (CNetClient*)context;
     675    JSContext* cx = client->GetScriptInterface().GetContext();
     676
     677    CClientTimeoutMessage* message = (CClientTimeoutMessage*)event->GetParamRef();
     678    JS::RootedValue msg(cx);
     679
     680    client->GetScriptInterface().Eval("({'type':'netwarn', 'warntype': 'client-timeout' })", &msg);
     681    client->GetScriptInterface().SetProperty(msg, "guid", std::string(message->m_GUID));
     682    client->GetScriptInterface().SetProperty(msg, "lastReceivedTime", message->m_LastReceivedTime);
     683    client->PushGuiMessage(msg);
     684
     685    return true;
     686}
     687
     688bool CNetClient::OnClientPerformance(void *context, CFsmEvent* event)
     689{
     690    ENSURE(event->GetType() == (uint)NMT_CLIENT_PERFORMANCE);
     691
     692    CNetClient* client = (CNetClient*)context;
     693    JSContext* cx = client->GetScriptInterface().GetContext();
     694
     695    CClientPerformanceMessage* message = (CClientPerformanceMessage*)event->GetParamRef();
     696    std::vector<CClientPerformanceMessage::S_m_Clients> clients = message->m_Clients;
     697
     698    for (size_t i = 0; i < clients.size(); ++i)
     699    {
     700        u32 meanRTT = clients[i].m_MeanRTT;
     701
     702        if (!client->m_ClientTurnManager || meanRTT <  client->m_ClientTurnManager->GetCurrentTurnLength() * NETWORK_WARNING_LATENCY)
     703            continue;
     704
     705        JS::RootedValue msg(cx);
     706        client->GetScriptInterface().Eval("({'type':'netwarn', 'warntype': 'client-latency' })", &msg);
     707        client->GetScriptInterface().SetProperty(msg, "guid", clients[i].m_GUID);
     708        client->GetScriptInterface().SetProperty(msg, "meanRTT", meanRTT);
     709        client->PushGuiMessage(msg);
     710    }
     711
     712    return true;
     713}
     714
    630715bool CNetClient::OnLoadedGame(void* context, CFsmEvent* event)
    631716{
    632717    ENSURE(event->GetType() == (uint)NMT_LOADED_GAME);
    633718
    634719    CNetClient* client = (CNetClient*)context;
  • source/network/NetClient.h

     
    1 /* Copyright (C) 2015 Wildfire Games.
     1/* Copyright (C) 2016 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    55 * it under the terms of the GNU General Public License as published by
    66 * the Free Software Foundation, either version 2 of the License, or
    public:  
    107107     * This must be called frequently (i.e. once per frame).
    108108     */
    109109    void Poll();
    110110
    111111    /**
     112     * Sends a GUI message if the connection is being lost or has bad latency.
     113     */
     114    void CheckServerConnection();
     115
     116    /**
    112117     * Flush any queued outgoing network messages.
    113118     * This should be called soon after sending a group of messages that may be batched together.
    114119     */
    115120    void Flush();
    116121
    private:  
    200205    static bool OnGameStart(void* context, CFsmEvent* event);
    201206    static bool OnJoinSyncStart(void* context, CFsmEvent* event);
    202207    static bool OnJoinSyncEndCommandBatch(void* context, CFsmEvent* event);
    203208    static bool OnRejoined(void* context, CFsmEvent* event);
    204209    static bool OnKicked(void* context, CFsmEvent* event);
     210    static bool OnClientTimeout(void* context, CFsmEvent* event);
     211    static bool OnClientPerformance(void* context, CFsmEvent* event);
    205212    static bool OnLoadedGame(void* context, CFsmEvent* event);
    206213
    207214    /**
    208215     * Take ownership of a session object, and use it for all network communication.
    209216     */
    private:  
    238245    /// Queue of messages for GuiPoll
    239246    std::deque<JS::Heap<JS::Value> > m_GuiMessageQueue;
    240247
    241248    /// Serialized game state received when joining an in-progress game
    242249    std::string m_JoinSyncBuffer;
     250
     251    /// Time when the server was last checked for timeout
     252    u32 m_LastConnectionCheck;
    243253};
    244254
    245255/// Global network client for the standard game
    246256extern CNetClient *g_NetClient;
    247257
  • source/network/NetMessage.cpp

     
    1 /* Copyright (C) 2015 Wildfire Games.
     1/* Copyright (C) 2016 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    55 * it under the terms of the GNU General Public License as published by
    66 * the Free Software Foundation, either version 2 of the License, or
    CNetMessage* CNetMessageFactory::CreateM  
    137137
    138138    case NMT_KICKED:
    139139        pNewMessage = new CKickedMessage;
    140140        break;
    141141
     142    case NMT_CLIENT_TIMEOUT:
     143        pNewMessage = new CClientTimeoutMessage;
     144        break;
     145
     146    case NMT_CLIENT_PERFORMANCE:
     147        pNewMessage = new CClientPerformanceMessage;
     148        break;
     149
    142150    case NMT_LOADED_GAME:
    143151        pNewMessage = new CLoadedGameMessage;
    144152        break;
    145153
    146154    case NMT_SERVER_HANDSHAKE:
  • source/network/NetMessages.h

     
    1 /* Copyright (C) 2015 Wildfire Games.
     1/* Copyright (C) 2016 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    55 * it under the terms of the GNU General Public License as published by
    66 * the Free Software Foundation, either version 2 of the License, or
     
    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             0x01010008      // Arbitrary protocol
     31#define PS_PROTOCOL_VERSION             0x01010009      // 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  
    5858    NMT_JOIN_SYNC_START,
    5959
    6060    NMT_REJOINED,
    6161    NMT_KICKED,
    6262
     63    NMT_CLIENT_TIMEOUT,
     64    NMT_CLIENT_PERFORMANCE,
     65
    6366    NMT_LOADED_GAME,
    6467    NMT_GAME_START,
    6568    NMT_END_COMMAND_BATCH,
    6669    NMT_SYNC_CHECK, // OOS-detection hash checking
    6770    NMT_SYNC_ERROR, // OOS-detection error
    END_NMT_CLASS()  
    165168START_NMT_CLASS_(Kicked, NMT_KICKED)
    166169    NMT_FIELD(CStrW, m_Name)
    167170    NMT_FIELD_INT(m_Ban, u8, 1)
    168171END_NMT_CLASS()
    169172
     173START_NMT_CLASS_(ClientTimeout, NMT_CLIENT_TIMEOUT)
     174    NMT_FIELD(CStr8, m_GUID)
     175    NMT_FIELD_INT(m_LastReceivedTime, u16, 2)
     176END_NMT_CLASS()
     177
     178START_NMT_CLASS_(ClientPerformance, NMT_CLIENT_PERFORMANCE)
     179    NMT_START_ARRAY(m_Clients)
     180        NMT_FIELD(CStr8, m_GUID)
     181        NMT_FIELD_INT(m_MeanRTT, u16, 2)
     182    NMT_END_ARRAY()
     183END_NMT_CLASS()
     184
    170185START_NMT_CLASS_(LoadedGame, NMT_LOADED_GAME)
    171186    NMT_FIELD_INT(m_CurrentTurn, u32, 4)
    172187END_NMT_CLASS()
    173188
    174189START_NMT_CLASS_(GameStart, NMT_GAME_START)
  • source/network/NetServer.cpp

    private:  
    119119
    120120CNetServerWorker::CNetServerWorker(int autostartPlayers) :
    121121    m_AutostartPlayers(autostartPlayers),
    122122    m_Shutdown(false),
    123123    m_ScriptInterface(NULL),
    124     m_NextHostID(1), m_Host(NULL), m_HostGUID(), m_Stats(NULL)
     124    m_NextHostID(1), m_Host(NULL), m_HostGUID(), m_Stats(NULL),
     125    m_LastConnectionCheck(0)
    125126{
    126127    m_State = SERVER_STATE_UNCONNECTED;
    127128
    128129    m_ServerTurnManager = NULL;
    129130
    bool CNetServerWorker::RunStep()  
    449450
    450451    // Perform file transfers
    451452    for (size_t i = 0; i < m_Sessions.size(); ++i)
    452453        m_Sessions[i]->GetFileTransferer().Poll();
    453454
     455    CheckClientConnections();
     456
    454457    // Process network events:
    455458
    456459    ENetEvent event;
    457     int status = enet_host_service(m_Host, &event, HOST_SERVICE_TIMEOUT);
     460    int status = enet_host_service(m_Host, &event, NETWORK_WARNING_TIMEOUT);
    458461    if (status < 0)
    459462    {
    460463        LOGERROR("CNetServerWorker: enet_host_service failed (%d)", status);
    461464        // TODO: notify game that the server has shut down
    462465        return false;
    bool CNetServerWorker::RunStep()  
    547550    }
    548551
    549552    return true;
    550553}
    551554
     555void CNetServerWorker::CheckClientConnections()
     556{
     557    // Send messages at most once per second
     558    u32 now = std::time(nullptr);
     559    if (now <= m_LastConnectionCheck)
     560        return;
     561
     562    m_LastConnectionCheck = now;
     563
     564    for (size_t i = 0; i < m_Sessions.size(); ++i)
     565    {
     566        CNetMessage* message = nullptr;
     567
     568        // Report if we didn't hear from the client since few seconds
     569        u32 lastReceived = m_Sessions[i]->GetLastReceivedTime() / 1000;
     570        if (lastReceived > NETWORK_WARNING_TIMEOUT)
     571        {
     572            CClientTimeoutMessage* msg = new CClientTimeoutMessage();
     573            msg->m_GUID = m_Sessions[i]->GetGUID();
     574            msg->m_LastReceivedTime = lastReceived;
     575            message = msg;
     576        }
     577
     578        // Else report if the client has bad ping
     579        u32 meanRTT = m_Sessions[i]->GetMeanRTT();
     580        if (!message && m_ServerTurnManager &&  meanRTT > m_ServerTurnManager->GetCurrentTurnLength() * NETWORK_WARNING_LATENCY)
     581        {
     582            CClientPerformanceMessage* msg = new CClientPerformanceMessage();
     583            CClientPerformanceMessage::S_m_Clients client;
     584            client.m_GUID = m_Sessions[i]->GetGUID();
     585            client.m_MeanRTT = meanRTT;
     586            msg->m_Clients.push_back(client);
     587            message = msg;
     588        }
     589
     590        // Send to all users except the affected one
     591        if (message)
     592            for (size_t j = 0; j < m_Sessions.size(); ++j)
     593                if (i != j)
     594                    m_Sessions[j]->SendMessage(message);
     595    }
     596}
     597
    552598void CNetServerWorker::HandleMessageReceive(const CNetMessage* message, CNetServerSession* session)
    553599{
    554600    // Handle non-FSM messages first
    555601    Status status = session->GetFileTransferer().HandleMessageReceive(message);
    556602    if (status != INFO::SKIPPED)
  • source/network/NetServer.h

     
    1 /* Copyright (C) 2015 Wildfire Games.
     1/* Copyright (C) 2016 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    55 * it under the terms of the GNU General Public License as published by
    66 * the Free Software Foundation, either version 2 of the License, or
    private:  
    279279
    280280    void ConstructPlayerAssignmentMessage(CPlayerAssignmentMessage& message);
    281281
    282282    void HandleMessageReceive(const CNetMessage* message, CNetServerSession* session);
    283283
     284    /**
     285     * Send a network warning if the connection to a client is being lost or has bad latency.
     286     */
     287    void CheckClientConnections();
    284288
    285289    /**
    286290     * Internal script context for (de)serializing script messages,
    287291     * and for storing game attributes.
    288292     * (TODO: we shouldn't bother deserializing (except for debug printing of messages),
    private:  
    329333     * The latest copy of the simulation state, received from an existing
    330334     * client when a new client has asked to rejoin the game.
    331335     */
    332336    std::string m_JoinSyncFile;
    333337
     338    /**
     339     *  Time when the clients connections were last checked for timeouts and latency.
     340     */
     341    u32 m_LastConnectionCheck;
     342
    334343private:
    335344    // Thread-related stuff:
    336345
    337346#if CONFIG2_MINIUPNPC
    338347    /**
  • source/network/NetSession.cpp

    bool CNetClientSession::SendMessage(cons  
    166166    ENSURE(m_Host && m_Server);
    167167
    168168    return CNetHost::SendMessage(message, m_Server, "server");
    169169}
    170170
     171u32 CNetClientSession::GetLastReceivedTime() const
     172{
     173    return m_Server ? enet_time_get() - m_Server->lastReceiveTime : 0;
     174}
     175
     176u32 CNetClientSession::GetMeanRTT() const
     177{
     178    return m_Server ? m_Server->roundTripTime : 0;
     179}
     180
    171181
    172182
    173183CNetServerSession::CNetServerSession(CNetServerWorker& server, ENetPeer* peer) :
    174184    m_Server(server), m_FileTransferer(this), m_Peer(peer)
    175185{
    CStr CNetServerSession::GetIPAddress() c  
    182192        LOGMESSAGE("Could not get IP address of a client!");
    183193
    184194    return ipAddress;
    185195}
    186196
     197u32 CNetServerSession::GetLastReceivedTime() const
     198{
     199    return m_Peer ? enet_time_get() - m_Peer->lastReceiveTime : 0;
     200}
     201
     202u32 CNetServerSession::GetMeanRTT() const
     203{
     204    return m_Peer ? m_Peer->roundTripTime : 0;
     205}
     206
    187207void CNetServerSession::Disconnect(u32 reason)
    188208{
    189209    Update((uint)NMT_CONNECTION_LOST, NULL);
    190210
    191211    enet_peer_disconnect(m_Peer, reason);
  • source/network/NetSession.h

     
    1 /* Copyright (C) 2015 Wildfire Games.
     1/* Copyright (C) 2016 Wildfire Games.
    22 * This file is part of 0 A.D.
    33 *
    44 * 0 A.D. is free software: you can redistribute it and/or modify
    55 * it under the terms of the GNU General Public License as published by
    66 * the Free Software Foundation, either version 2 of the License, or
     
    2222#include "network/NetFileTransfer.h"
    2323#include "network/NetHost.h"
    2424#include "ps/CStr.h"
    2525#include "scriptinterface/ScriptVal.h"
    2626
     27/**
     28 * Report the peer if we didn't receive a packet after this number of seconds.
     29 */
     30static const int NETWORK_WARNING_TIMEOUT = 4;
     31
     32/**
     33 * Report the peer if the mean RTT takes longer than this multiple of a turnlength.
     34 */
     35static const float NETWORK_WARNING_LATENCY = 1.1f;
     36
    2737class CNetClient;
    2838class CNetServerWorker;
    2939
    3040class CNetStatsTable;
    3141
    public:  
    8191    /**
    8292     * Send a message to the server.
    8393     */
    8494    virtual bool SendMessage(const CNetMessage* message);
    8595
     96    /**
     97     * Number of milliseconds since the most recent packet of the server was received.
     98     */
     99    u32 GetLastReceivedTime() const;
     100
     101    /**
     102     * Average round trip time to the server.
     103     */
     104    u32 GetMeanRTT() const;
     105
    86106    CNetFileTransferer& GetFileTransferer() { return m_FileTransferer; }
    87107
    88108private:
    89109    CNetClient& m_Client;
    90110
    public:  
    123143    void SetHostID(u32 id) { m_HostID = id; }
    124144
    125145    CStr GetIPAddress() const;
    126146
    127147    /**
     148     * Number of milliseconds since the latest packet of that client was received.
     149     */
     150    u32 GetLastReceivedTime() const;
     151
     152    /**
     153     * Average round trip time to the client.
     154     */
     155    u32 GetMeanRTT() const;
     156
     157    /**
    128158     * Sends a disconnection notification to the client,
    129159     * and sends a NMT_CONNECTION_LOST message to the session FSM.
    130160     * The server will receive a disconnection notification after a while.
    131161     * The server will not receive any further messages sent via this session.
    132162     */
  • source/network/NetTurnManager.h

    public:  
    139139    void RewindTimeWarp();
    140140
    141141    void QuickSave();
    142142    void QuickLoad();
    143143
    144     u32 GetCurrentTurn() { return m_CurrentTurn; }
     144    u32 GetCurrentTurn() const { return m_CurrentTurn; }
     145    u32 GetCurrentTurnLength() const { return m_TurnLength; };
    145146
    146147protected:
    147148    /**
    148149     * Store a command to be executed at a given turn.
    149150     */
    public:  
    318319     * Returns the turn length that was used for the given turn.
    319320     * Requires turn <= GetReadyTurn().
    320321     */
    321322    u32 GetSavedTurnLength(u32 turn);
    322323
     324    /**
     325     * Returns the used turn length in milliseconds.
     326     */
     327    u32 GetCurrentTurnLength() const { return m_TurnLength; };
     328
    323329protected:
    324330    void CheckClientsReady();
    325331
    326332    /// The latest turn for which we have received all commands from all clients
    327333    u32 m_ReadyTurn;