Skip to content

Commit

Permalink
Mapcycle Bug Fixes
Browse files Browse the repository at this point in the history
- Addresses bug where OnGameEnded fires multiple times, causing the game to crash
  • Loading branch information
data-bomb committed Mar 17, 2024
1 parent 3d67c1f commit ea4dd5b
Showing 1 changed file with 142 additions and 78 deletions.
220 changes: 142 additions & 78 deletions Si_Mapcycle/Si_Mapcycle.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@ You should have received a copy of the GNU General Public License
using System.Collections.Generic;
using SilicaAdminMod;
using System.Linq;
using UnityEngine;

[assembly: MelonInfo(typeof(MapCycleMod), "Mapcycle", "1.4.6", "databomb", "https://github.com/data-bomb/Silica")]
[assembly: MelonInfo(typeof(MapCycleMod), "Mapcycle", "1.4.7", "databomb", "https://github.com/data-bomb/Silica")]
[assembly: MelonGame("Bohemia Interactive", "Silica")]
[assembly: MelonOptionalDependencies("Admin Mod")]

Expand All @@ -49,6 +50,7 @@ public class MapCycleMod : MelonMod
static bool rtvFinalChangeTimerExpired;
static bool rtvChangeLevelTimerExpired;
static int iMapLoadCount;
static bool firedRoundEndOnce;
static int roundsOnSameMap;
static List<Player> rockers = null!;
static string[]? sMapCycle;
Expand All @@ -58,9 +60,9 @@ public class MapCycleMod : MelonMod
static MelonPreferences_Entry<int> Pref_Mapcycle_RoundsBeforeChange = null!;
static MelonPreferences_Entry<int> Pref_Mapcycle_EndgameDelay = null!;

static System.Timers.Timer? EndRoundDelayTimer;
static System.Timers.Timer? InitialPostVoteDelayTimer;
static System.Timers.Timer? FinalPostVoteDelayTimer;
static System.Timers.Timer? Timer_EndRoundDelay;
static System.Timers.Timer? Timer_InitialPostVoteDelay;
static System.Timers.Timer? Timer_FinalPostVoteDelay;

public override void OnInitializeMelon()
{
Expand Down Expand Up @@ -128,7 +130,7 @@ public static void Command_RockTheVote(Player callerPlayer, String args)
}

// is the end-game timer already preparing to switch
if (EndRoundDelayTimer != null && EndRoundDelayTimer.Enabled)
if (Timer_EndRoundDelay != null && Timer_EndRoundDelay.Enabled)
{
HelperMethods.SendChatMessageToPlayer(callerPlayer, HelperMethods.chatPrefix, " Can't rock the vote. Map change already in progress.");
return;
Expand Down Expand Up @@ -203,6 +205,13 @@ public static void RockTheVote_Handler(ChatVoteResults results)
MelonLogger.Msg("Reached vote handler for Rock the Vote. Winning result was: " + results.WinningCommand);
rockers.Clear();

// should we continue or has the game already ended or is in the progress of ending?
if (!GameMode.CurrentGameMode.GameOngoing || ((Timer_EndRoundDelay != null) && (Timer_EndRoundDelay.Enabled)))
{
MelonLogger.Warning("Cancelling Rock the Vote handling. Round is not currently active.");
return;
}

if (sMapCycle == null)
{
MelonLogger.Error("mapcycle is null");
Expand Down Expand Up @@ -240,10 +249,10 @@ public static void RockTheVote_Handler(ChatVoteResults results)
}

double interval = 1000.0;
MapCycleMod.InitialPostVoteDelayTimer = new System.Timers.Timer(interval);
MapCycleMod.InitialPostVoteDelayTimer.Elapsed += new ElapsedEventHandler(HandleTimerRockTheVote);
MapCycleMod.InitialPostVoteDelayTimer.AutoReset = false;
MapCycleMod.InitialPostVoteDelayTimer.Enabled = true;
Timer_InitialPostVoteDelay = new System.Timers.Timer(interval);
Timer_InitialPostVoteDelay.Elapsed += new ElapsedEventHandler(HandleTimerRockTheVote);
Timer_InitialPostVoteDelay.AutoReset = false;
Timer_InitialPostVoteDelay.Enabled = true;
}

public static int MoreRocksNeededForVote()
Expand Down Expand Up @@ -279,6 +288,7 @@ public static void Command_NextMap(Player callerPlayer, String args)
{
if (sMapCycle == null)
{
MelonLogger.Warning("sMapCycle is null. Skipping nextmap handling.");
return;
}

Expand Down Expand Up @@ -356,68 +366,75 @@ private static class ApplyPatch_MusicJukeboxHandlerUpdate
{
private static void Postfix(MusicJukeboxHandler __instance)
{
// check if timers expired
if (rtvChangeLevelTimerExpired)
try
{
rtvChangeLevelTimerExpired = false;

HelperMethods.ReplyToCommand("Rock the vote finished.");
if (rockthevoteWinningMap == "")
// check if timers expired
if (rtvChangeLevelTimerExpired)
{
HelperMethods.ReplyToCommand("Staying on current map.");
return;
}
rtvChangeLevelTimerExpired = false;

roundsOnSameMap = 0;
HelperMethods.ReplyToCommand("Preparing to change map to " + rockthevoteWinningMap + "...");
HelperMethods.ReplyToCommand("Rock the vote finished.");
if (rockthevoteWinningMap == "")
{
HelperMethods.ReplyToCommand("Staying on current map.");
return;
}

double interval = 6000.0;
MapCycleMod.FinalPostVoteDelayTimer = new System.Timers.Timer(interval);
MapCycleMod.FinalPostVoteDelayTimer.Elapsed += new ElapsedEventHandler(HandleTimerRockTheVoteFinal);
MapCycleMod.FinalPostVoteDelayTimer.AutoReset = false;
MapCycleMod.FinalPostVoteDelayTimer.Enabled = true;

return;
}
roundsOnSameMap = 0;
HelperMethods.ReplyToCommand("Preparing to change map to " + rockthevoteWinningMap + "...");

if (rtvFinalChangeTimerExpired)
{
rtvFinalChangeTimerExpired = false;
MelonLogger.Msg("Changing map to " + rockthevoteWinningMap + "...");
NetworkGameServer.LoadLevel(rockthevoteWinningMap, GameMode.CurrentGameMode.GameModeInfo);
return;
}
double interval = 6000.0;
Timer_FinalPostVoteDelay = new System.Timers.Timer(interval);
Timer_FinalPostVoteDelay.Elapsed += new ElapsedEventHandler(HandleTimerRockTheVoteFinal);
Timer_FinalPostVoteDelay.AutoReset = false;
Timer_FinalPostVoteDelay.Enabled = true;

if (sMapCycle == null)
{
return;
}
return;
}

if (bEndRound)
{
// if rtv timers are running then don't call the endround timer
if ((FinalPostVoteDelayTimer != null && FinalPostVoteDelayTimer.Enabled) ||
(InitialPostVoteDelayTimer != null && InitialPostVoteDelayTimer.Enabled))
if (rtvFinalChangeTimerExpired)
{
MelonLogger.Warning("RTV timers running when end round vote would have happened.");
bEndRound = false;
rtvFinalChangeTimerExpired = false;
MelonLogger.Msg("Changing map to " + rockthevoteWinningMap + "...");
NetworkGameServer.LoadLevel(rockthevoteWinningMap, GameMode.CurrentGameMode.GameModeInfo);
return;
}

if (!endroundChangeLevelTimerExpired)
if (sMapCycle == null)
{
return;
}

bEndRound = false;
iMapLoadCount++;
if (bEndRound)
{
// if rtv timers are running then don't call the endround timer
if ((Timer_FinalPostVoteDelay != null && Timer_FinalPostVoteDelay.Enabled) ||
(Timer_InitialPostVoteDelay != null && Timer_InitialPostVoteDelay.Enabled))
{
MelonLogger.Warning("RTV timers running when end round vote would have happened.");
bEndRound = false;
return;
}

if (!endroundChangeLevelTimerExpired)
{
return;
}

bEndRound = false;
iMapLoadCount++;

String sNextMap = sMapCycle[iMapLoadCount % (sMapCycle.Length-1)];
String sNextMap = sMapCycle[iMapLoadCount % (sMapCycle.Length - 1)];

MelonLogger.Msg("Changing map to " + sNextMap + "...");
NetworkGameServer.LoadLevel(sNextMap, GameMode.CurrentGameMode.GameModeInfo);
MelonLogger.Msg("Changing map to " + sNextMap + "...");
NetworkGameServer.LoadLevel(sNextMap, GameMode.CurrentGameMode.GameModeInfo);

return;
return;
}
}
catch (Exception error)
{
HelperMethods.PrintError(error, "Failed to run MusicJukeboxHandler::Update");
}
}
}
Expand All @@ -427,40 +444,87 @@ private static class ApplyPatch_OnGameEnded
{
public static void Postfix(MusicJukeboxHandler __instance, GameMode __0, Team __1)
{
if (sMapCycle == null)
try
{
return;
}
if (firedRoundEndOnce)
{
return;
}

if (bEndRound)
{
return;
}
firedRoundEndOnce = true;

bEndRound = true;
if (sMapCycle == null)
{
MelonLogger.Warning("sMapCycle is null. Skipping end-round routines.");
return;
}

roundsOnSameMap++;
if (Pref_Mapcycle_RoundsBeforeChange.Value > roundsOnSameMap)
roundsOnSameMap++;
if (Pref_Mapcycle_RoundsBeforeChange.Value > roundsOnSameMap)
{
int roundsLeft = Pref_Mapcycle_RoundsBeforeChange.Value - roundsOnSameMap;
HelperMethods.ReplyToCommand("Current map will change after " + roundsLeft.ToString() + " more round" + (roundsLeft == 1 ? "." : "s."));
return;
}

if (Timer_EndRoundDelay != null && Timer_EndRoundDelay.Enabled)
{
MelonLogger.Warning("End round delay timer already started.");
return;
}

if (bEndRound)
{
MelonLogger.Warning("End round status already set.");
return;
}

bEndRound = true;

// if any rock-the-vote actions are pending then they should be cancelled
if (Timer_FinalPostVoteDelay != null && Timer_FinalPostVoteDelay.Enabled)
{
Timer_FinalPostVoteDelay.Stop();

MelonLogger.Msg("Game ended while final RTV timer was in progress. Forcing timer to expire.");
}

if (Timer_InitialPostVoteDelay != null && Timer_InitialPostVoteDelay.Enabled)
{
Timer_InitialPostVoteDelay.Stop();

MelonLogger.Msg("Game ended while initial RTV timer was in progress. Forcing timer to expire.");
}

HelperMethods.ReplyToCommand("Preparing to change map to " + sMapCycle[(iMapLoadCount + 1) % (sMapCycle.Length - 1)] + "....");
endroundChangeLevelTimerExpired = false;

double interval = Pref_Mapcycle_EndgameDelay.Value * 1000.0f;
Timer_EndRoundDelay = new System.Timers.Timer(interval);
Timer_EndRoundDelay.Elapsed += new ElapsedEventHandler(HandleTimerChangeLevel);
Timer_EndRoundDelay.AutoReset = false;
Timer_EndRoundDelay.Enabled = true;
}
catch (Exception error)
{
int roundsLeft = Pref_Mapcycle_RoundsBeforeChange.Value - roundsOnSameMap;
HelperMethods.ReplyToCommand("Current map will change after " + roundsLeft.ToString() + " more round" + (roundsLeft == 1 ? "." : "s."));
return;
HelperMethods.PrintError(error, "Failed to run MusicJukeboxHandler::OnGameEnded");
}
}
}

if (EndRoundDelayTimer != null && EndRoundDelayTimer.Enabled)
[HarmonyPatch(typeof(MusicJukeboxHandler), nameof(MusicJukeboxHandler.OnGameStarted))]
private static class ApplyPatch_OnGameStarted
{
public static void Prefix(MusicJukeboxHandler __instance, GameMode __0)
{
try
{
MelonLogger.Warning("End round delay timer already started.");
return;
firedRoundEndOnce = false;
}
catch (Exception error)
{
HelperMethods.PrintError(error, "Failed to run MusicJukeboxHandler::OnGameStarted");
}

HelperMethods.ReplyToCommand("Preparing to change map to " + sMapCycle[(iMapLoadCount + 1) % (sMapCycle.Length - 1)] + "....");
endroundChangeLevelTimerExpired = false;

double interval = Pref_Mapcycle_EndgameDelay.Value * 1000.0f;
MapCycleMod.EndRoundDelayTimer = new System.Timers.Timer(interval);
MapCycleMod.EndRoundDelayTimer.Elapsed += new ElapsedEventHandler(HandleTimerChangeLevel);
MapCycleMod.EndRoundDelayTimer.AutoReset = false;
MapCycleMod.EndRoundDelayTimer.Enabled = true;
}
}
}
Expand Down

0 comments on commit ea4dd5b

Please sign in to comment.