From 313c3595b9ca041d86cdd0f9ea71b13e1289ab83 Mon Sep 17 00:00:00 2001 From: maxsupermanhd Date: Sun, 20 Aug 2023 22:13:00 +0300 Subject: [PATCH] Implement automatic desync kick --- lib/netplay/netplay.cpp | 1 + src/configuration.cpp | 2 ++ src/multibot.cpp | 2 ++ src/multiint.cpp | 1 + src/multijoin.cpp | 3 ++ src/multiplay.cpp | 72 +++++++++++++++++++++++++++++++++++++++-- src/multiplay.h | 2 ++ src/warzoneconfig.cpp | 16 +++++++++ src/warzoneconfig.h | 2 ++ 9 files changed, 98 insertions(+), 3 deletions(-) diff --git a/lib/netplay/netplay.cpp b/lib/netplay/netplay.cpp index 2d4135b4271..fd7561424b9 100644 --- a/lib/netplay/netplay.cpp +++ b/lib/netplay/netplay.cpp @@ -1898,6 +1898,7 @@ static bool swapPlayerIndexes(uint32_t playerIndexA, uint32_t playerIndexB) // Swap certain ingame player-associated entries std::swap(ingame.PingTimes[playerIndexA], ingame.PingTimes[playerIndexB]); std::swap(ingame.LagCounter[playerIndexA], ingame.LagCounter[playerIndexB]); + std::swap(ingame.DesyncCounter[playerIndexA], ingame.DesyncCounter[playerIndexB]); std::swap(ingame.VerifiedIdentity[playerIndexA], ingame.VerifiedIdentity[playerIndexB]); std::swap(ingame.JoiningInProgress[playerIndexA], ingame.JoiningInProgress[playerIndexB]); std::swap(ingame.DataIntegrity[playerIndexA], ingame.DataIntegrity[playerIndexB]); diff --git a/src/configuration.cpp b/src/configuration.cpp index a6971bf9855..a891777f45a 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -581,6 +581,7 @@ bool loadConfig() pie_EnableFog(false); } war_setAutoLagKickSeconds(iniGetInteger("hostAutoLagKickSeconds", war_getAutoLagKickSeconds()).value()); + war_setAutoDesyncKickSeconds(iniGetInteger("hostAutoDesyncKickSeconds", war_getAutoDesyncKickSeconds()).value()); war_setDisableReplayRecording(iniGetBool("disableReplayRecord", war_getDisableReplayRecording()).value()); war_setMaxReplaysSaved(iniGetInteger("maxReplaysSaved", war_getMaxReplaysSaved()).value()); war_setOldLogsLimit(iniGetInteger("oldLogsLimit", war_getOldLogsLimit()).value()); @@ -768,6 +769,7 @@ bool saveConfig() iniSetBool("autosaveEnabled", autosaveEnabled); iniSetBool("fog", pie_GetFogEnabled()); iniSetInteger("hostAutoLagKickSeconds", war_getAutoLagKickSeconds()); + iniSetInteger("hostAutoDesyncKickSeconds", war_getAutoDesyncKickSeconds()); iniSetBool("disableReplayRecord", war_getDisableReplayRecording()); iniSetInteger("maxReplaysSaved", war_getMaxReplaysSaved()); iniSetInteger("oldLogsLimit", war_getOldLogsLimit()); diff --git a/src/multibot.cpp b/src/multibot.cpp index 99f6c181bba..c9238b3cc4b 100644 --- a/src/multibot.cpp +++ b/src/multibot.cpp @@ -43,6 +43,8 @@ #include "mapgrid.h" #include "multirecv.h" #include "transporter.h" +#include "warzoneconfig.h" +#include "multiint.h" #include #include diff --git a/src/multiint.cpp b/src/multiint.cpp index cff55349cf0..3d72dea3125 100644 --- a/src/multiint.cpp +++ b/src/multiint.cpp @@ -5842,6 +5842,7 @@ bool WzMultiplayerOptionsTitleUI::startHost() ingame.PingTimes[i] = 0; ingame.VerifiedIdentity[i] = false; ingame.LagCounter[i] = 0; + ingame.DesyncCounter[i] = 0; ingame.lastSentPlayerDataCheck2[i].reset(); ingame.muteChat[i] = false; } diff --git a/src/multijoin.cpp b/src/multijoin.cpp index 20ddf85e1d5..579481caa10 100644 --- a/src/multijoin.cpp +++ b/src/multijoin.cpp @@ -189,6 +189,7 @@ void clearPlayer(UDWORD player, bool quietly) debug(LOG_NET, "R.I.P. %s (%u). quietly is %s", getPlayerName(player), player, quietly ? "true" : "false"); ingame.LagCounter[player] = 0; + ingame.DesyncCounter[player] = 0; ingame.JoiningInProgress[player] = false; // if they never joined, reset the flag ingame.DataIntegrity[player] = false; ingame.lastSentPlayerDataCheck2[player].reset(); @@ -396,6 +397,7 @@ void handlePlayerLeftInGame(UDWORD player) debug(LOG_NET, "R.I.P. %s (%u).", getPlayerName(player), player); ingame.LagCounter[player] = 0; + ingame.DesyncCounter[player] = 0; ingame.JoiningInProgress[player] = false; // if they never joined, reset the flag ingame.DataIntegrity[player] = false; ingame.lastSentPlayerDataCheck2[player].reset(); @@ -718,6 +720,7 @@ void setupNewPlayer(UDWORD player) ingame.PingTimes[player] = 0; // Reset ping time ingame.LagCounter[player] = 0; + ingame.DesyncCounter[player] = 0; ingame.VerifiedIdentity[player] = false; ingame.JoiningInProgress[player] = true; // Note that player is now joining ingame.DataIntegrity[player] = false; diff --git a/src/multiplay.cpp b/src/multiplay.cpp index 480d40cc7b2..8cda0bb7724 100644 --- a/src/multiplay.cpp +++ b/src/multiplay.cpp @@ -111,7 +111,7 @@ static bool sendDataCheck2(); void startMultiplayerGame(); // //////////////////////////////////////////////////////////////////////////// -// Auto Lag Kick Handling +// Auto Kick Handling #define LAG_INITIAL_LOAD_GRACEPERIOD 60 #define LAG_CHECK_INTERVAL 1000 @@ -180,12 +180,77 @@ static void autoLagKickRoutine() ingame.LagCounter[i] = 0; } else if (ingame.LagCounter[i] >= (LagAutoKickSeconds - 3)) { - std::string msg = astringf("Auto-kicking player %" PRIu32 " (\"%s\") in %u seconds.", i, getPlayerName(i), (LagAutoKickSeconds - ingame.LagCounter[i])); + std::string msg = astringf("Auto-kicking player %" PRIu32 " (\"%s\") in %u seconds. (lag)", i, getPlayerName(i), (LagAutoKickSeconds - ingame.LagCounter[i])); debug(LOG_INFO, "%s", msg.c_str()); sendTextMessage(msg.c_str()); } else if (ingame.LagCounter[i] % 15 == 0) { // every 15 seconds - std::string msg = astringf("Auto-kicking player %" PRIu32 " (\"%s\") in %u seconds.", i, getPlayerName(i), (LagAutoKickSeconds - ingame.LagCounter[i])); + std::string msg = astringf("Auto-kicking player %" PRIu32 " (\"%s\") in %u seconds. (lag)", i, getPlayerName(i), (LagAutoKickSeconds - ingame.LagCounter[i])); + debug(LOG_INFO, "%s", msg.c_str()); + sendTextMessage(msg.c_str()); + } + } +} + +#define DESYNC_CHECK_INTERVAL 1000 +const std::chrono::milliseconds DesyncCheckInterval(DESYNC_CHECK_INTERVAL); + +static void autoDesyncKickRoutine() +{ + if (!NetPlay.isHost) + { + return; + } + + int DesyncAutoKickSeconds = war_getAutoDesyncKickSeconds(); + if (DesyncAutoKickSeconds <= 0) + { + return; + } + + const std::chrono::steady_clock::time_point now = std::chrono::steady_clock::now(); + if (std::chrono::duration_cast(now - ingame.lastDesyncCheck) < DesyncCheckInterval) + { + return; + } + + ingame.lastDesyncCheck = now; + uint32_t playerCheckLimit = (!ingame.TimeEveryoneIsInGame.has_value()) ? MAX_CONNECTED_PLAYERS : MAX_PLAYERS; + for (uint32_t i = 0; i < playerCheckLimit; ++i) + { + if (!isHumanPlayer(i)) + { + continue; + } + if (i > MAX_PLAYERS && !gtimeShouldWaitForPlayer(i)) + { + continue; + } + if (NETcheckPlayerConnectionStatus(CONNECTIONSTATUS_DESYNC, i)) + { + ingame.DesyncCounter[i]++; + } + else + { + ingame.DesyncCounter[i] = 0; + continue; + } + debug(LOG_INFO, "Player %d Status %d Counter %d", i, NETcheckPlayerConnectionStatus(CONNECTIONSTATUS_DESYNC, i), ingame.DesyncCounter[i]); + if (ingame.DesyncCounter[i] >= DesyncAutoKickSeconds) { + std::string msg = astringf("Auto-kicking player %" PRIu32 " (\"%s\") because of desync. (Timeout: %u seconds)", i, getPlayerName(i), DesyncAutoKickSeconds); + debug(LOG_INFO, "%s", msg.c_str()); + sendTextMessage(msg.c_str()); + wz_command_interface_output("WZEVENT: desync-kick: %u %s\n", i, NetPlay.players[i].IPtextAddress); + kickPlayer(i, "Your timeline diviated too far.", ERROR_CONNECTION, false); + ingame.DesyncCounter[i] = 0; + } + else if (ingame.DesyncCounter[i] >= (DesyncAutoKickSeconds - 3)) { + std::string msg = astringf("Auto-kicking player %" PRIu32 " (\"%s\") in %u seconds. (desync)", i, getPlayerName(i), (DesyncAutoKickSeconds - ingame.DesyncCounter[i])); + debug(LOG_INFO, "%s", msg.c_str()); + sendTextMessage(msg.c_str()); + } + else if (ingame.DesyncCounter[i] % 2 == 0) { // every 2 seconds + std::string msg = astringf("Auto-kicking player %" PRIu32 " (\"%s\") in %u seconds. (desync)", i, getPlayerName(i), (DesyncAutoKickSeconds - ingame.DesyncCounter[i])); debug(LOG_INFO, "%s", msg.c_str()); sendTextMessage(msg.c_str()); } @@ -385,6 +450,7 @@ bool multiPlayerLoop() if (NetPlay.isHost) { autoLagKickRoutine(); + autoDesyncKickRoutine(); } // if player has won then process the win effects... diff --git a/src/multiplay.h b/src/multiplay.h index a7178c5039d..f365a911de4 100644 --- a/src/multiplay.h +++ b/src/multiplay.h @@ -105,6 +105,7 @@ struct MULTIPLAYERINGAME { UDWORD PingTimes[MAX_CONNECTED_PLAYERS]; // store for pings. int LagCounter[MAX_CONNECTED_PLAYERS]; + int DesyncCounter[MAX_CONNECTED_PLAYERS]; bool VerifiedIdentity[MAX_CONNECTED_PLAYERS]; // if the multistats identity has been verified. bool localOptionsReceived; // used to show if we have game options yet.. bool localJoiningInProgress; // used before we know our player number. @@ -116,6 +117,7 @@ struct MULTIPLAYERINGAME std::chrono::steady_clock::time_point startTime; optional endTime; std::chrono::steady_clock::time_point lastLagCheck; + std::chrono::steady_clock::time_point lastDesyncCheck; optional lastSentPlayerDataCheck2[MAX_CONNECTED_PLAYERS] = {}; std::chrono::steady_clock::time_point lastPlayerDataCheck2; bool muteChat[MAX_CONNECTED_PLAYERS] = {false}; diff --git a/src/warzoneconfig.cpp b/src/warzoneconfig.cpp index 1ea19eac540..b99d5ac4192 100644 --- a/src/warzoneconfig.cpp +++ b/src/warzoneconfig.cpp @@ -68,6 +68,7 @@ struct WARZONE_GLOBALS JS_BACKEND jsBackend = (JS_BACKEND)0; bool autoAdjustDisplayScale = true; int autoLagKickSeconds = 60; + int autoDesyncKickSeconds = 10; bool disableReplayRecording = false; int maxReplaysSaved = MAX_REPLAY_FILES; int oldLogsLimit = MAX_OLD_LOGS; @@ -470,6 +471,21 @@ void war_setAutoLagKickSeconds(int seconds) warGlobs.autoLagKickSeconds = seconds; } +int war_getAutoDesyncKickSeconds() +{ + return warGlobs.autoDesyncKickSeconds; +} + +void war_setAutoDesyncKickSeconds(int seconds) +{ + seconds = std::max(seconds, 0); + if (seconds > 0) + { + seconds = std::max(seconds, 10); + } + warGlobs.autoDesyncKickSeconds = seconds; +} + bool war_getDisableReplayRecording() { return warGlobs.disableReplayRecording; diff --git a/src/warzoneconfig.h b/src/warzoneconfig.h index 70520d13db0..d43a73bcd5e 100644 --- a/src/warzoneconfig.h +++ b/src/warzoneconfig.h @@ -132,6 +132,8 @@ bool war_getAutoAdjustDisplayScale(); void war_setAutoAdjustDisplayScale(bool autoAdjustDisplayScale); int war_getAutoLagKickSeconds(); void war_setAutoLagKickSeconds(int seconds); +int war_getAutoDesyncKickSeconds(); +void war_setAutoDesyncKickSeconds(int seconds); bool war_getDisableReplayRecording(); void war_setDisableReplayRecording(bool disable); int war_getMaxReplaysSaved();