1
0
forked from 0ad/0ad

Implement network-warnings, fixes #3264.

Shows a notification if the local client or other players connections
timeout or have bad latency.

This was SVN commit r17730.
This commit is contained in:
elexis 2016-02-04 17:14:46 +00:00
parent 4d41bd9622
commit 22f5b00fce
21 changed files with 468 additions and 19 deletions

View File

@ -352,6 +352,7 @@ enabledmods = "mod public"
[overlay]
fps = "false" ; Show frames per second in top right corner
realtime = "false" ; Show current system time in top right corner
netwarnings = "true" ; Show warnings if the network connection is bad
[profiler2]
autoenable = false ; Enable HTTP server output at startup (default off for security/performance)

View File

@ -1,7 +1,6 @@
/*
DESCRIPTION : Contains global GUI functions, which will later be accessible from every GUI script/file.
NOTES : So far, only the message box-related functions are implemented.
*/
/**
* Contains global GUI functions accessible from every GUI script/file.
*/
// *******************************************
// messageBox
@ -139,4 +138,42 @@ function updateCounters()
var dataCounter = Engine.GetGUIObjectByName("dataCounter");
dataCounter.caption = caption;
dataCounter.size = sprintf("100%%-100 40 100%%-5 %(bottom)s", { bottom: 40 + 14 * linesCount });
dataCounter.hidden = linesCount == 0;
}
/**
* Update the overlay with the most recent network warning of each client.
*/
function displayGamestateNotifications()
{
let messages = [];
let maxTextWidth = 0;
// TODO: Players who paused the game should be added here
// Add network warnings
if (Engine.ConfigDB_GetValue("user", "overlay.netwarnings") == "true")
{
let netwarnings = getNetworkWarnings();
messages = messages.concat(netwarnings.messages);
maxTextWidth = Math.max(maxTextWidth, netwarnings.maxTextWidth);
}
// Resize textbox
let width = maxTextWidth + 20;
let height = 14 * messages.length;
// Position left of the dataCounter
let top = "40";
let right = Engine.GetGUIObjectByName("dataCounter").hidden ? "100%-15" : "100%-110";
let bottom = top + "+" + height;
let left = right + "-" + width;
let gameStateNotifications = Engine.GetGUIObjectByName("gameStateNotifications");
gameStateNotifications.caption = messages.join("\n");
gameStateNotifications.hidden = !messages.length;
gameStateNotifications.size = left + " " + top + " " + right + " " + bottom;
setTimeout(displayGamestateNotifications, 1000);
}

View File

@ -12,6 +12,24 @@
<object>
<!--
==========================================
- GAMESTATE NOTIFICATIONS
==========================================
-->
<object name="gameStateNotifications"
type="text"
ghost="true"
z="199"
size="100%-110 40 100%-110 40"
font="mono-stroke-10"
textcolor="255 219 77"
text_align="center"
text_valign="top"
sprite="color: 0 0 0 100"
>
</object>
<!--
==========================================
- FPS & REAL TIME & GAME TIME COUNTER

View File

@ -1,3 +1,41 @@
/**
* Number of milliseconds to display network warnings.
*/
const g_NetworkWarningTimeout = 3000;
/**
* Currently displayed network warnings. At most one message per user.
*/
var g_NetworkWarnings = {};
/**
* Message-types to be displayed.
*/
var g_NetworkWarningTexts = {
"server-timeout": (msg, username) =>
sprintf(translate("Losing connection to server (%(seconds)s)"), {
"seconds": Math.ceil(msg.lastReceivedTime / 1000)
}),
"client-timeout": (msg, username) =>
sprintf(translate("%(player)s losing connection (%(seconds)s)"), {
"player": username,
"seconds": Math.ceil(msg.lastReceivedTime / 1000)
}),
"server-latency": (msg, username) =>
sprintf(translate("Bad connection to server (%(milliseconds)sms)"), {
"milliseconds": msg.meanRTT
}),
"client-latency": (msg, username) =>
sprintf(translate("Bad connection to %(player)s (%(milliseconds)sms)"), {
"player": username,
"milliseconds": msg.meanRTT
})
};
var g_NetworkCommands = {
"/kick": argument => kickPlayer(argument, false),
"/ban": argument => kickPlayer(argument, true),
@ -94,3 +132,69 @@ function executeNetworkCommand(input)
return !!g_NetworkCommands[command];
}
/**
* Remember this warning for a few seconds.
* Overwrite previous warnings for this user.
*
* @param msg - GUI message sent by NetServer or NetClient
*/
function addNetworkWarning(msg)
{
if (!g_NetworkWarningTexts[msg.warntype])
{
warn("Unknown network warning type received: " + uneval(msg));
return;
}
if (Engine.ConfigDB_GetValue("user", "overlay.netwarnings") != "true")
return;
g_NetworkWarnings[msg.guid || "server"] = {
"added": Date.now(),
"msg": msg
};
}
/**
* Colorizes and concatenates all network warnings.
* Returns text and textWidth.
*/
function getNetworkWarnings()
{
// Remove outdated messages
for (let guid in g_NetworkWarnings)
{
if (Date.now() > g_NetworkWarnings[guid].added + g_NetworkWarningTimeout)
delete g_NetworkWarnings[guid];
if (guid != "server" && !g_PlayerAssignments[guid])
delete g_NetworkWarnings[guid];
}
// Show local messages first
let guids = Object.keys(g_NetworkWarnings).sort(guid => guid != "server");
let font = Engine.GetGUIObjectByName("gameStateNotifications").font;
let messages = [];
let maxTextWidth = 0;
for (let guid of guids)
{
let msg = g_NetworkWarnings[guid].msg;
// Add formatted text
messages.push(g_NetworkWarningTexts[msg.warntype](msg, colorizePlayernameByGUID(guid)));
// Add width of unformatted text
let username = guid != "server" && g_PlayerAssignments[guid].name;
let textWidth = Engine.GetTextWidth(font, g_NetworkWarningTexts[msg.warntype](msg, username));
maxTextWidth = Math.max(textWidth, maxTextWidth);
}
return {
"messages": messages,
"maxTextWidth": maxTextWidth
};
}

View File

@ -28,6 +28,7 @@ const g_MapPath = {
*/
const g_NetMessageTypes = {
"netstatus": msg => handleNetStatusMessage(msg),
"netwarn": msg => addNetworkWarning(msg),
"gamesetup": msg => handleGamesetupMessage(msg),
"players": msg => handlePlayerAssignmentMessage(msg),
"ready": msg => handleReadyMessage(msg),
@ -211,6 +212,8 @@ function init(attribs)
g_DefaultPlayerData.shift();
for (let i in g_DefaultPlayerData)
g_DefaultPlayerData[i].Civ = "random";
setTimeout(displayGamestateNotifications, 1000);
}
/**
@ -946,6 +949,8 @@ function onTick()
error("Unrecognised net message type " + message.type);
}
}
updateTimers();
}
/**

View File

@ -8,6 +8,7 @@
<script file="gui/common/functions_utility.js"/>
<script file="gui/common/network.js"/>
<script file="gui/common/settings.js"/>
<script file="gui/common/timer.js"/>
<!-- After settings.js, which defines g_Settings -->
<script file="gui/gamesetup/gamesetup.js"/>

View File

@ -126,7 +126,9 @@ function pollAndHandleNetworkClient()
break;
case "chat":
// Ignore, since we have nowhere to display chat messages
break;
case "netwarn":
break;
default:
@ -169,6 +171,10 @@ function pollAndHandleNetworkClient()
break;
}
break;
case "netwarn":
break;
default:
error(sprintf("Unrecognised net message type %(messageType)s", { messageType: message.type }));
break;

View File

@ -25,6 +25,12 @@
"tooltip": "Show detailed tooltips for trainable units in unit-producing buildings.",
"parameters": { "config": "showdetailedtooltips" }
},
{
"type": "boolean",
"label": "Network Warnings",
"tooltip": "Show which player has a bad connection in multiplayer games.",
"parameters": { "config": "overlay.netwarnings" }
},
{
"type": "boolean",
"label": "FPS Overlay",

View File

@ -29,6 +29,7 @@ var g_ChatTimers = [];
*/
var g_NetMessageTypes = {
"netstatus": msg => handleNetStatusMessage(msg),
"netwarn": msg => addNetworkWarning(msg),
"players": msg => handlePlayerAssignmentsMessage(msg),
"rejoined": msg => addChatMessage({ "type": "rejoined", "guid": msg.guid }),
"kicked": msg => addChatMessage({ "type": "system", "text": sprintf(translate("%(username)s has been kicked"), { "username": msg.username }) }),

View File

@ -269,6 +269,8 @@ function init(initData, hotloadData)
onSimulationUpdate();
setTimeout(displayGamestateNotifications, 1000);
// Report the performance after 5 seconds (when we're still near
// the initial camera view) and a minute (when the profiler will
// have settled down if framerates as very low), to give some

View File

@ -20,6 +20,7 @@
#include "scriptinterface/ScriptInterface.h"
#include "graphics/Camera.h"
#include "graphics/FontMetrics.h"
#include "graphics/GameView.h"
#include "graphics/MapReader.h"
#include "graphics/scripting/JSInterface_GameView.h"
@ -875,6 +876,16 @@ CParamNode GetTemplate(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const std
return g_GUI->GetTemplate(templateName);
}
int GetTextWidth(ScriptInterface::CxPrivate* UNUSED(pCxPrivate), const CStr& fontName, const CStrW& text)
{
int width = 0;
int height = 0;
CStrIntern _fontName(fontName);
CFontMetrics fontMetrics(_fontName);
fontMetrics.CalculateStringSize(text.c_str(), width, height);
return width;
}
//-----------------------------------------------------------------------------
// Timer
//-----------------------------------------------------------------------------
@ -1049,6 +1060,7 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
scriptInterface.RegisterFunction<void, std::wstring, JS::HandleValue, &WriteJSONFile>("WriteJSONFile");
scriptInterface.RegisterFunction<bool, std::string, &TemplateExists>("TemplateExists");
scriptInterface.RegisterFunction<CParamNode, std::string, &GetTemplate>("GetTemplate");
scriptInterface.RegisterFunction<int, CStr, CStrW, &GetTextWidth>("GetTextWidth");
// User report functions
scriptInterface.RegisterFunction<bool, &IsUserReportEnabled>("IsUserReportEnabled");

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2015 Wildfire Games.
/* Copyright (C) 2016 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -70,7 +70,8 @@ CNetClient::CNetClient(CGame* game) :
m_Session(NULL),
m_UserName(L"anonymous"),
m_GUID(ps_generate_guid()), m_HostID((u32)-1), m_ClientTurnManager(NULL), m_Game(game),
m_GameAttributes(game->GetSimulation2()->GetScriptInterface().GetContext())
m_GameAttributes(game->GetSimulation2()->GetScriptInterface().GetContext()),
m_LastConnectionCheck(0)
{
m_Game->SetTurnManager(NULL); // delete the old local turn manager so we don't accidentally use it
@ -94,12 +95,16 @@ CNetClient::CNetClient(CGame* game) :
AddTransition(NCS_PREGAME, (uint)NMT_GAME_SETUP, NCS_PREGAME, (void*)&OnGameSetup, context);
AddTransition(NCS_PREGAME, (uint)NMT_PLAYER_ASSIGNMENT, NCS_PREGAME, (void*)&OnPlayerAssignment, context);
AddTransition(NCS_PREGAME, (uint)NMT_KICKED, NCS_PREGAME, (void*)&OnKicked, context);
AddTransition(NCS_PREGAME, (uint)NMT_CLIENT_TIMEOUT, NCS_PREGAME, (void*)&OnClientTimeout, context);
AddTransition(NCS_PREGAME, (uint)NMT_CLIENT_PERFORMANCE, NCS_PREGAME, (void*)&OnClientPerformance, context);
AddTransition(NCS_PREGAME, (uint)NMT_GAME_START, NCS_LOADING, (void*)&OnGameStart, context);
AddTransition(NCS_PREGAME, (uint)NMT_JOIN_SYNC_START, NCS_JOIN_SYNCING, (void*)&OnJoinSyncStart, context);
AddTransition(NCS_JOIN_SYNCING, (uint)NMT_CHAT, NCS_JOIN_SYNCING, (void*)&OnChat, context);
AddTransition(NCS_JOIN_SYNCING, (uint)NMT_GAME_SETUP, NCS_JOIN_SYNCING, (void*)&OnGameSetup, context);
AddTransition(NCS_JOIN_SYNCING, (uint)NMT_PLAYER_ASSIGNMENT, NCS_JOIN_SYNCING, (void*)&OnPlayerAssignment, context);
AddTransition(NCS_JOIN_SYNCING, (uint)NMT_CLIENT_TIMEOUT, NCS_JOIN_SYNCING, (void*)&OnClientTimeout, context);
AddTransition(NCS_JOIN_SYNCING, (uint)NMT_CLIENT_PERFORMANCE, NCS_JOIN_SYNCING, (void*)&OnClientPerformance, context);
AddTransition(NCS_JOIN_SYNCING, (uint)NMT_GAME_START, NCS_JOIN_SYNCING, (void*)&OnGameStart, context);
AddTransition(NCS_JOIN_SYNCING, (uint)NMT_SIMULATION_COMMAND, NCS_JOIN_SYNCING, (void*)&OnInGame, context);
AddTransition(NCS_JOIN_SYNCING, (uint)NMT_END_COMMAND_BATCH, NCS_JOIN_SYNCING, (void*)&OnJoinSyncEndCommandBatch, context);
@ -108,10 +113,14 @@ CNetClient::CNetClient(CGame* game) :
AddTransition(NCS_LOADING, (uint)NMT_CHAT, NCS_LOADING, (void*)&OnChat, context);
AddTransition(NCS_LOADING, (uint)NMT_GAME_SETUP, NCS_LOADING, (void*)&OnGameSetup, context);
AddTransition(NCS_LOADING, (uint)NMT_PLAYER_ASSIGNMENT, NCS_LOADING, (void*)&OnPlayerAssignment, context);
AddTransition(NCS_LOADING, (uint)NMT_CLIENT_TIMEOUT, NCS_LOADING, (void*)&OnClientTimeout, context);
AddTransition(NCS_LOADING, (uint)NMT_CLIENT_PERFORMANCE, NCS_LOADING, (void*)&OnClientPerformance, context);
AddTransition(NCS_LOADING, (uint)NMT_LOADED_GAME, NCS_INGAME, (void*)&OnLoadedGame, context);
AddTransition(NCS_INGAME, (uint)NMT_REJOINED, NCS_INGAME, (void*)&OnRejoined, context);
AddTransition(NCS_INGAME, (uint)NMT_KICKED, NCS_INGAME, (void*)&OnKicked, context);
AddTransition(NCS_INGAME, (uint)NMT_CLIENT_TIMEOUT, NCS_INGAME, (void*)&OnClientTimeout, context);
AddTransition(NCS_INGAME, (uint)NMT_CLIENT_PERFORMANCE, NCS_INGAME, (void*)&OnClientPerformance, context);
AddTransition(NCS_INGAME, (uint)NMT_CHAT, NCS_INGAME, (void*)&OnChat, context);
AddTransition(NCS_INGAME, (uint)NMT_GAME_SETUP, NCS_INGAME, (void*)&OnGameSetup, context);
AddTransition(NCS_INGAME, (uint)NMT_PLAYER_ASSIGNMENT, NCS_INGAME, (void*)&OnPlayerAssignment, context);
@ -170,8 +179,45 @@ void CNetClient::DestroyConnection()
void CNetClient::Poll()
{
if (m_Session)
m_Session->Poll();
if (!m_Session)
return;
CheckServerConnection();
m_Session->Poll();
}
void CNetClient::CheckServerConnection()
{
// Trigger local warnings if the connection to the server is bad.
// At most once per second.
std::time_t now = std::time(nullptr);
if (now <= m_LastConnectionCheck)
return;
m_LastConnectionCheck = now;
JSContext* cx = GetScriptInterface().GetContext();
// Report if we are losing the connection to the server
u32 lastReceived = m_Session->GetLastReceivedTime();
if (lastReceived > NETWORK_WARNING_TIMEOUT)
{
JS::RootedValue msg(cx);
GetScriptInterface().Eval("({ 'type':'netwarn', 'warntype': 'server-timeout' })", &msg);
GetScriptInterface().SetProperty(msg, "lastReceivedTime", lastReceived);
PushGuiMessage(msg);
return;
}
// Report if we have a bad ping to the server
u32 meanRTT = m_Session->GetMeanRTT();
if (meanRTT > DEFAULT_TURN_LENGTH_MP)
{
JS::RootedValue msg(cx);
GetScriptInterface().Eval("({ 'type':'netwarn', 'warntype': 'server-latency' })", &msg);
GetScriptInterface().SetProperty(msg, "meanRTT", meanRTT);
PushGuiMessage(msg);
}
}
void CNetClient::Flush()
@ -627,6 +673,60 @@ bool CNetClient::OnKicked(void *context, CFsmEvent* event)
return true;
}
bool CNetClient::OnClientTimeout(void *context, CFsmEvent* event)
{
// Report the timeout of some other client
ENSURE(event->GetType() == (uint)NMT_CLIENT_TIMEOUT);
CNetClient* client = (CNetClient*)context;
JSContext* cx = client->GetScriptInterface().GetContext();
if (client->GetCurrState() == NCS_LOADING)
return true;
CClientTimeoutMessage* message = (CClientTimeoutMessage*)event->GetParamRef();
JS::RootedValue msg(cx);
client->GetScriptInterface().Eval("({ 'type':'netwarn', 'warntype': 'client-timeout' })", &msg);
client->GetScriptInterface().SetProperty(msg, "guid", std::string(message->m_GUID));
client->GetScriptInterface().SetProperty(msg, "lastReceivedTime", message->m_LastReceivedTime);
client->PushGuiMessage(msg);
return true;
}
bool CNetClient::OnClientPerformance(void *context, CFsmEvent* event)
{
// Performance statistics for one or multiple clients
ENSURE(event->GetType() == (uint)NMT_CLIENT_PERFORMANCE);
CNetClient* client = (CNetClient*)context;
JSContext* cx = client->GetScriptInterface().GetContext();
if (client->GetCurrState() == NCS_LOADING)
return true;
CClientPerformanceMessage* message = (CClientPerformanceMessage*)event->GetParamRef();
std::vector<CClientPerformanceMessage::S_m_Clients> &clients = message->m_Clients;
// Display warnings for other clients with bad ping
for (size_t i = 0; i < clients.size(); ++i)
{
if (clients[i].m_MeanRTT < DEFAULT_TURN_LENGTH_MP || clients[i].m_GUID == client->m_GUID)
continue;
JS::RootedValue msg(cx);
client->GetScriptInterface().Eval("({ 'type':'netwarn', 'warntype': 'client-latency' })", &msg);
client->GetScriptInterface().SetProperty(msg, "guid", clients[i].m_GUID);
client->GetScriptInterface().SetProperty(msg, "meanRTT", clients[i].m_MeanRTT);
client->PushGuiMessage(msg);
}
return true;
}
bool CNetClient::OnLoadedGame(void* context, CFsmEvent* event)
{
ENSURE(event->GetType() == (uint)NMT_LOADED_GAME);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2015 Wildfire Games.
/* Copyright (C) 2016 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -108,6 +108,11 @@ public:
*/
void Poll();
/**
* Locally triggers a GUI message if the connection to the server is being lost or has bad latency.
*/
void CheckServerConnection();
/**
* Flush any queued outgoing network messages.
* This should be called soon after sending a group of messages that may be batched together.
@ -202,6 +207,8 @@ private:
static bool OnJoinSyncEndCommandBatch(void* context, CFsmEvent* event);
static bool OnRejoined(void* context, CFsmEvent* event);
static bool OnKicked(void* context, CFsmEvent* event);
static bool OnClientTimeout(void* context, CFsmEvent* event);
static bool OnClientPerformance(void* context, CFsmEvent* event);
static bool OnLoadedGame(void* context, CFsmEvent* event);
/**
@ -240,6 +247,9 @@ private:
/// Serialized game state received when joining an in-progress game
std::string m_JoinSyncBuffer;
/// Time when the server was last checked for timeouts and bad latency
std::time_t m_LastConnectionCheck;
};
/// Global network client for the standard game

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2015 Wildfire Games.
/* Copyright (C) 2016 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -139,6 +139,14 @@ CNetMessage* CNetMessageFactory::CreateMessage(const void* pData,
pNewMessage = new CKickedMessage;
break;
case NMT_CLIENT_TIMEOUT:
pNewMessage = new CClientTimeoutMessage;
break;
case NMT_CLIENT_PERFORMANCE:
pNewMessage = new CClientPerformanceMessage;
break;
case NMT_LOADED_GAME:
pNewMessage = new CLoadedGameMessage;
break;

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2015 Wildfire Games.
/* Copyright (C) 2016 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -28,7 +28,7 @@
#define PS_PROTOCOL_MAGIC 0x5073013f // 'P', 's', 0x01, '?'
#define PS_PROTOCOL_MAGIC_RESPONSE 0x50630121 // 'P', 'c', 0x01, '!'
#define PS_PROTOCOL_VERSION 0x01010008 // Arbitrary protocol
#define PS_PROTOCOL_VERSION 0x01010009 // Arbitrary protocol
#define PS_DEFAULT_PORT 0x5073 // 'P', 's'
// Defines the list of message types. The order of the list must not change.
@ -60,6 +60,9 @@ enum NetMessageType
NMT_REJOINED,
NMT_KICKED,
NMT_CLIENT_TIMEOUT,
NMT_CLIENT_PERFORMANCE,
NMT_LOADED_GAME,
NMT_GAME_START,
NMT_END_COMMAND_BATCH,
@ -167,6 +170,18 @@ START_NMT_CLASS_(Kicked, NMT_KICKED)
NMT_FIELD_INT(m_Ban, u8, 1)
END_NMT_CLASS()
START_NMT_CLASS_(ClientTimeout, NMT_CLIENT_TIMEOUT)
NMT_FIELD(CStr8, m_GUID)
NMT_FIELD_INT(m_LastReceivedTime, u32, 4)
END_NMT_CLASS()
START_NMT_CLASS_(ClientPerformance, NMT_CLIENT_PERFORMANCE)
NMT_START_ARRAY(m_Clients)
NMT_FIELD(CStr8, m_GUID)
NMT_FIELD_INT(m_MeanRTT, u32, 4)
NMT_END_ARRAY()
END_NMT_CLASS()
START_NMT_CLASS_(LoadedGame, NMT_LOADED_GAME)
NMT_FIELD_INT(m_CurrentTurn, u32, 4)
END_NMT_CLASS()

View File

@ -121,7 +121,8 @@ CNetServerWorker::CNetServerWorker(int autostartPlayers) :
m_AutostartPlayers(autostartPlayers),
m_Shutdown(false),
m_ScriptInterface(NULL),
m_NextHostID(1), m_Host(NULL), m_HostGUID(), m_Stats(NULL)
m_NextHostID(1), m_Host(NULL), m_HostGUID(), m_Stats(NULL),
m_LastConnectionCheck(0)
{
m_State = SERVER_STATE_UNCONNECTED;
@ -451,6 +452,8 @@ bool CNetServerWorker::RunStep()
for (size_t i = 0; i < m_Sessions.size(); ++i)
m_Sessions[i]->GetFileTransferer().Poll();
CheckClientConnections();
// Process network events:
ENetEvent event;
@ -549,6 +552,55 @@ bool CNetServerWorker::RunStep()
return true;
}
void CNetServerWorker::CheckClientConnections()
{
if (m_State == SERVER_STATE_LOADING)
return;
// Send messages at most once per second
std::time_t now = std::time(nullptr);
if (now <= m_LastConnectionCheck)
return;
m_LastConnectionCheck = now;
for (size_t i = 0; i < m_Sessions.size(); ++i)
{
u32 lastReceived = m_Sessions[i]->GetLastReceivedTime();
u32 meanRTT = m_Sessions[i]->GetMeanRTT();
CNetMessage* message = nullptr;
// Report if we didn't hear from the client since few seconds
if (lastReceived > NETWORK_WARNING_TIMEOUT)
{
CClientTimeoutMessage* msg = new CClientTimeoutMessage();
msg->m_GUID = m_Sessions[i]->GetGUID();
msg->m_LastReceivedTime = lastReceived;
message = msg;
}
// Report if the client has bad ping
else if (meanRTT > DEFAULT_TURN_LENGTH_MP)
{
CClientPerformanceMessage* msg = new CClientPerformanceMessage();
CClientPerformanceMessage::S_m_Clients client;
client.m_GUID = m_Sessions[i]->GetGUID();
client.m_MeanRTT = meanRTT;
msg->m_Clients.push_back(client);
message = msg;
}
// Send to all clients except the affected one
// (since that will show the locally triggered warning instead)
if (message)
for (size_t j = 0; j < m_Sessions.size(); ++j)
if (i != j)
m_Sessions[j]->SendMessage(message);
SAFE_DELETE(message);
}
}
void CNetServerWorker::HandleMessageReceive(const CNetMessage* message, CNetServerSession* session)
{
// Handle non-FSM messages first

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2015 Wildfire Games.
/* Copyright (C) 2016 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -281,6 +281,10 @@ private:
void HandleMessageReceive(const CNetMessage* message, CNetServerSession* session);
/**
* Send a network warning if the connection to a client is being lost or has bad latency.
*/
void CheckClientConnections();
/**
* Internal script context for (de)serializing script messages,
@ -331,6 +335,11 @@ private:
*/
std::string m_JoinSyncFile;
/**
* Time when the clients connections were last checked for timeouts and latency.
*/
std::time_t m_LastConnectionCheck;
private:
// Thread-related stuff:

View File

@ -25,6 +25,8 @@
#include "ps/CLogger.h"
#include "scriptinterface/ScriptInterface.h"
const u32 NETWORK_WARNING_TIMEOUT = 4000;
static const int CHANNEL_COUNT = 1;
CNetClientSession::CNetClientSession(CNetClient& client) :
@ -168,6 +170,22 @@ bool CNetClientSession::SendMessage(const CNetMessage* message)
return CNetHost::SendMessage(message, m_Server, "server");
}
u32 CNetClientSession::GetLastReceivedTime() const
{
if (!m_Server)
return 0;
return enet_time_get() - m_Server->lastReceiveTime;
}
u32 CNetClientSession::GetMeanRTT() const
{
if (!m_Server)
return 0;
return m_Server->roundTripTime;
}
CNetServerSession::CNetServerSession(CNetServerWorker& server, ENetPeer* peer) :
@ -184,6 +202,22 @@ CStr CNetServerSession::GetIPAddress() const
return ipAddress;
}
u32 CNetServerSession::GetLastReceivedTime() const
{
if (!m_Peer)
return 0;
return enet_time_get() - m_Peer->lastReceiveTime;
}
u32 CNetServerSession::GetMeanRTT() const
{
if (!m_Peer)
return 0;
return m_Peer->roundTripTime;
}
void CNetServerSession::Disconnect(u32 reason)
{
Update((uint)NMT_CONNECTION_LOST, NULL);

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2015 Wildfire Games.
/* Copyright (C) 2016 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -24,6 +24,11 @@
#include "ps/CStr.h"
#include "scriptinterface/ScriptVal.h"
/**
* Report the peer if we didn't receive a packet after this time (milliseconds).
*/
extern const u32 NETWORK_WARNING_TIMEOUT;
class CNetClient;
class CNetServerWorker;
@ -83,6 +88,16 @@ public:
*/
virtual bool SendMessage(const CNetMessage* message);
/**
* Number of milliseconds since the most recent packet of the server was received.
*/
u32 GetLastReceivedTime() const;
/**
* Average round trip time to the server.
*/
u32 GetMeanRTT() const;
CNetFileTransferer& GetFileTransferer() { return m_FileTransferer; }
private:
@ -124,6 +139,16 @@ public:
CStr GetIPAddress() const;
/**
* Number of milliseconds since the latest packet of that client was received.
*/
u32 GetLastReceivedTime() const;
/**
* Average round trip time to the client.
*/
u32 GetMeanRTT() const;
/**
* Sends a disconnection notification to the client,
* and sends a NMT_CONNECTION_LOST message to the session FSM.

View File

@ -38,8 +38,8 @@
#include <fstream>
#include <iomanip>
static const int DEFAULT_TURN_LENGTH_MP = 500;
static const int DEFAULT_TURN_LENGTH_SP = 200;
const u32 DEFAULT_TURN_LENGTH_MP = 500;
const u32 DEFAULT_TURN_LENGTH_SP = 200;
static const int COMMAND_DELAY = 2;

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2015 Wildfire Games.
/* Copyright (C) 2016 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -26,6 +26,9 @@
#include <map>
#include <vector>
extern const u32 DEFAULT_TURN_LENGTH_MP;
extern const u32 DEFAULT_TURN_LENGTH_SP;
class CNetServerWorker;
class CNetClient;
class CSimulationMessage;