[LazyPrune] youll never imagine, but it wasnt even pruning lol

This commit is contained in:
NotAKidoS 2024-06-05 20:02:30 -05:00
parent 287ea5d0df
commit 29d9f2e8c7
3 changed files with 41 additions and 26 deletions

View file

@ -3,7 +3,6 @@ using ABI_RC.Core.EventSystem;
using ABI_RC.Core.IO; using ABI_RC.Core.IO;
using ABI_RC.Core.Networking.API.Responses; using ABI_RC.Core.Networking.API.Responses;
using ABI_RC.Systems.GameEventSystem; using ABI_RC.Systems.GameEventSystem;
using ABI.CCK.Components;
using HarmonyLib; using HarmonyLib;
using MelonLoader; using MelonLoader;
using UnityEngine; using UnityEngine;
@ -12,21 +11,19 @@ namespace NAK.LazyPrune;
public class LazyPrune : MelonMod 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 int MAX_OBJECTS_UNLOADED_AT_ONCE = 3; // just to alleviate hitch on mass destruction
private const float OBJECT_CACHE_TIMEOUT = 3f; // minutes private const float OBJECT_CACHE_TIMEOUT = 2f; // minutes
private static readonly Dictionary<CVRObjectLoader.LoadedObject, float> _loadedObjects = new(); private static readonly Dictionary<CVRObjectLoader.LoadedObject, float> _loadedObjects = new();
private static string _lastLoadedWorld; private static string _lastLoadedWorld;
private static bool _isInitialized;
public override void OnInitializeMelon() public override void OnInitializeMelon()
{ {
Logger = LoggerInstance; Logger = LoggerInstance;
// every minute, check for objects to prune
SchedulerSystem.AddJob(CheckForObjectsToPrune, 1f, 60f, -1);
// listen for world load // listen for world load
CVRGameEventSystem.World.OnLoad.AddListener(OnWorldLoaded); CVRGameEventSystem.World.OnLoad.AddListener(OnWorldLoaded);
@ -53,11 +50,23 @@ public class LazyPrune : MelonMod
prefix: new HarmonyMethod(typeof(LazyPrune).GetMethod(nameof(OnObjectDestroyed), prefix: new HarmonyMethod(typeof(LazyPrune).GetMethod(nameof(OnObjectDestroyed),
BindingFlags.NonPublic | BindingFlags.Static)) 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 #region Game Events
// fucking dumb
private static void OnInstantiateAvatarFromExistingPrefab(string objectId, string instantiationTarget, private static void OnInstantiateAvatarFromExistingPrefab(string objectId, string instantiationTarget,
GameObject prefabObject, GameObject prefabObject,
ref CVRObjectLoader.LoadedObject loadedObject, string blockReason, AssetManagement.AvatarTags? avatarTags, ref CVRObjectLoader.LoadedObject loadedObject, string blockReason, AssetManagement.AvatarTags? avatarTags,
@ -76,10 +85,16 @@ public class LazyPrune : MelonMod
private static void OnWorldLoaded(string guid) 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) if (_lastLoadedWorld != guid)
ForcePrunePendingObjects(); ForcePrunePendingObjects();
// did you know worlds can spam OnEnabled :)
_lastLoadedWorld = guid; _lastLoadedWorld = guid;
} }
@ -88,13 +103,13 @@ public class LazyPrune : MelonMod
if (___loadedObject == null) if (___loadedObject == null)
{ {
Logger.Error("Avatar/Prop created with no backed LoadedObject."); Logger.Error("Avatar/Prop created with no backed LoadedObject.");
return; // uhh return;
} }
if (_loadedObjects.ContainsKey(___loadedObject)) if (_loadedObjects.ContainsKey(___loadedObject))
{ {
_loadedObjects[___loadedObject] = -1; // mark as ineligible for pruning _loadedObjects[___loadedObject] = -1; // mark as ineligible for pruning
return; // already in cache return;
} }
___loadedObject.refCount++; // increment ref count ___loadedObject.refCount++; // increment ref count
@ -106,11 +121,11 @@ public class LazyPrune : MelonMod
if (loadedObject == null) if (loadedObject == null)
{ {
Logger.Error("Avatar/Prop destroyed with no backed LoadedObject."); Logger.Error("Avatar/Prop destroyed with no backed LoadedObject.");
return; // handled by AttemptPruneObject return;
} }
if (loadedObject.refCount > 1) 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)) if (_loadedObjects.ContainsKey(loadedObject))
_loadedObjects[loadedObject] = Time.time + OBJECT_CACHE_TIMEOUT * 60f; _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--) for (int i = _loadedObjects.Count - 1; i >= 0; i--)
{ {
(CVRObjectLoader.LoadedObject loadedObject, var killTime) = _loadedObjects.ElementAt(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() private static void CheckForObjectsToPrune()
{ {
//int unloaded = 0; int unloaded = 0;
float time = Time.time; float time = Time.time;
for (int i = _loadedObjects.Count - 1; i >= 0; i--) for (int i = _loadedObjects.Count - 1; i >= 0; i--)
{ {
(CVRObjectLoader.LoadedObject loadedObject, var killTime) = _loadedObjects.ElementAt(i); (CVRObjectLoader.LoadedObject loadedObject, var killTime) = _loadedObjects.ElementAt(i);
if (!(killTime < time) || killTime < 0) continue; if (!(killTime < time) || killTime < 0) continue;
AttemptPruneObject(loadedObject); // prune expired objects AttemptPruneObject(loadedObject);
//if (unloaded++ >= MAX_OBJECTS_UNLOADED_AT_ONCE) break; // limit unloads per check if (unloaded++ >= MAX_OBJECTS_UNLOADED_AT_ONCE) break;
} }
} }
@ -153,15 +168,15 @@ public class LazyPrune : MelonMod
if (loadedObject.refCount > 1) if (loadedObject.refCount > 1)
{ {
Logger.Error($"Object {loadedObject.prefabName} has ref count {loadedObject.refCount}, expected 1"); Logger.Error($"Object {loadedObject.prefabName} has ref count {loadedObject.refCount}, expected 1");
_loadedObjects[loadedObject] = -1; // mark as ineligible for pruning ??? _loadedObjects[loadedObject] = -1;
return; // is something somehow holding a reference? return;
} }
Logger.Msg($"Pruning object {loadedObject.prefabName}"); Logger.Msg($"Pruning object {loadedObject.prefabName}");
_loadedObjects.Remove(loadedObject); // remove from cache _loadedObjects.Remove(loadedObject);
loadedObject.refCount--; // decrement ref count loadedObject.refCount--;
if (CVRObjectLoader.Instance != null) // provoke destruction if (CVRObjectLoader.Instance != null)
CVRObjectLoader.Instance.CheckForDestruction(loadedObject); CVRObjectLoader.Instance.CheckForDestruction(loadedObject);
} }

View file

@ -25,6 +25,6 @@ using System.Reflection;
namespace NAK.LazyPrune.Properties; namespace NAK.LazyPrune.Properties;
internal static class AssemblyInfoParams internal static class AssemblyInfoParams
{ {
public const string Version = "1.0.1"; public const string Version = "1.0.2";
public const string Author = "NotAKidoS"; public const string Author = "NotAKidoS";
} }

View file

@ -1,7 +1,7 @@
{ {
"_id": 214, "_id": 214,
"name": "LazyPrune", "name": "LazyPrune",
"modversion": "1.0.1", "modversion": "1.0.2",
"gameversion": "2024r175", "gameversion": "2024r175",
"loaderversion": "0.6.1", "loaderversion": "0.6.1",
"modtype": "Mod", "modtype": "Mod",
@ -17,8 +17,8 @@
"requirements": [ "requirements": [
"None" "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/", "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" "embedcolor": "#1c75f1"
} }