From 8b7f9211c9026aa10888f25bc2ff0004125a2d57 Mon Sep 17 00:00:00 2001 From: Mhburg Date: Mon, 24 Aug 2020 15:33:26 -0400 Subject: [PATCH] Add ActionSemaphore and pooling patches for VisibilityCache --- .../LowVisibility/LowVisibility.csproj | 18 ++++ .../LowVisibility/Object/ActionSemaphore.cs | 64 ++++++++++++++ .../Object/VisibilityCacheGate.cs | 86 +++++++++++++++++++ .../Patch/AbstractActorPatches.cs | 28 ++++++ .../Patch/CombatGameStatePatches.cs | 6 ++ .../Patch/VisibilityCachePatches.cs | 78 +++++++++++------ 6 files changed, 253 insertions(+), 27 deletions(-) create mode 100644 LowVisibility/LowVisibility/Object/ActionSemaphore.cs create mode 100644 LowVisibility/LowVisibility/Object/VisibilityCacheGate.cs diff --git a/LowVisibility/LowVisibility/LowVisibility.csproj b/LowVisibility/LowVisibility/LowVisibility.csproj index c0a37ec..44496e8 100644 --- a/LowVisibility/LowVisibility/LowVisibility.csproj +++ b/LowVisibility/LowVisibility/LowVisibility.csproj @@ -37,34 +37,43 @@ E:\steam\SteamApps\common\BATTLETECH\BattleTech_Data\Managed\0Harmony.dll + False False E:\steam\SteamApps\common\BATTLETECH\BattleTech_Data\Managed\Assembly-CSharp.dll + False False E:\steam\SteamApps\common\BATTLETECH\BattleTech_Data\Managed\Assembly-CSharp-firstpass.dll + False E:\steam\SteamApps\common\BATTLETECH\Mods\CustomActivatableEquipment\CustomActivatableEquipment.dll + False E:\steam\SteamApps\common\BATTLETECH\Mods\CustomAmmoCategories\CustomAmmoCategories.dll + False E:\steam\SteamApps\common\BATTLETECH\Mods\CustomComponents\CustomComponents.dll + False False E:\steam\SteamApps\common\BATTLETECH\Mods\IRBTModUtils\IRBTModUtils.dll + False E:\steam\SteamApps\common\BATTLETECH\Mods\MechEngineer\MechEngineer.dll + False False E:\steam\SteamApps\common\BATTLETECH\BattleTech_Data\Managed\Newtonsoft.Json.dll + False @@ -74,26 +83,33 @@ E:\steam\SteamApps\common\BATTLETECH\BattleTech_Data\Managed\Unity.TextMeshPro.dll + False False E:\steam\SteamApps\common\BATTLETECH\BattleTech_Data\Managed\UnityEngine.dll + False E:\steam\SteamApps\common\BATTLETECH\BattleTech_Data\Managed\UnityEngine.AssetBundleModule.dll + False E:\steam\SteamApps\common\BATTLETECH\BattleTech_Data\Managed\UnityEngine.CoreModule.dll + False E:\steam\SteamApps\common\BATTLETECH\BattleTech_Data\Managed\UnityEngine.ParticleSystemModule.dll + False E:\steam\SteamApps\common\BATTLETECH\BattleTech_Data\Managed\UnityEngine.PhysicsModule.dll + False False E:\steam\SteamApps\common\BATTLETECH\BattleTech_Data\Managed\UnityEngine.UI.dll + False @@ -104,11 +120,13 @@ + + diff --git a/LowVisibility/LowVisibility/Object/ActionSemaphore.cs b/LowVisibility/LowVisibility/Object/ActionSemaphore.cs new file mode 100644 index 0000000..9feffb6 --- /dev/null +++ b/LowVisibility/LowVisibility/Object/ActionSemaphore.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LowVisibility.Object +{ + public abstract class ActionSemaphore + { + protected Func shouldTakeaction; + + protected Action actionToTake; + + protected int counter; + + protected ActionSemaphore(Func shouldTakeaction, Action actionToTake) + { + this.shouldTakeaction = shouldTakeaction; + this.actionToTake = actionToTake; + } + + public int Counter => counter; + + public virtual void ResetSemaphore() { + counter = 0; + } + + public virtual void Enter() + { + ++counter; + if (shouldTakeaction()) { + actionToTake(); + } + } + + public virtual void Exit() + { + --counter; + counter = counter < 0 ? 0 : counter; + if (shouldTakeaction()) { + actionToTake(); + } + } + + public virtual void ResetHard() { + counter = 0; + if (shouldTakeaction()) { + actionToTake(); + } + } + + public virtual void ResetSoft() { + while (counter-- > 0) { + if (shouldTakeaction()) { + actionToTake(); + } + } + + if (counter < 0) + ResetHard(); + } + } +} diff --git a/LowVisibility/LowVisibility/Object/VisibilityCacheGate.cs b/LowVisibility/LowVisibility/Object/VisibilityCacheGate.cs new file mode 100644 index 0000000..ce5bba2 --- /dev/null +++ b/LowVisibility/LowVisibility/Object/VisibilityCacheGate.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using BattleTech; + +namespace LowVisibility.Object +{ + /// + /// Pool all requests to rebuild the visibility cache into one place. + /// + public class VisibilityCacheGate : ActionSemaphore + { + private static VisibilityCacheGate cacheGate = new VisibilityCacheGate(); + + private readonly HashSet actors = new HashSet(); + + private VisibilityCacheGate() + : base(null, null) + { + shouldTakeaction = () => counter == 0; + actionToTake = () => + { + List combatants = UnityGameInstance.BattleTechGame.Combat.GetAllLivingCombatants(); + List uniDirectionalList = new List(); + List biDirectionalList = new List(); + + foreach (ICombatant combatant in combatants) + { + if (actors.Contains(combatant)) + { + uniDirectionalList.Add(combatant); + } + else + { + biDirectionalList.Add(combatant); + } + } + + foreach (AbstractActor actor in actors) + { + actor.VisibilityCache?.UpdateCacheReciprocal(biDirectionalList); + actor.VisibilityCache?.RebuildCache(uniDirectionalList); + } + + actors.Clear(); + }; + } + + public static bool Active => cacheGate.counter > 0; + + public static int GetCounter => cacheGate.counter; + + public static void EnterGate() + { + cacheGate.Enter(); + } + + public static void ExitGate() + { + cacheGate.Exit(); + } + + public static void ExitAll() { + cacheGate.ResetHard(); + } + + public static void Reset() { + cacheGate.ResetSemaphore(); + } + + public static void AddActorToRefresh(AbstractActor actor) { + cacheGate.actors.Add(actor); + } + + #region Overrides of ActionSemaphore + + public override void ResetSemaphore() { + base.ResetSemaphore(); + actors.Clear(); + } + + #endregion + } +} diff --git a/LowVisibility/LowVisibility/Patch/AbstractActorPatches.cs b/LowVisibility/LowVisibility/Patch/AbstractActorPatches.cs index a12fde5..6467b32 100644 --- a/LowVisibility/LowVisibility/Patch/AbstractActorPatches.cs +++ b/LowVisibility/LowVisibility/Patch/AbstractActorPatches.cs @@ -278,5 +278,33 @@ public static void Prefix(AbstractActor __instance) { } } + [HarmonyPatch(typeof(AbstractActor), nameof(AbstractActor.HandleDeath))] + public static class AbstractActor_HandleDeath { + + private static int counter = 0; + + public static bool GateActive = false; + + [HarmonyPriority(900)] + public static void Prefix(AbstractActor __instance) { + VisibilityCacheGate.EnterGate(); + GateActive = true; + counter = VisibilityCacheGate.GetCounter; + } + + [HarmonyPriority(0)] + public static void Postfix(AbstractActor __instance) { + VisibilityCacheGate.ExitGate(); + GateActive = false; + + int exitCounter = VisibilityCacheGate.GetCounter; + if (exitCounter < counter) { + Mod.Log.Debug?.Write($"Reset or unsymmetrical larger number of ExitGate() are call."); + } + else if (exitCounter > counter) { + Mod.Log.Error?.Write($"Fewer calls to ExitGate() than EnterGate()."); + } + } + } } diff --git a/LowVisibility/LowVisibility/Patch/CombatGameStatePatches.cs b/LowVisibility/LowVisibility/Patch/CombatGameStatePatches.cs index e96ff4d..19e7be5 100644 --- a/LowVisibility/LowVisibility/Patch/CombatGameStatePatches.cs +++ b/LowVisibility/LowVisibility/Patch/CombatGameStatePatches.cs @@ -45,6 +45,7 @@ static void Postfix() Mod.Log.Trace?.Write("CGS:OCGD - entered."); ModState.Reset(); + VisibilityCacheGate.Reset(); } } @@ -57,6 +58,11 @@ public static void Postfix() Mod.Log.Error?.Write($"Something has gone wrong in refreshing visibility cache, resetting."); EWState.ResetCache(); } + + if (AbstractActor_HandleDeath.GateActive) { + Mod.Log.Error?.Write($"Something has gone wrong in handling actor death, resetting VisibilityCacheGate."); + VisibilityCacheGate.ExitAll(); + } } } } diff --git a/LowVisibility/LowVisibility/Patch/VisibilityCachePatches.cs b/LowVisibility/LowVisibility/Patch/VisibilityCachePatches.cs index 3ba360e..2c27c13 100644 --- a/LowVisibility/LowVisibility/Patch/VisibilityCachePatches.cs +++ b/LowVisibility/LowVisibility/Patch/VisibilityCachePatches.cs @@ -9,40 +9,64 @@ namespace LowVisibility.Patch { - [HarmonyPatch(typeof(VisibilityCache), nameof(VisibilityCache.RebuildCache))] - public static class VisibilityCache_RebuildCache + public static class VisibilityCachePatches { - // Lowest priority - [HarmonyPriority(0)] - public static void Prefix() - { - EWState.InBatchProcess = true; - EWState.EWStateCache.Clear(); - } + public delegate AbstractActor OwningActor(VisibilityCache cache); - // Highest priority - [HarmonyPriority(900)] - public static void Postfix() - { - EWState.InBatchProcess = false; - } - } + public static OwningActor GetOwningActor = + (OwningActor) Delegate.CreateDelegate(typeof(OwningActor), typeof(VisibilityCache).GetProperty("OwningActor", AccessTools.all).GetMethod); - [HarmonyPatch(typeof(VisibilityCache), nameof(VisibilityCache.UpdateCacheReciprocal))] - public static class VisibilityCache_UpdateCacheReciprocal - { - // Lowest priority - [HarmonyPriority(0)] - public static void Prefix() + [HarmonyPatch(typeof(VisibilityCache), nameof(VisibilityCache.RebuildCache))] + public static class VisibilityCache_RebuildCache { - EWState.InBatchProcess = true; - EWState.EWStateCache.Clear(); + // Lowest priority + [HarmonyPriority(0)] + public static bool Prefix(VisibilityCache __instance) + { + EWState.InBatchProcess = true; + EWState.EWStateCache.Clear(); + + if (VisibilityCacheGate.Active) + { + VisibilityCacheGate.AddActorToRefresh(GetOwningActor(__instance)); + return false; + } + + return true; + } + + // Highest priority + [HarmonyPriority(900)] + public static void Postfix() + { + EWState.InBatchProcess = false; + } } - [HarmonyPriority(900)] - public static void Postfix() + [HarmonyPatch(typeof(VisibilityCache), nameof(VisibilityCache.UpdateCacheReciprocal))] + public static class VisibilityCache_UpdateCacheReciprocal { - EWState.InBatchProcess = false; + // Lowest priority + [HarmonyPriority(0)] + public static bool Prefix(VisibilityCache __instance) + { + EWState.InBatchProcess = true; + EWState.EWStateCache.Clear(); + + if (VisibilityCacheGate.Active) + { + VisibilityCacheGate.AddActorToRefresh(GetOwningActor(__instance)); + return false; + } + + return true; + } + + [HarmonyPriority(900)] + public static void Postfix() + { + EWState.InBatchProcess = false; + } } } }