From 4e3fd80d80d0a1ceb5098ad2cb81365ad8f1f594 Mon Sep 17 00:00:00 2001 From: SokyranTheDragon <36712560+SokyranTheDragon@users.noreply.github.com> Date: Sun, 9 Jun 2024 02:04:41 +0200 Subject: [PATCH] Fix issues caused by HugsLib long events (#450) I've moved the long event patch from Perspective: Ores to `PatchingUtilities` and applied it to HugsLib as well. --- Source/Mods/HugsLib.cs | 16 +++++++++ Source/Mods/PerspectiveOres.cs | 13 ++----- Source/PatchingUtilities.cs | 64 ++++++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+), 11 deletions(-) create mode 100644 Source/Mods/HugsLib.cs diff --git a/Source/Mods/HugsLib.cs b/Source/Mods/HugsLib.cs new file mode 100644 index 00000000..b67959f0 --- /dev/null +++ b/Source/Mods/HugsLib.cs @@ -0,0 +1,16 @@ +using Verse; + +namespace Multiplayer.Compat; + +/// HugsLib by UnlimitedHugs +/// +/// +[MpCompatFor("UnlimitedHugs.HugsLib")] +public class HugsLib +{ + public HugsLib(ModContentPack mod) + { + // Stop the long event from running when it could break stuff. + PatchingUtilities.CancelCallIfLongEventsAreUnsafe("HugsLib.HugsLibController:OnMapInitFinalized"); + } +} \ No newline at end of file diff --git a/Source/Mods/PerspectiveOres.cs b/Source/Mods/PerspectiveOres.cs index 9d46e1f3..bbe61db8 100644 --- a/Source/Mods/PerspectiveOres.cs +++ b/Source/Mods/PerspectiveOres.cs @@ -10,28 +10,19 @@ namespace Multiplayer.Compat [MpCompatFor("Owlchemist.PerspectiveOres")] public class PerspectiveOres { - private static bool allowedToRun = true; - public PerspectiveOres(ModContentPack mod) { - // Patch to MP to mark when to stop long events Perspective: Ores - MpCompat.harmony.Patch(AccessTools.DeclaredMethod("Multiplayer.Client.SaveLoad:LoadInMainThread"), - prefix: new HarmonyMethod(typeof(PerspectiveOres), nameof(SetDisallowedToRun)), - finalizer: new HarmonyMethod(typeof(PerspectiveOres), nameof(SetAllowedToRun))); + PatchingUtilities.PatchLongEventMarkers(); // Stop the "ProcessMap" from running when it could break stuff. MpCompat.harmony.Patch(AccessTools.DeclaredMethod("PerspectiveOres.PerspectiveOresSetup:ProcessMap"), prefix: new HarmonyMethod(typeof(PerspectiveOres), nameof(StopSecondHostCall))); } - private static void SetDisallowedToRun() => allowedToRun = false; - - private static void SetAllowedToRun() => allowedToRun = true; - private static bool StopSecondHostCall(bool reset) { // Stop the host from running it for the second time. It messes stuff up due to the place it's called from. - if (!allowedToRun) + if (!PatchingUtilities.AllowedToRunLongEvents) return false; // Prevent it from being called due to settings change, could mess stuff up. if (reset && MP.IsInMultiplayer) diff --git a/Source/PatchingUtilities.cs b/Source/PatchingUtilities.cs index a547220e..736c3675 100644 --- a/Source/PatchingUtilities.cs +++ b/Source/PatchingUtilities.cs @@ -917,5 +917,69 @@ public static void InitializeTimestampFixer() } #endregion + + #region Unsafe long event cancelling + + public static bool AllowedToRunLongEvents { get; private set; } = true; + private static bool longEventMarkerPatchActive = false; + + /// Patches the methods to cancel the call if it's unsafe to start long events + /// Names (type colon name) of the methods to patch + public static void CancelCallIfLongEventsAreUnsafe(params string[] methodNames) => CancelCallIfLongEventsAreUnsafe(methodNames as IEnumerable); + + /// Patches the methods to cancel the call if it's unsafe to start long events + /// Names (type colon name) of the methods to patch + public static void CancelCallIfLongEventsAreUnsafe(IEnumerable methodNames) + => CancelCallIfLongEventsAreUnsafe(methodNames + .Select(m => + { + var method = AccessTools.DeclaredMethod(m) ?? AccessTools.Method(m); + if (method == null) + Log.Error($"({nameof(PatchingUtilities)}) Could not find method {m}"); + return method; + }) + .Where(m => m != null)); + + /// Patches the methods to cancel the call if it's unsafe to start long events + /// Methods to patch + public static void CancelCallIfLongEventsAreUnsafe(params MethodBase[] methods) => CancelCallIfLongEventsAreUnsafe(methods as IEnumerable); + + /// Patches the methods to cancel the call if it's unsafe to start long events + /// Methods to patch + public static void CancelCallIfLongEventsAreUnsafe(IEnumerable methods) + { + PatchLongEventMarkers(); + + var patch = new HarmonyMethod(typeof(PatchingUtilities), nameof(StopSecondHostCall)); + foreach (var method in methods) + MpCompat.harmony.Patch(method, prefix: patch); + } + + public static void PatchLongEventMarkers() + { + if (longEventMarkerPatchActive) + return; + + longEventMarkerPatchActive = true; + + // Patch to MP to mark when to stop long events Perspective: Ores + MpCompat.harmony.Patch(AccessTools.DeclaredMethod("Multiplayer.Client.SaveLoad:LoadInMainThread"), + prefix: new HarmonyMethod(typeof(PatchingUtilities), nameof(SetDisallowedToRun)), + finalizer: new HarmonyMethod(typeof(PatchingUtilities), nameof(SetAllowedToRun))); + } + + private static void SetDisallowedToRun() => AllowedToRunLongEvents = false; + + private static void SetAllowedToRun() => AllowedToRunLongEvents = true; + + private static bool StopSecondHostCall() + { + // Stop the host from running it for the second time. It messes stuff up due to the place it's called from. + // It can cause the host to start generating non-local IDs when they should generate local ones. This causes + // the host to assign higher IDs than all the connected clients. + return AllowedToRunLongEvents; + } + + #endregion } } \ No newline at end of file