This Trac instance is not used for development anymore!

We migrated our development workflow to git and Gitea.
To test the future redirection, replace trac by ariadne in the page URL.

Changeset 21842 for ps


Ignore:
Timestamp:
06/07/18 00:09:38 (7 years ago)
Author:
elexis
Message:

Prevent players from disconnecting during the loading screen by increasing the timeout tolerance to 60 seconds for that period, fixes #5163.

The NetClient runs in the main thread, so any part of the loading screen consuming several seconds makes that client timeout.
This is a workaround because threading the NetClient would have prevent these timeouts, refs #3700.
Coutnerintuitively, since enet timeout tolerance is proportional to the latency, the better the connection of the player, the more likely it was to drop on gamestart.

This problem became very frequent in Alpha 23, at least due to the Aura bugfix rP21785, AIInterface being particularly slow and that not having been disabled yet in the loading screen resulting in additional 10 second freezes during the loading screen, even on empty maps, refs #5200, rP21838.

Differential Revision: https://code.wildfiregames.com/D1513
Based on patch by: causative

Location:
ps/trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • ps/trunk/binaries/data/config/default.cfg

    r21759 r21842  
    446446lateobservers = everyone          ; Allow observers to join the game after it started. Possible values: everyone, buddies, disabled.
    447447observerlimit = 8                 ; Prevent further observer joins in running games if this limit is reached
     448gamestarttimeout = 60000          ; Don't disconnect clients timing out in the loading screen and rejoin process before exceeding this timeout.
    448449
    449450[overlay]
  • ps/trunk/source/network/NetClient.cpp

    r21837 r21842  
    673673}
    674674
     675// This is called either when the host clicks the StartGame button or
     676// if this client rejoins and finishes the download of the simstate.
    675677bool CNetClient::OnGameStart(void* context, CFsmEvent* event)
    676678{
     
    680682    JSContext* cx = client->GetScriptInterface().GetContext();
    681683    JSAutoRequest rq(cx);
     684
     685    client->m_Session->SetLongTimeout(true);
    682686
    683687    // Find the player assigned to our GUID
     
    821825    CClientsLoadingMessage* message = (CClientsLoadingMessage*)event->GetParamRef();
    822826
     827    CNetClient* client = (CNetClient*)context;
     828    JSContext* cx = client->GetScriptInterface().GetContext();
     829    JSAutoRequest rq(cx);
     830
     831    bool finished = true;
    823832    std::vector<CStr> guids;
    824833    guids.reserve(message->m_Clients.size());
    825     for (const CClientsLoadingMessage::S_m_Clients& client : message->m_Clients)
    826         guids.push_back(client.m_GUID);
    827 
    828     CNetClient* client = (CNetClient*)context;
    829     JSContext* cx = client->GetScriptInterface().GetContext();
    830     JSAutoRequest rq(cx);
     834    for (const CClientsLoadingMessage::S_m_Clients& mClient : message->m_Clients)
     835    {
     836        if (client->m_GUID == mClient.m_GUID)
     837            finished = false;
     838
     839        guids.push_back(mClient.m_GUID);
     840    }
     841
     842    // Disable the timeout here after processing the enet message, so as to ensure that the connection isn't currently
     843    // timing out (as it is when just leaving the loading screen in LoadFinished).
     844    if (finished)
     845        client->m_Session->SetLongTimeout(false);
    831846
    832847    JS::RootedValue msg(cx);
     
    875890    if (client->m_Rejoin)
    876891        client->SendRejoinedMessage();
     892
     893    // The last client to leave the loading screen didn't receive the CClientsLoadingMessage, so disable here.
     894    client->m_Session->SetLongTimeout(false);
    877895
    878896    return true;
  • ps/trunk/source/network/NetServer.cpp

    r21836 r21842  
    10961096        // the most efficient client to request a copy from
    10971097        CNetServerSession* sourceSession = server.m_Sessions.at(0);
     1098
     1099        session->SetLongTimeout(true);
     1100
    10981101        sourceSession->GetFileTransferer().StartTask(
    10991102            shared_ptr<CNetFileReceiveTask>(new CNetFileReceiveTask_ServerRejoin(server, newHostID))
     
    12641267    CNetServerWorker& server = loadedSession->GetServer();
    12651268
     1269    loadedSession->SetLongTimeout(false);
     1270
    12661271    // We're in the loading state, so wait until every client has loaded
    12671272    // before starting the game
     
    13611366    }
    13621367
     1368    session->SetLongTimeout(false);
     1369
    13631370    return true;
    13641371}
     
    14511458    m_ServerTurnManager = new CNetServerTurnManager(*this);
    14521459
    1453     for (const CNetServerSession* session : m_Sessions)
     1460    for (CNetServerSession* session : m_Sessions)
     1461    {
    14541462        m_ServerTurnManager->InitialiseClient(session->GetHostID(), 0); // TODO: only for non-observers
     1463        session->SetLongTimeout(true);
     1464    }
    14551465
    14561466    m_State = SERVER_STATE_LOADING;
  • ps/trunk/source/network/NetSession.cpp

    r21841 r21842  
    2424#include "lib/external_libraries/enet.h"
    2525#include "ps/CLogger.h"
     26#include "ps/ConfigDB.h"
    2627#include "ps/Profile.h"
    2728#include "scriptinterface/ScriptInterface.h"
     
    3233
    3334static const int CHANNEL_COUNT = 1;
     35
     36// Only disable long timeouts after a packet from the remote enet peer has been processed.
     37// Otherwise a long timeout can still be in progress when disabling it here.
     38void SetEnetLongTimeout(ENetPeer* peer, bool isLocalClient, bool enabled)
     39{
     40#if (ENET_VERSION >= ENET_VERSION_CREATE(1, 3, 4))
     41    if (!peer || isLocalClient)
     42        return;
     43
     44    if (enabled)
     45    {
     46        u32 timeout;
     47        CFG_GET_VAL("network.gamestarttimeout", timeout);
     48        enet_peer_timeout(peer, 0, timeout, timeout);
     49    }
     50    else
     51        enet_peer_timeout(peer, 0, 0, 0);
     52#endif
     53}
    3454
    3555CNetClientSession::CNetClientSession(CNetClient& client) :
     
    202222}
    203223
     224void CNetClientSession::SetLongTimeout(bool enabled)
     225{
     226    SetEnetLongTimeout(m_Server, m_IsLocalClient, enabled);
     227}
     228
    204229CNetServerSession::CNetServerSession(CNetServerWorker& server, ENetPeer* peer) :
    205230    m_Server(server), m_FileTransferer(this), m_Peer(peer), m_IsLocalClient(false), m_HostID(0), m_GUID(), m_UserName()
     
    262287#endif
    263288}
     289
     290void CNetServerSession::SetLongTimeout(bool enabled)
     291{
     292    SetEnetLongTimeout(m_Peer, m_IsLocalClient, enabled);
     293}
  • ps/trunk/source/network/NetSession.h

    r21841 r21842  
    106106    u32 GetMeanRTT() const;
    107107
     108    /**
     109     * Allows increasing the timeout to prevent drops during an expensive operation,
     110     * and decreasing it back to normal afterwards.
     111     */
     112    void SetLongTimeout(bool longTimeout);
     113
    108114    CNetFileTransferer& GetFileTransferer() { return m_FileTransferer; }
    109115
     
    185191
    186192    /**
     193     * Allows increasing the timeout to prevent drops during an expensive operation,
     194     * and decreasing it back to normal afterwards.
     195     */
     196    void SetLongTimeout(bool longTimeout);
     197
     198    /**
    187199     * Send a message to the client.
    188200     */
Note: See TracChangeset for help on using the changeset viewer.