Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/API/GregAPI.cs
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@ public static uint GetRackCount()
{
try
{
var racks = UnityEngine.Object.FindObjectsOfType<Il2Cpp.Rack>();
return racks != null ? (uint)racks.Count : 0u;
// ⚡ Bolt: Performance Optimization - Replaced O(N) FindObjectsOfType with O(1) cache
return gregCore.GameLayer.Patches.Hardware.RackPatch.GetRackCount();
}
catch { return 0u; }
}
Expand Down
4 changes: 2 additions & 2 deletions src/Compatibility/DataCenterModLoader/GameHooks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -271,8 +271,8 @@ public static uint GetRackCount()
{
try
{
var racks = UnityEngine.Object.FindObjectsOfType<Rack>();
return racks != null ? (uint)racks.Length : 0;
// ⚡ Bolt: Performance Optimization - Replaced O(N) FindObjectsOfType with O(1) cache
return gregCore.GameLayer.Patches.Hardware.RackPatch.GetRackCount();
}
catch { return 0; }
}
Expand Down
64 changes: 64 additions & 0 deletions src/GameLayer/Patches/Hardware/RackPatch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,70 @@ public static class RackPatch
{
private static readonly Dictionary<int, HashSet<int>> _usedPositions = new();
private static readonly object _lock = new();
private static readonly HashSet<global::Il2Cpp.Rack> _racksCache = new();
private static readonly object _cacheLock = new();

// ⚡ Bolt Performance Optimization:
// Replaced expensive `UnityEngine.Object.FindObjectsOfType<Rack>()` which is O(N)
// over all active GameObjects, causing main thread hitches and severe GC pressure.
// Instead, we maintain an O(1) cache of all active Racks via Awake/OnDestroy hooks.
// Impact: Eliminates GC allocations and slow scene traversals when counting Racks.

[HarmonyPatch(typeof(global::Il2Cpp.Rack), nameof(global::Il2Cpp.Rack.Awake))]
[HarmonyPostfix]
private static void AwakePostfix(global::Il2Cpp.Rack __instance)
{
try
{
if (__instance == null || __instance.Pointer == IntPtr.Zero) return;
lock (_cacheLock)
{
_racksCache.Add(__instance);
}
}
catch (Exception ex)
{
MelonLogger.Error($"[RackPatch] Awake failed: {ex.Message}");
}
}

[HarmonyPatch(typeof(global::Il2Cpp.Rack), nameof(global::Il2Cpp.Rack.OnDestroy))]
[HarmonyPrefix]
private static void OnDestroyPrefix(global::Il2Cpp.Rack __instance)
{
try
{
if (__instance == null) return;
lock (_cacheLock)
{
_racksCache.Remove(__instance);
}
}
catch (Exception ex)
{
MelonLogger.Error($"[RackPatch] OnDestroy failed: {ex.Message}");
}
}

public static uint GetRackCount()
{
lock (_cacheLock)
{
// Prune dead references defensively
_racksCache.RemoveWhere(r => r == null || r.Pointer == IntPtr.Zero);
return (uint)_racksCache.Count;
}
}

public static List<global::Il2Cpp.Rack> GetRacks()
{
lock (_cacheLock)
{
// Prune dead references defensively
_racksCache.RemoveWhere(r => r == null || r.Pointer == IntPtr.Zero);
return new List<global::Il2Cpp.Rack>(_racksCache);
}
}

[HarmonyPatch(typeof(global::Il2Cpp.Rack), nameof(global::Il2Cpp.Rack.IsPositionAvailable))]
[HarmonyPrefix]
Expand Down
7 changes: 4 additions & 3 deletions src/Infrastructure/Scripting/Lua/Modules/LuaRackModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ public static void Register(Table greg, Script script, string modId)
{
try
{
var racks = UnityEngine.Object.FindObjectsOfType<Il2Cpp.Rack>();
// Bolt: Performance Optimization - Replaced FindObjectsOfType (O(N)) with O(1) cached lookup to prevent main thread blocking and GC pressure
var racks = gregCore.GameLayer.Patches.Hardware.RackPatch.GetRacks();
var result = new Table(script);
int i = 1;
foreach (var rack in racks)
Expand Down Expand Up @@ -56,8 +57,8 @@ public static void Register(Table greg, Script script, string modId)
{
try
{
var racks = UnityEngine.Object.FindObjectsOfType<Il2Cpp.Rack>();
return racks?.Count ?? 0;
// Bolt: Performance Optimization - Replaced FindObjectsOfType with cached count
return (int)gregCore.GameLayer.Patches.Hardware.RackPatch.GetRackCount();
}
catch { return 0; }
});
Expand Down
3 changes: 2 additions & 1 deletion src/PublicApi/Modules/GregFacilityModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ public sealed class GregFacilityModule
private readonly GregApiContext _ctx;
internal GregFacilityModule(GregApiContext ctx) => _ctx = ctx;

public int GetRackCount() => UnityEngine.Object.FindObjectsOfType<global::Il2Cpp.Rack>().Length;
// ⚡ Bolt: Performance Optimization - Replaced O(N) FindObjectsOfType with O(1) cache
public int GetRackCount() => (int)gregCore.GameLayer.Patches.Hardware.RackPatch.GetRackCount();
}
Loading