From e9451baa38cf8ffd116c078d52b5880184304e03 Mon Sep 17 00:00:00 2001 From: Nicolai Cornelis Date: Fri, 16 Sep 2022 03:42:19 +0200 Subject: [PATCH] Fix problems with game going directly to live due to poorly timed swap/stay commands (#880) --- scripting/get5.sp | 13 ++++++++---- scripting/get5/goinglive.sp | 39 +++++++++++++++++------------------ scripting/get5/kniferounds.sp | 9 ++++---- scripting/get5/version.sp | 2 +- scripting/include/get5.inc | 2 +- 5 files changed, 35 insertions(+), 30 deletions(-) diff --git a/scripting/get5.sp b/scripting/get5.sp index e5b17bf89..ab3987e14 100644 --- a/scripting/get5.sp +++ b/scripting/get5.sp @@ -669,7 +669,11 @@ static Action Timer_InfoMessages(Handle timer) { g_MapChangePending && GetTvDelay() > 0) { Get5_MessageToAll("%t", "WaitingForGOTVVetoInfoMessage"); } else if (g_GameState == Get5State_WaitingForKnifeRoundDecision) { - // Handle waiting for knife decision + if (g_KnifeWinnerTeam == Get5Team_None) { + return Plugin_Continue; + } + // Handle waiting for knife decision. Also check g_KnifeWinnerTeam as there is a small delay between + // selecting a side and the game state changing, during which this message should not be printed. char formattedStayCommand[64]; FormatChatCommand(formattedStayCommand, sizeof(formattedStayCommand), "!stay"); char formattedSwapCommand[64]; @@ -1455,9 +1459,7 @@ static Action Event_RoundStart(Event event, const char[] name, bool dontBroadcas } // We cannot do this during warmup, as sending users into warmup post-knife triggers a round start - // event. We add an extra restart to clear lingering state from the knife round, such as the round - // indicator in the middle of the scoreboard not being reset. This also tightly couples the - // live-announcement to the actual live start. + // event. if (!InWarmup()) { if (g_GameState == Get5State_WaitingForKnifeRoundDecision) { // Ensures that round end after knife sends players directly into warmup. @@ -1471,6 +1473,9 @@ static Action Event_RoundStart(Event event, const char[] name, bool dontBroadcas if (g_GameState == Get5State_GoingLive) { LogDebug("Changed to live."); ChangeState(Get5State_Live); + // We add an extra restart to clear lingering state from the knife round, such as the round + // indicator in the middle of the scoreboard not being reset. This also tightly couples the + // live-announcement to the actual live start. RestartGame(); CreateTimer(3.0, Timer_MatchLive, _, TIMER_FLAG_NO_MAPCHANGE); return; // Next round start will take care of below, such as writing backup. diff --git a/scripting/get5/goinglive.sp b/scripting/get5/goinglive.sp index b808b7776..b297302df 100644 --- a/scripting/get5/goinglive.sp +++ b/scripting/get5/goinglive.sp @@ -1,41 +1,40 @@ void StartGoingLive() { LogDebug("StartGoingLive"); ExecCfg(g_LiveCfgCvar); - - Get5GoingLiveEvent liveEvent = new Get5GoingLiveEvent(g_MatchID, g_MapNumber); - - LogDebug("Calling Get5_OnGoingLive()"); - - Call_StartForward(g_OnGoingLive); - Call_PushCell(liveEvent); - Call_Finish(); - - EventLogger_LogAndDeleteEvent(liveEvent); - - ChangeState(Get5State_GoingLive); - // This ensures that we can send send the game to warmup and count down *even if* someone had put // "mp_warmup_end", or something else that would mess up warmup, in their live config, which they // shouldn't. But we can't be sure. - CreateTimer(1.0, Timer_GoToLiveAfterWarmupCountdown, _, TIMER_FLAG_NO_MAPCHANGE); + CreateTimer(1.0, Timer_GoToLive, _, TIMER_FLAG_NO_MAPCHANGE); } -static Action Timer_GoToLiveAfterWarmupCountdown(Handle timer) { - if (g_GameState != Get5State_GoingLive) { - return Plugin_Handled; // super defensive race-condition check. +static Action Timer_GoToLive(Handle timer) { + if (g_GameState != Get5State_Warmup && g_GameState != Get5State_WaitingForKnifeRoundDecision) { + // super defensive race-condition check. These are the only two allowed states + // for this callback, so if the game had been canceled during this time, do nothing. + return; } // Always disable sv_cheats! ServerCommand("sv_cheats 0"); // Ensure we're in warmup and counting down to live. Round_PreStart handles the rest. int countdown = g_LiveCountdownTimeCvar.IntValue; if (countdown < 5) { - countdown = - 5; // ensures that a cvar countdown value of 0 does not leave the game forever in warmup. + // ensures that a cvar countdown value of 0 does not leave the game forever in warmup. + countdown = 5; } Get5_MessageToAll("%t", "MatchBeginInSecondsInfoMessage", countdown); StartWarmup(countdown); LogDebug("Started warmup countdown to live in %d seconds.", countdown); - return Plugin_Handled; + + // Change state as we're now counting down to live from warmup. + ChangeState(Get5State_GoingLive); + + // Going live event + Get5GoingLiveEvent liveEvent = new Get5GoingLiveEvent(g_MatchID, g_MapNumber); + LogDebug("Calling Get5_OnGoingLive()"); + Call_StartForward(g_OnGoingLive); + Call_PushCell(liveEvent); + Call_Finish(); + EventLogger_LogAndDeleteEvent(liveEvent); } Action Timer_MatchLive(Handle timer) { diff --git a/scripting/get5/kniferounds.sp b/scripting/get5/kniferounds.sp index a5b5f6d3b..dfc028d1e 100644 --- a/scripting/get5/kniferounds.sp +++ b/scripting/get5/kniferounds.sp @@ -98,10 +98,11 @@ static void EndKnifeRound(bool swap) { } static bool AwaitingKnifeDecision(int client) { - bool waiting = g_GameState == Get5State_WaitingForKnifeRoundDecision; + if (g_GameState != Get5State_WaitingForKnifeRoundDecision || g_KnifeWinnerTeam == Get5Team_None) { + return false; + } bool onWinningTeam = IsPlayer(client) && GetClientMatchTeam(client) == g_KnifeWinnerTeam; - bool admin = (client == 0); - return waiting && (onWinningTeam || admin); + return onWinningTeam || (client == 0); } Action Command_Stay(int client, int args) { @@ -152,7 +153,7 @@ Action Command_T(int client, int args) { Action Timer_ForceKnifeDecision(Handle timer) { g_KnifeDecisionTimer = INVALID_HANDLE; - if (g_GameState == Get5State_WaitingForKnifeRoundDecision) { + if (g_GameState == Get5State_WaitingForKnifeRoundDecision && g_KnifeWinnerTeam != Get5Team_None) { Get5_MessageToAll("%t", "TeamLostTimeToDecideInfoMessage", g_FormattedTeamNames[g_KnifeWinnerTeam]); EndKnifeRound(false); diff --git a/scripting/get5/version.sp b/scripting/get5/version.sp index 5c11922ee..0102cbb55 100644 --- a/scripting/get5/version.sp +++ b/scripting/get5/version.sp @@ -1,6 +1,6 @@ #tryinclude "manual_version.sp" #if !defined PLUGIN_VERSION -#define PLUGIN_VERSION "0.10.2-dev" +#define PLUGIN_VERSION "0.10.3-dev" #endif // This MUST be the latest version in x.y.z semver format followed by -dev. diff --git a/scripting/include/get5.inc b/scripting/include/get5.inc index e4672aa11..3854fed55 100644 --- a/scripting/include/get5.inc +++ b/scripting/include/get5.inc @@ -36,7 +36,7 @@ enum Get5State { Get5State_Warmup, // setup done, waiting for players to ready up Get5State_KnifeRound, // in the knife round Get5State_WaitingForKnifeRoundDecision, // waiting for a .stay/.swap command after the knife - Get5State_GoingLive, // in the lo3 process + Get5State_GoingLive, // counting down to live Get5State_Live, // the match is live Get5State_PostGame, // postgame screen + waiting for GOTV to finish broadcast };