From 29d9f2e8c707f13ba9fbb6d01c5e9a55b488f78a Mon Sep 17 00:00:00 2001 From: NotAKidoS <37721153+NotAKidOnSteam@users.noreply.github.com> Date: Wed, 5 Jun 2024 20:02:30 -0500 Subject: [PATCH] [LazyPrune] youll never imagine, but it wasnt even pruning lol --- LazyPrune/Main.cs | 59 +++++++++++++++++----------- LazyPrune/Properties/AssemblyInfo.cs | 2 +- LazyPrune/format.json | 6 +-- 3 files changed, 41 insertions(+), 26 deletions(-) diff --git a/LazyPrune/Main.cs b/LazyPrune/Main.cs index b8520ef..b8a9516 100644 --- a/LazyPrune/Main.cs +++ b/LazyPrune/Main.cs @@ -3,7 +3,6 @@ using ABI_RC.Core.EventSystem; using ABI_RC.Core.IO; using ABI_RC.Core.Networking.API.Responses; using ABI_RC.Systems.GameEventSystem; -using ABI.CCK.Components; using HarmonyLib; using MelonLoader; using UnityEngine; @@ -12,21 +11,19 @@ namespace NAK.LazyPrune; public class LazyPrune : MelonMod { - internal static MelonLogger.Instance Logger; + private static MelonLogger.Instance Logger; - //private const int MAX_OBJECTS_UNLOADED_AT_ONCE = 5; // just to alleviate hitch on mass destruction - private const float OBJECT_CACHE_TIMEOUT = 3f; // minutes + private const int MAX_OBJECTS_UNLOADED_AT_ONCE = 3; // just to alleviate hitch on mass destruction + private const float OBJECT_CACHE_TIMEOUT = 2f; // minutes private static readonly Dictionary _loadedObjects = new(); private static string _lastLoadedWorld; + private static bool _isInitialized; public override void OnInitializeMelon() { Logger = LoggerInstance; - // every minute, check for objects to prune - SchedulerSystem.AddJob(CheckForObjectsToPrune, 1f, 60f, -1); - // listen for world load CVRGameEventSystem.World.OnLoad.AddListener(OnWorldLoaded); @@ -53,11 +50,23 @@ public class LazyPrune : MelonMod prefix: new HarmonyMethod(typeof(LazyPrune).GetMethod(nameof(OnObjectDestroyed), BindingFlags.NonPublic | BindingFlags.Static)) ); + + // hook into Unity's low memory warning + Application.lowMemory += OnLowMemory; } + #region Application Events + + private static void OnLowMemory() + { + Logger.Warning("Low memory warning received! Forcing prune of all pending objects."); + ForcePrunePendingObjects(); + } + + #endregion Application Events + #region Game Events - // fucking dumb private static void OnInstantiateAvatarFromExistingPrefab(string objectId, string instantiationTarget, GameObject prefabObject, ref CVRObjectLoader.LoadedObject loadedObject, string blockReason, AssetManagement.AvatarTags? avatarTags, @@ -76,10 +85,16 @@ public class LazyPrune : MelonMod private static void OnWorldLoaded(string guid) { + if (!_isInitialized) + { + // every minute, check for objects to prune + SchedulerSystem.AddJob(CheckForObjectsToPrune, 1f, 10f, -1); + _isInitialized = true; + } + if (_lastLoadedWorld != guid) ForcePrunePendingObjects(); - // did you know worlds can spam OnEnabled :) _lastLoadedWorld = guid; } @@ -88,13 +103,13 @@ public class LazyPrune : MelonMod if (___loadedObject == null) { Logger.Error("Avatar/Prop created with no backed LoadedObject."); - return; // uhh + return; } if (_loadedObjects.ContainsKey(___loadedObject)) { _loadedObjects[___loadedObject] = -1; // mark as ineligible for pruning - return; // already in cache + return; } ___loadedObject.refCount++; // increment ref count @@ -106,11 +121,11 @@ public class LazyPrune : MelonMod if (loadedObject == null) { Logger.Error("Avatar/Prop destroyed with no backed LoadedObject."); - return; // handled by AttemptPruneObject + return; } if (loadedObject.refCount > 1) - return; // we added our own ref, so begin death count at 1 (we are the last one) + return; if (_loadedObjects.ContainsKey(loadedObject)) _loadedObjects[loadedObject] = Time.time + OBJECT_CACHE_TIMEOUT * 60f; @@ -125,20 +140,20 @@ public class LazyPrune : MelonMod for (int i = _loadedObjects.Count - 1; i >= 0; i--) { (CVRObjectLoader.LoadedObject loadedObject, var killTime) = _loadedObjects.ElementAt(i); - if (killTime > 0) AttemptPruneObject(loadedObject); // prune all pending objects + if (killTime > 0) AttemptPruneObject(loadedObject); } } private static void CheckForObjectsToPrune() { - //int unloaded = 0; + int unloaded = 0; float time = Time.time; for (int i = _loadedObjects.Count - 1; i >= 0; i--) { (CVRObjectLoader.LoadedObject loadedObject, var killTime) = _loadedObjects.ElementAt(i); if (!(killTime < time) || killTime < 0) continue; - AttemptPruneObject(loadedObject); // prune expired objects - //if (unloaded++ >= MAX_OBJECTS_UNLOADED_AT_ONCE) break; // limit unloads per check + AttemptPruneObject(loadedObject); + if (unloaded++ >= MAX_OBJECTS_UNLOADED_AT_ONCE) break; } } @@ -153,15 +168,15 @@ public class LazyPrune : MelonMod if (loadedObject.refCount > 1) { Logger.Error($"Object {loadedObject.prefabName} has ref count {loadedObject.refCount}, expected 1"); - _loadedObjects[loadedObject] = -1; // mark as ineligible for pruning ??? - return; // is something somehow holding a reference? + _loadedObjects[loadedObject] = -1; + return; } Logger.Msg($"Pruning object {loadedObject.prefabName}"); - _loadedObjects.Remove(loadedObject); // remove from cache + _loadedObjects.Remove(loadedObject); - loadedObject.refCount--; // decrement ref count - if (CVRObjectLoader.Instance != null) // provoke destruction + loadedObject.refCount--; + if (CVRObjectLoader.Instance != null) CVRObjectLoader.Instance.CheckForDestruction(loadedObject); } diff --git a/LazyPrune/Properties/AssemblyInfo.cs b/LazyPrune/Properties/AssemblyInfo.cs index cbb31db..123eb2a 100644 --- a/LazyPrune/Properties/AssemblyInfo.cs +++ b/LazyPrune/Properties/AssemblyInfo.cs @@ -25,6 +25,6 @@ using System.Reflection; namespace NAK.LazyPrune.Properties; internal static class AssemblyInfoParams { - public const string Version = "1.0.1"; + public const string Version = "1.0.2"; public const string Author = "NotAKidoS"; } \ No newline at end of file diff --git a/LazyPrune/format.json b/LazyPrune/format.json index acf299c..7072b9a 100644 --- a/LazyPrune/format.json +++ b/LazyPrune/format.json @@ -1,7 +1,7 @@ { "_id": 214, "name": "LazyPrune", - "modversion": "1.0.1", + "modversion": "1.0.2", "gameversion": "2024r175", "loaderversion": "0.6.1", "modtype": "Mod", @@ -17,8 +17,8 @@ "requirements": [ "None" ], - "downloadlink": "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/download/r31/LazyPrune.dll", + "downloadlink": "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/releases/download/r32/LazyPrune.dll", "sourcelink": "https://github.com/NotAKidOnSteam/NAK_CVR_Mods/tree/main/LazyPrune/", - "changelog": "- Fixed killtime check, would needlessly check known non-eligible objects for pruning.\n- Moved away from using GameEventSystem as it proved unreliable for tracking remote Avatar destruction, now patching Object Loader directly as loadedObject is not assigned when object wrappers are enabled.", + "changelog": "- Fixed killtime check, would needlessly check known non-eligible objects for pruning.\n- Moved away from using GameEventSystem as it proved unreliable for tracking remote Avatar destruction, now patching Object Loader directly as loadedObject is not assigned when object wrappers are enabled.\n- Fixed scheduled prune job being nuked as it was created before scene load.", "embedcolor": "#1c75f1" } \ No newline at end of file