From a123b91c4e87d907c3c7d5568f6f8a350cf09964 Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidOnSteam@users.noreply.github.com> Date: Sun, 16 Apr 2023 17:30:20 -0500 Subject: [PATCH] Added BadAnimatorFix manually --- BadAnimatorFix/BadAnimatorFix.cs | 52 +++++++++++++++++ BadAnimatorFix/BadAnimatorFix.csproj | 71 +++++++++++++++++++++++ BadAnimatorFix/BadAnimatorFix.sln | 25 ++++++++ BadAnimatorFix/BadAnimatorFixManager.cs | 67 +++++++++++++++++++++ BadAnimatorFix/HarmonyPatches.cs | 63 ++++++++++++++++++++ BadAnimatorFix/Main.cs | 59 +++++++++++++++++++ BadAnimatorFix/Properties/AssemblyInfo.cs | 30 ++++++++++ BadAnimatorFix/format.json | 23 ++++++++ 8 files changed, 390 insertions(+) create mode 100644 BadAnimatorFix/BadAnimatorFix.cs create mode 100644 BadAnimatorFix/BadAnimatorFix.csproj create mode 100644 BadAnimatorFix/BadAnimatorFix.sln create mode 100644 BadAnimatorFix/BadAnimatorFixManager.cs create mode 100644 BadAnimatorFix/HarmonyPatches.cs create mode 100644 BadAnimatorFix/Main.cs create mode 100644 BadAnimatorFix/Properties/AssemblyInfo.cs create mode 100644 BadAnimatorFix/format.json diff --git a/BadAnimatorFix/BadAnimatorFix.cs b/BadAnimatorFix/BadAnimatorFix.cs new file mode 100644 index 0000000..494ce52 --- /dev/null +++ b/BadAnimatorFix/BadAnimatorFix.cs @@ -0,0 +1,52 @@ +using UnityEngine; +using UnityEngine.Playables; + +namespace NAK.Melons.BadAnimatorFix; + +public class BadAnimatorFix : MonoBehaviour +{ + private const float StateLimit = 20f; + + private Animator animator; + + private void Start() + { + animator = GetComponent(); + } + + private void OnEnable() => BadAnimatorFixManager.Add(this); + private void OnDisable() => BadAnimatorFixManager.Remove(this); + + public void AttemptRewindAnimator() + { + bool rewound = false; + + if (animator != null && animator.isActiveAndEnabled) + { + for (int layerIndex = 0; layerIndex < animator.layerCount; layerIndex++) + { + AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(layerIndex); + AnimatorTransitionInfo transitionInfo = animator.GetAnimatorTransitionInfo(layerIndex); + // Skip if mid-transition + if (transitionInfo.fullPathHash != 0) continue; + // Skip if anim doesn't loop, or hasn't looped enough + if (stateInfo.normalizedTime < StateLimit) continue; + // Rewind state, with 10f as buffer, to account for reasonable use of ExitTime + float offset = 10f + (stateInfo.normalizedTime % 1f); + animator.Play(stateInfo.fullPathHash, layerIndex, offset); + rewound = true; + } + + if (rewound) + { + PlayableExtensions.SetTime(animator.playableGraph.GetRootPlayable(0), 0); + } + } + + if (BadAnimatorFixMod.EntryLogging.Value) + { + string message = rewound ? $"Rewound animator and playable {animator}." : $"Animator did not meet criteria to rewind {animator}."; + BadAnimatorFixMod.Logger.Msg(message); + } + } +} \ No newline at end of file diff --git a/BadAnimatorFix/BadAnimatorFix.csproj b/BadAnimatorFix/BadAnimatorFix.csproj new file mode 100644 index 0000000..040f29a --- /dev/null +++ b/BadAnimatorFix/BadAnimatorFix.csproj @@ -0,0 +1,71 @@ + + + + + netstandard2.1 + enable + latest + false + True + + + + + C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\MelonLoader\0Harmony.dll + + + ..\..\FuckCohtml\FuckMetrics\ManagedLibs\Assembly-CSharp.dll + + + C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Assembly-CSharp-firstpass.dll + + + C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Aura2_Core.dll + + + C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\cohtml.Net.dll + + + C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Cohtml.Runtime.dll + + + C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\MelonLoader\MelonLoader.dll + + + C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\SteamVR.dll + + + C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\Mods\UIExpansionKit.dll + + + C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Unity.Postprocessing.Runtime.dll + + + C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\Unity.TextMeshPro.dll + + + C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.AnimationModule.dll + + + C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.CoreModule.dll + + + C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.InputLegacyModule.dll + + + C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.PhysicsModule.dll + + + C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.VRModule.dll + + + C:\Program Files (x86)\Steam\steamapps\common\ChilloutVR\ChilloutVR_Data\Managed\UnityEngine.XRModule.dll + + + + + + + + + diff --git a/BadAnimatorFix/BadAnimatorFix.sln b/BadAnimatorFix/BadAnimatorFix.sln new file mode 100644 index 0000000..83f9cfb --- /dev/null +++ b/BadAnimatorFix/BadAnimatorFix.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.2.32630.192 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BadAnimatorFix", "BadAnimatorFix.csproj", "{36FB9A18-B03E-45F6-9EF5-A276D2FFFC2B}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {36FB9A18-B03E-45F6-9EF5-A276D2FFFC2B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36FB9A18-B03E-45F6-9EF5-A276D2FFFC2B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36FB9A18-B03E-45F6-9EF5-A276D2FFFC2B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36FB9A18-B03E-45F6-9EF5-A276D2FFFC2B}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {61C931D9-0377-4934-BF85-FCEB9A40547E} + EndGlobalSection +EndGlobal diff --git a/BadAnimatorFix/BadAnimatorFixManager.cs b/BadAnimatorFix/BadAnimatorFixManager.cs new file mode 100644 index 0000000..6497ca4 --- /dev/null +++ b/BadAnimatorFix/BadAnimatorFixManager.cs @@ -0,0 +1,67 @@ +using ABI_RC.Core.IO; +using UnityEngine; +using UnityEngine.SceneManagement; + +namespace NAK.Melons.BadAnimatorFix; + +public static class BadAnimatorFixManager +{ + private static List badAnimatorFixes = new List(); + private static int currentIndex = 0; + private static float checkInterval = 5f; + + public static void Add(BadAnimatorFix bad) + { + if (!badAnimatorFixes.Contains(bad)) + badAnimatorFixes.Add(bad); + } + + public static void Remove(BadAnimatorFix bad) + { + if (badAnimatorFixes.Contains(bad)) + badAnimatorFixes.Remove(bad); + } + + public static void OnPlayerLoaded() + { + ToggleJob(BadAnimatorFixMod.EntryEnabled.Value); + } + + public static void OnSceneInitialized(string sceneName) + { + // Get all the animators in the loaded world + var allAnimators = SceneManager.GetSceneByName(sceneName).GetRootGameObjects() + .SelectMany(x => x.GetComponentsInChildren(true)); + + foreach (var animator in allAnimators) + { + // Ignore objects that have our "fix", this shouldn't be needed but eh + if (!animator.TryGetComponent(out _)) + { + animator.gameObject.AddComponent(); + } + } + } + + private static void CheckNextAnimator() + { + if (badAnimatorFixes.Count == 0) return; + currentIndex = (currentIndex + 1) % badAnimatorFixes.Count; + + BadAnimatorFix currentAnimatorFix = badAnimatorFixes[currentIndex]; + currentAnimatorFix.AttemptRewindAnimator(); + } + + public static void ToggleJob(bool enable) + { + var job = SchedulerSystem.Instance.activeJobs.FirstOrDefault(pair => pair.Job.Method.Name == "CheckNextAnimator").Job; + if (enable && job == null) + { + SchedulerSystem.AddJob(new SchedulerSystem.Job(CheckNextAnimator), 0f, checkInterval, -1); + } + else if (!enable && job != null) + { + SchedulerSystem.RemoveJob(job); + } + } +} diff --git a/BadAnimatorFix/HarmonyPatches.cs b/BadAnimatorFix/HarmonyPatches.cs new file mode 100644 index 0000000..a372d48 --- /dev/null +++ b/BadAnimatorFix/HarmonyPatches.cs @@ -0,0 +1,63 @@ +using ABI.CCK.Components; +using ABI_RC.Core.InteractionSystem; +using ABI_RC.Core.Player; +using HarmonyLib; +using UnityEngine; + +namespace NAK.Melons.BadAnimatorFix.HarmonyPatches; + +internal static class AnimatorPatches +{ + [HarmonyPostfix] + [HarmonyPatch(typeof(PlayerSetup), "Start")] + private static void Postfix_PlayerSetup_Start() + { + BadAnimatorFixManager.OnPlayerLoaded(); + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(CVRAvatar), "Start")] + private static void Postfix_CVRAvatar_Start(CVRAvatar __instance) + { + if (!BadAnimatorFixMod.EntryCVRAvatar.Value) return; + AddBadAnimatorFixComponentIfAnimatorExists(__instance.gameObject); + } + + [HarmonyPostfix] + [HarmonyPatch(typeof(CVRSpawnable), "Start")] + private static void Postfix_CVRSpawnable_Start(CVRSpawnable __instance) + { + if (!BadAnimatorFixMod.EntryCVRSpawnable.Value) return; + AddBadAnimatorFixComponentIfAnimatorExists(__instance.gameObject); + } + + // Set QM stuff + [HarmonyPostfix] + [HarmonyPatch(typeof(CVR_MenuManager), "Start")] + private static void Postfix_CVR_MenuManager_Start(ref CVR_MenuManager __instance) + { + if (!BadAnimatorFixMod.EntryMenus.Value) return; + AddBadAnimatorFixComponentIfAnimatorExists(__instance.gameObject); + } + + // Set MM stuff + [HarmonyPostfix] + [HarmonyPatch(typeof(ViewManager), "Start")] + private static void Postfix_ViewManager_Start(ref ViewManager __instance) + { + if (!BadAnimatorFixMod.EntryMenus.Value) return; + AddBadAnimatorFixComponentIfAnimatorExists(__instance.gameObject); + } + + private static void AddBadAnimatorFixComponentIfAnimatorExists(GameObject gameObject) + { + Animator[] animators = gameObject.GetComponentsInChildren(true); + foreach (Animator animator in animators) + { + if (!animator.TryGetComponent(out _)) + { + animator.gameObject.AddComponent(); + } + } + } +} diff --git a/BadAnimatorFix/Main.cs b/BadAnimatorFix/Main.cs new file mode 100644 index 0000000..c25f91f --- /dev/null +++ b/BadAnimatorFix/Main.cs @@ -0,0 +1,59 @@ +using MelonLoader; + +namespace NAK.Melons.BadAnimatorFix; + +public class BadAnimatorFixMod : MelonMod +{ + internal static MelonLogger.Instance Logger; + public const string SettingsCategory = "BadAnimatorFix"; + public static readonly MelonPreferences_Category CategoryBadAnimatorFix = MelonPreferences.CreateCategory(SettingsCategory); + + public static readonly MelonPreferences_Entry EntryEnabled = + CategoryBadAnimatorFix.CreateEntry("Enabled", true, description: "Toggle BadAnimatorFix entirely. Requires avatar/spawnable/world reload."); + + public static readonly MelonPreferences_Entry EntryCVRAvatar = + CategoryBadAnimatorFix.CreateEntry("Add to CVRAvatar", true, description: "Should BadAnimatorFix run for CVRAvatar? Requires avatar reload."); + + public static readonly MelonPreferences_Entry EntryCVRSpawnable = + CategoryBadAnimatorFix.CreateEntry("Add to CVRSpawnable", true, description: "Should BadAnimatorFix run for CVRSpawnable? Requires spawnable reload."); + + public static readonly MelonPreferences_Entry EntryCVRWorld = + CategoryBadAnimatorFix.CreateEntry("Add to CVRWorld", true, description: "Should BadAnimatorFix run for CVRWorld? Requires world reload."); + + public static readonly MelonPreferences_Entry EntryMenus = + CategoryBadAnimatorFix.CreateEntry("Add to Menus", true, description: "Should BadAnimatorFix run for QM & MM? Requires game restart."); + + public static readonly MelonPreferences_Entry EntryLogging = + CategoryBadAnimatorFix.CreateEntry("Debugging", false, description: "Toggle to log each rewind if successful. Only needed for debugging."); + + public override void OnInitializeMelon() + { + Logger = LoggerInstance; + EntryEnabled.OnEntryValueChangedUntyped.Subscribe(OnEnabled); + ApplyPatches(typeof(HarmonyPatches.AnimatorPatches)); + } + + public override void OnSceneWasInitialized(int buildIndex, string sceneName) + { + if (!EntryCVRWorld.Value) return; + BadAnimatorFixManager.OnSceneInitialized(sceneName); + } + + private void OnEnabled(object arg1, object arg2) + { + BadAnimatorFixManager.ToggleJob(EntryEnabled.Value); + } + + private void ApplyPatches(Type type) + { + try + { + HarmonyInstance.PatchAll(type); + } + catch (Exception e) + { + Logger.Msg($"Failed while patching {type.Name}!"); + Logger.Error(e); + } + } +} \ No newline at end of file diff --git a/BadAnimatorFix/Properties/AssemblyInfo.cs b/BadAnimatorFix/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..4b98794 --- /dev/null +++ b/BadAnimatorFix/Properties/AssemblyInfo.cs @@ -0,0 +1,30 @@ +using MelonLoader; +using NAK.Melons.BadAnimatorFix.Properties; +using System.Reflection; + +[assembly: AssemblyVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyFileVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyInformationalVersion(AssemblyInfoParams.Version)] +[assembly: AssemblyTitle(nameof(NAK.Melons.BadAnimatorFix))] +[assembly: AssemblyCompany(AssemblyInfoParams.Author)] +[assembly: AssemblyProduct(nameof(NAK.Melons.BadAnimatorFix))] + +[assembly: MelonInfo( + typeof(NAK.Melons.BadAnimatorFix.BadAnimatorFixMod), + nameof(NAK.Melons.BadAnimatorFix), + AssemblyInfoParams.Version, + AssemblyInfoParams.Author, + downloadLink: "https://github.com/NotAKidOnSteam/BadAnimatorFix" +)] + +[assembly: MelonGame("Alpha Blend Interactive", "ChilloutVR")] +[assembly: MelonPlatform(MelonPlatformAttribute.CompatiblePlatforms.WINDOWS_X64)] +[assembly: MelonPlatformDomain(MelonPlatformDomainAttribute.CompatibleDomains.MONO)] +[assembly: HarmonyDontPatchAll] + +namespace NAK.Melons.BadAnimatorFix.Properties; +internal static class AssemblyInfoParams +{ + public const string Version = "1.0.0"; + public const string Author = "NotAKidoS"; +} \ No newline at end of file diff --git a/BadAnimatorFix/format.json b/BadAnimatorFix/format.json new file mode 100644 index 0000000..df8fd08 --- /dev/null +++ b/BadAnimatorFix/format.json @@ -0,0 +1,23 @@ +{ + "_id": -1, + "name": "BadAnimatorFix", + "modversion": "1.0.0", + "gameversion": "2022r170", + "loaderversion": "0.5.7", + "modtype": "Mod", + "author": "NotAKidoS", + "description": "This mod occasionally rewinds animation states that have loop enabled.\n\nUnity seems to have a weird quirk where *sometimes* animations with loop cause performance issues after running for a long time.\nYou'll only start to notice this after a few hours to a few days of idling.\n\nIf you don't happen to be AFK for long periods of time, you probably don't need this mod. This issue seems to be primarily caused by one-two frame animation clips meant for toggles with loop needlessly enabled.", + "searchtags": [ + "bad", + "fix", + "animator", + "loop" + ], + "requirements": [ + "None" + ], + "downloadlink": "https://github.com/NotAKidOnSteam/BadAnimatorFix/releases/download/v1.0.0/BadAnimatorFix.dll", + "sourcelink": "https://github.com/NotAKidOnSteam/BadAnimatorFix/", + "changelog": "Initial Release", + "embedcolor": "7F3F99" +} \ No newline at end of file