From 5ebf2020b04b28a48332b2449eafc06c7f3af49a Mon Sep 17 00:00:00 2001 From: wraitii Date: Mon, 29 Mar 2021 07:53:06 +0000 Subject: [PATCH] Netcode: allow observers to lag behind the live game. Observers no longer lag the game for players. There is still some time to serialise the game when sending it to a joining observer, and depending on the chosen 'max lag' the game may stop while observers sufficiently catch up, but this impact too is reduced. - Make the NetServerTurnManager ignore players marked as 'observers' for the purpose of ending a turn, effectively making it possible for observers to lag without it affecting the players in any way. - Add a config option (network.observermaxlag) that specifies how many turns behind the live game observers are allowed to be. Default to 10 turns, or 2 seconds, to keep them 'largely live'. - The controller is not treated as an observer. - Implement a simple UI to show this delay & allow the game to speed up automatically to try and catch up. This can be deactivated via network.autocatchup. - Move network options to the renamed 'Network / Lobby' options page. - Do not debug_warn/crash when receiving commands from the past - instead warn and carry on, to avoid DOS and "coop play" issues. Refs #5903, Refs #4210 Differential Revision: https://code.wildfiregames.com/D3737 This was SVN commit r25156. --- binaries/data/config/default.cfg | 2 + .../data/mods/public/gui/options/options.json | 66 +++++++++++-------- .../public/gui/session/NetworkDelayOverlay.js | 63 ++++++++++++++++++ .../gui/session/NetworkDelayOverlay.xml | 14 ++++ .../data/mods/public/gui/session/session.js | 2 + .../data/mods/public/gui/session/session.xml | 1 + source/network/NetServer.cpp | 12 +++- source/network/NetServerTurnManager.cpp | 13 +++- source/network/NetServerTurnManager.h | 21 +++--- source/ps/scripting/JSInterface_Game.cpp | 12 ++++ source/simulation2/system/TurnManager.cpp | 32 ++++----- source/simulation2/system/TurnManager.h | 10 ++- 12 files changed, 188 insertions(+), 60 deletions(-) create mode 100644 binaries/data/mods/public/gui/session/NetworkDelayOverlay.js create mode 100644 binaries/data/mods/public/gui/session/NetworkDelayOverlay.xml diff --git a/binaries/data/config/default.cfg b/binaries/data/config/default.cfg index b49da8636c..b67a3a645e 100644 --- a/binaries/data/config/default.cfg +++ b/binaries/data/config/default.cfg @@ -486,6 +486,8 @@ name_id = "0ad" duplicateplayernames = false ; Rename joining player to "User (2)" if "User" is already connected, otherwise prohibit join. lateobservers = everyone ; Allow observers to join the game after it started. Possible values: everyone, buddies, disabled. observerlimit = 8 ; Prevent further observer joins in running games if this limit is reached +observermaxlag = 10 ; Make clients wait for observers if they lag more than X turns behind. -1 means "never wait for observers". +autocatchup = true ; Auto-accelerate the sim rate if lagging behind (as an observer). [overlay] fps = "false" ; Show frames per second in top right corner diff --git a/binaries/data/mods/public/gui/options/options.json b/binaries/data/mods/public/gui/options/options.json index eccaca9747..ce7c494d46 100644 --- a/binaries/data/mods/public/gui/options/options.json +++ b/binaries/data/mods/public/gui/options/options.json @@ -27,12 +27,6 @@ "tooltip": "If you disable it, the welcome screen will still appear once, each time a new version is available. You can always launch it from the main menu.", "config": "gui.splashscreen.enable" }, - { - "type": "boolean", - "label": "Network warnings", - "tooltip": "Show which player has a bad connection in multiplayer games.", - "config": "overlay.netwarnings" - }, { "type": "boolean", "label": "FPS overlay", @@ -57,25 +51,6 @@ "tooltip": "Always show the remaining ceasefire time.", "config": "gui.session.ceasefirecounter" }, - { - "type": "dropdown", - "label": "Late observer joins", - "tooltip": "Allow everybody or buddies only to join the game as observer after it started.", - "config": "network.lateobservers", - "list": [ - { "value": "everyone", "label": "Everyone" }, - { "value": "buddies", "label": "Buddies" }, - { "value": "disabled", "label": "Disabled" } - ] - }, - { - "type": "number", - "label": "Observer limit", - "tooltip": "Prevent further observers from joining if the limit is reached.", - "config": "network.observerlimit", - "min": 0, - "max": 32 - }, { "type": "boolean", "label": "Chat timestamp", @@ -425,7 +400,7 @@ ] }, { - "label": "Lobby", + "label": "Networking / Lobby", "tooltip": "These settings only affect the multiplayer.", "options": [ @@ -447,6 +422,45 @@ "label": "Game rating column", "tooltip": "Show the average rating of the participating players in a column of the gamelist.", "config": "lobby.columns.gamerating" + }, + { + "type": "boolean", + "label": "Network warnings", + "tooltip": "Show which player has a bad connection in multiplayer games.", + "config": "overlay.netwarnings" + }, + { + "type": "dropdown", + "label": "Late observer joins", + "tooltip": "Allow everybody or buddies only to join the game as observer after it started.", + "config": "network.lateobservers", + "list": [ + { "value": "everyone", "label": "Everyone" }, + { "value": "buddies", "label": "Buddies" }, + { "value": "disabled", "label": "Disabled" } + ] + }, + { + "type": "number", + "label": "Observer limit", + "tooltip": "Prevent further observers from joining if the limit is reached.", + "config": "network.observerlimit", + "min": 0, + "max": 32 + }, + { + "type": "number", + "label": "Max lag for observers", + "tooltip": "When hosting, pause the game if observers are lagging more than this many turns. If set to -1, observers are ignored.", + "config": "network.observermaxlag", + "min": -1, + "max": 10000 + }, + { + "type": "boolean", + "label": "(Observer) Speed up when lagging.", + "tooltip": "When observing a game, automatically speed up if you start lagging, to catch up with the live match.", + "config": "network.autocatchup" } ] }, diff --git a/binaries/data/mods/public/gui/session/NetworkDelayOverlay.js b/binaries/data/mods/public/gui/session/NetworkDelayOverlay.js new file mode 100644 index 0000000000..a687c8d204 --- /dev/null +++ b/binaries/data/mods/public/gui/session/NetworkDelayOverlay.js @@ -0,0 +1,63 @@ +/** + * Shows an overlay if the game is lagging behind the net server. + */ +class NetworkDelayOverlay +{ + constructor() + { + this.netDelayOverlay = Engine.GetGUIObjectByName("netDelayOverlay"); + + this.netDelayOverlay.caption="toto"; + this.caption = translate(this.Caption); + this.sprintfData = {}; + + this.initialSimRate = Engine.GetSimRate(); + this.currentSimRate = this.initialSimRate; + + setTimeout(() => this.CheckDelay(), 1000); + } + + CheckDelay() + { + setTimeout(() => this.CheckDelay(), 1000); + let delay = +(Engine.HasNetClient() && Engine.GetPendingTurns()); + + if (g_IsObserver && Engine.ConfigDB_GetValue("user", "network.autocatchup")) + { + if (delay > this.MAX_LIVE_DELAY && this.currentSimRate <= this.initialSimRate) + { + this.currentSimRate = this.initialSimRate * 1.1; + Engine.SetSimRate(this.currentSimRate); + } + else if (delay <= this.NORMAL_DELAY && this.currentSimRate > this.initialSimRate) + { + this.currentSimRate = this.initialSimRate; + Engine.SetSimRate(this.currentSimRate); + } + } + + if (delay < this.MAX_LIVE_DELAY) + { + this.netDelayOverlay.hidden = true; + return; + } + this.netDelayOverlay.hidden = false; + this.sprintfData.delay = (delay / this.TURNS_PER_SECOND); + this.sprintfData.delay = this.sprintfData.delay.toFixed(this.sprintfData.delay < 5 ? 1 : 0); + this.netDelayOverlay.caption = sprintf(this.caption, this.sprintfData); + } +} + +/** + * Because of command delay, we can still be several turns behind the 'ready' turn and not + * particularly late. This should be kept in sync with the command delay. + */ +NetworkDelayOverlay.prototype.NORMAL_DELAY = 3; +NetworkDelayOverlay.prototype.MAX_LIVE_DELAY = 6; + +/** + * This needs to be kept in sync with the turn length. + */ +NetworkDelayOverlay.prototype.TURNS_PER_SECOND = 5; + +NetworkDelayOverlay.prototype.Caption = translate("Delay to live stream: %(delay)ss"); diff --git a/binaries/data/mods/public/gui/session/NetworkDelayOverlay.xml b/binaries/data/mods/public/gui/session/NetworkDelayOverlay.xml new file mode 100644 index 0000000000..7992d99773 --- /dev/null +++ b/binaries/data/mods/public/gui/session/NetworkDelayOverlay.xml @@ -0,0 +1,14 @@ + + +