Skip to content

Commit

Permalink
Fix issues caused by HugsLib long events (#450)
Browse files Browse the repository at this point in the history
I've moved the long event patch from Perspective: Ores to `PatchingUtilities` and applied it to HugsLib as well.
  • Loading branch information
SokyranTheDragon authored Jun 9, 2024
1 parent 1be8ae9 commit 4e3fd80
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 11 deletions.
16 changes: 16 additions & 0 deletions Source/Mods/HugsLib.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Verse;

namespace Multiplayer.Compat;

/// <summary>HugsLib by UnlimitedHugs</summary>
/// <see href="https://github.com/UnlimitedHugs/RimworldHugsLib"/>
/// <see href="https://steamcommunity.com/sharedfiles/filedetails/?id=818773962"/>
[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");
}
}
13 changes: 2 additions & 11 deletions Source/Mods/PerspectiveOres.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
64 changes: 64 additions & 0 deletions Source/PatchingUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <summary>Patches the methods to cancel the call if it's unsafe to start long events</summary>
/// <param name="methodNames">Names (type colon name) of the methods to patch</param>
public static void CancelCallIfLongEventsAreUnsafe(params string[] methodNames) => CancelCallIfLongEventsAreUnsafe(methodNames as IEnumerable<string>);

/// <summary>Patches the methods to cancel the call if it's unsafe to start long events</summary>
/// <param name="methodNames">Names (type colon name) of the methods to patch</param>
public static void CancelCallIfLongEventsAreUnsafe(IEnumerable<string> 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));

/// <summary>Patches the methods to cancel the call if it's unsafe to start long events</summary>
/// <param name="methods">Methods to patch</param>
public static void CancelCallIfLongEventsAreUnsafe(params MethodBase[] methods) => CancelCallIfLongEventsAreUnsafe(methods as IEnumerable<MethodBase>);

/// <summary>Patches the methods to cancel the call if it's unsafe to start long events</summary>
/// <param name="methods">Methods to patch</param>
public static void CancelCallIfLongEventsAreUnsafe(IEnumerable<MethodBase> 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
}
}

0 comments on commit 4e3fd80

Please sign in to comment.