From 38445efae3f5ee218c670439f0ca0202e5bf8a9b Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidOnSteam@users.noreply.github.com> Date: Fri, 24 Mar 2023 18:33:36 -0500 Subject: [PATCH] not really sure --- BadAnimatorFix/BadAnimatorFix.cs | 57 ++++++++++++------------- BadAnimatorFix/BadAnimatorFixManager.cs | 40 ++++++++++------- BadAnimatorFix/HarmonyPatches.cs | 25 ++++------- BadAnimatorFix/Main.cs | 20 ++++----- BadAnimatorFix/format.json | 18 ++++---- 5 files changed, 77 insertions(+), 83 deletions(-) diff --git a/BadAnimatorFix/BadAnimatorFix.cs b/BadAnimatorFix/BadAnimatorFix.cs index 5fb5e76..f1cb6f6 100644 --- a/BadAnimatorFix/BadAnimatorFix.cs +++ b/BadAnimatorFix/BadAnimatorFix.cs @@ -2,50 +2,49 @@ using UnityEngine.Playables; namespace NAK.Melons.BadAnimatorFix; - public class BadAnimatorFix : MonoBehaviour { - private float stateLimit = 50f; - private Animator animator; - private Playable playable; + private const float StateLimit = 20f; - void Start() + private Animator animator; + + private void Start() { animator = GetComponent(); - playable = animator.playableGraph.GetRootPlayable(0); - BadAnimatorFixManager.Add(this); } - void OnDestroy() - { - BadAnimatorFixManager.Remove(this); - } - - public double GetTime() - { - return PlayableExtensions.IsValid(playable) ? PlayableExtensions.GetTime(playable) : -1; - } + private void OnEnable() => BadAnimatorFixManager.Add(this); + private void OnDisable() => BadAnimatorFixManager.Remove(this); public void AttemptRewindAnimator() { + if (animator == null) return; + bool rewound = false; - for (int i = 0; i < animator.layerCount; i++) + for (int layerIndex = 0; layerIndex < animator.layerCount; layerIndex++) { - AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(i); - AnimatorTransitionInfo transitionInfo = animator.GetAnimatorTransitionInfo(i); - // 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 - rewound = true; - float offset = 10f + (stateInfo.normalizedTime % 1f); - animator.Play(stateInfo.fullPathHash, i, offset); + AnimatorStateInfo stateInfo = animator.GetCurrentAnimatorStateInfo(layerIndex); + AnimatorTransitionInfo transitionInfo = animator.GetAnimatorTransitionInfo(layerIndex); + + bool shouldSkipState = !stateInfo.loop || transitionInfo.fullPathHash != 0; + if (shouldSkipState) continue; + + bool shouldRewindState = stateInfo.normalizedTime >= StateLimit; + if (shouldRewindState) + { + float rewindOffset = (stateInfo.normalizedTime % 1f) + 10f; + animator.Play(stateInfo.fullPathHash, layerIndex, rewindOffset); + rewound = true; + } } + if (rewound) { - PlayableExtensions.SetTime(playable, 0); - BadAnimatorFixMod.Logger.Msg($"Rewound animator and playable {animator}."); + var rootPlayable = animator.playableGraph.GetRootPlayable(0); + PlayableExtensions.SetTime(rootPlayable, 0); + + if (BadAnimatorFixMod.EntryLogging.Value) + BadAnimatorFixMod.Logger.Msg($"Rewound animator and playable {animator}."); } } } \ No newline at end of file diff --git a/BadAnimatorFix/BadAnimatorFixManager.cs b/BadAnimatorFix/BadAnimatorFixManager.cs index b4c4d66..c912296 100644 --- a/BadAnimatorFix/BadAnimatorFixManager.cs +++ b/BadAnimatorFix/BadAnimatorFixManager.cs @@ -1,50 +1,58 @@ using ABI_RC.Core.IO; +using UnityEngine; +using UnityEngine.SceneManagement; namespace NAK.Melons.BadAnimatorFix; public static class BadAnimatorFixManager { - public static List badAnimatorFixes = new List(); + 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 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(); + } } } - // Runs every 5 seconds to see if an animator has played longer than PlayableTimeLimit - public static void CheckNextAnimator() + private static void CheckNextAnimator() { if (badAnimatorFixes.Count == 0) return; - - if (currentIndex >= badAnimatorFixes.Count) currentIndex = 0; + currentIndex = (currentIndex + 1) % badAnimatorFixes.Count; BadAnimatorFix currentAnimatorFix = badAnimatorFixes[currentIndex]; - if (currentAnimatorFix.GetTime() > BadAnimatorFixMod.EntryPlayableTimeLimit.Value) - { - currentAnimatorFix.AttemptRewindAnimator(); - } - - 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(BadAnimatorFixManager.CheckNextAnimator), 0f, 5f, -1); + SchedulerSystem.AddJob(new SchedulerSystem.Job(CheckNextAnimator), 0f, checkInterval, -1); } else if (!enable && job != null) { diff --git a/BadAnimatorFix/HarmonyPatches.cs b/BadAnimatorFix/HarmonyPatches.cs index c281f64..6e8b98a 100644 --- a/BadAnimatorFix/HarmonyPatches.cs +++ b/BadAnimatorFix/HarmonyPatches.cs @@ -2,11 +2,10 @@ using ABI_RC.Core.InteractionSystem; using HarmonyLib; using UnityEngine; -using ABI_RC.Core.IO; namespace NAK.Melons.BadAnimatorFix.HarmonyPatches; -internal class AnimatorPatches +internal static class AnimatorPatches { [HarmonyPostfix] [HarmonyPatch(typeof(CVRAvatar), "Start")] @@ -24,24 +23,16 @@ internal class AnimatorPatches AddBadAnimatorFixComponentIfAnimatorExists(__instance.gameObject); } - [HarmonyPostfix] - [HarmonyPatch(typeof(CVRWorld), "Start")] - private static void Post_CVRWorld_Start(CVRWorld __instance) - { - if (!BadAnimatorFixMod.EntryCVRWorld.Value) return; - AddBadAnimatorFixComponentIfAnimatorExists(__instance.gameObject); - } - - //Set QM stuff + // Set QM stuff [HarmonyPostfix] [HarmonyPatch(typeof(CVR_MenuManager), "Start")] - private static void Postfix_CVR_MenuManager_Start(ref CVR_MenuManager __instance, ref GameObject ____leftVrAnchor) + private static void Postfix_CVR_MenuManager_Start(ref CVR_MenuManager __instance) { if (!BadAnimatorFixMod.EntryMenus.Value) return; AddBadAnimatorFixComponentIfAnimatorExists(__instance.gameObject); } - //Set MM stuff + // Set MM stuff [HarmonyPostfix] [HarmonyPatch(typeof(ViewManager), "Start")] private static void Postfix_ViewManager_Start(ref ViewManager __instance) @@ -52,12 +43,12 @@ internal class AnimatorPatches private static void AddBadAnimatorFixComponentIfAnimatorExists(GameObject gameObject) { - //if (!BadAnimatorFixMod.EntryEnabled.Value) return; - Animator[] animators = gameObject.GetComponentsInChildren(); - foreach (Animator animator in animators.Where(a => a.gameObject.GetComponent() == null)) + Animator[] animators = gameObject.GetComponentsInChildren(true); + foreach (Animator animator in animators) { + if (animator.gameObject.GetComponent() != null) continue; if (animator.runtimeAnimatorController != null) animator.gameObject.AddComponent(); } } -} \ No newline at end of file +} diff --git a/BadAnimatorFix/Main.cs b/BadAnimatorFix/Main.cs index eba99a7..a854527 100644 --- a/BadAnimatorFix/Main.cs +++ b/BadAnimatorFix/Main.cs @@ -1,6 +1,4 @@ using MelonLoader; -using System.Collections; -using ABI_RC.Core.Player; namespace NAK.Melons.BadAnimatorFix; @@ -17,30 +15,28 @@ public class BadAnimatorFixMod : MelonMod 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", false, description: "Should BadAnimatorFix run for CVRSpawnable? Requires spawnable reload."); + 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", false, description: "Should BadAnimatorFix run for CVRWorld? Requires world reload."); + 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", false, description: "Should BadAnimatorFix run for QM & MM? Requires game restart."); + CategoryBadAnimatorFix.CreateEntry("Add to Menus", true, description: "Should BadAnimatorFix run for QM & MM? Requires game restart."); - public static readonly MelonPreferences_Entry EntryPlayableTimeLimit = - CategoryBadAnimatorFix.CreateEntry("Playable Time Limit", 600f, description: "How long in seconds can a Playable play for before rewinding its states."); + public static readonly MelonPreferences_Entry EntryLogging = + CategoryBadAnimatorFix.CreateEntry("Logging", 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)); - MelonCoroutines.Start(WaitForLocalPlayer()); } - private IEnumerator WaitForLocalPlayer() + public override void OnSceneWasInitialized(int buildIndex, string sceneName) { - while (PlayerSetup.Instance == null) - yield return null; - BadAnimatorFixManager.ToggleJob(EntryEnabled.Value); + if (!EntryCVRWorld.Value) return; + BadAnimatorFixManager.OnSceneInitialized(sceneName); } private void OnEnabled(object arg1, object arg2) diff --git a/BadAnimatorFix/format.json b/BadAnimatorFix/format.json index 7d05f54..e7f5445 100644 --- a/BadAnimatorFix/format.json +++ b/BadAnimatorFix/format.json @@ -1,23 +1,23 @@ { "_id": -1, - "name": "TrackedControllerFix", + "name": "BadAnimatorFix", "modversion": "1.0.0", "gameversion": "2022r170", "loaderversion": "0.5.7", "modtype": "Mod", "author": "NotAKidoS", - "description": "Allows your controllers to track while the SteamVR overlay is open. This also fixes Quest/Touch controllers feeling slow during fast movements.", + "description": "This mod occasionally rewinds animation states that have loop enabled.\n\nUnity seems to have a weird quirk where animations with loop cause performance issues after running for a long long time.\nYou'll only start to notice this after a few hours to a few days of idling.\n\nDisable loop on your 2-frame toggle clips! They cycle insanely fast and heavily contribute to this issue.", "searchtags": [ - "vr", - "quest", - "controller", - "tracking" + "bad", + "fix", + "animator", + "loop" ], "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidOnSteam/TrackedControllerFix/releases/download/v1.0.0/TrackedControllerFix.dll", - "sourcelink": "https://github.com/NotAKidOnSteam/TrackedControllerFix/", + "downloadlink": "https://github.com/NotAKidOnSteam/BadAnimatorFix/releases/download/v1.0.0/BadAnimatorFix.dll", + "sourcelink": "https://github.com/NotAKidOnSteam/BadAnimatorFix/", "changelog": "Initial Release", - "embedcolor": "3498db" + "embedcolor": "7F3F99" } \ No newline at end of file