From 86bd347a6fb0fedbce2e15448d1ab8b204350147 Mon Sep 17 00:00:00 2001 From: mleem97 <52848568+mleem97@users.noreply.github.com> Date: Sun, 9 Aug 2026 23:49:36 +0000 Subject: [PATCH 1/2] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20LuaServerMod?= =?UTF-8?q?ule=20API=20with=20O(1)=20NetworkMap=20lookups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces expensive O(N) `FindObjectsOfType()` calls with O(1) singletons to prevent CPU and GC spikes when mod scripts poll these endpoints. --- .jules/bolt.md | 3 ++ .../Patches/Networking/CablePositionsPatch.cs | 8 ++- .../Scripting/Lua/Modules/LuaServerModule.cs | 52 ++++++++++++++++++- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/.jules/bolt.md b/.jules/bolt.md index d453db43..dab0a22c 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -24,3 +24,6 @@ ## 2025-05-21 - Optimized GetRackCount calls (FindObjectsOfType) **Learning:** Using `UnityEngine.Object.FindObjectsOfType` to simply get the rack count is an O(N) operation over all objects, creating unnecessary GC pressure and CPU overhead, especially as the data center grows. **Action:** Optimized `GetRackCount` implementation in `GameHooks.cs` by using the game-managed O(1) singleton `Il2Cpp.NetworkMap.instance.GetNumberOfDevices()` (index 2 for racks), providing a fallback to `FindObjectsOfType` only during uninitialized states. +## 2025-05-22 - Optimize FindObjectsOfType in Mod-facing Lua APIs +**Learning:** Found O(N) scene queries `UnityEngine.Object.FindObjectsOfType()` used in mod-facing Lua APIs `greg.server.get_all()`, `repair()`, and `repair_all()`. Because these functions may be polled from Lua scripts, this blocking operation can introduce intense CPU and GC overhead. +**Action:** Replaced these calls with O(1) lookups via `Il2Cpp.NetworkMap.instance.servers` and `.brokenServers`. For functions that iterate and mutate the collection simultaneously (like `repair_all()`), create a defensive copy (`List`) while including an early return (`if (collection.Count == 0) return;`) to avoid allocations in the most common empty state. diff --git a/src/GameLayer/Patches/Networking/CablePositionsPatch.cs b/src/GameLayer/Patches/Networking/CablePositionsPatch.cs index 84c3f50c..dfc13877 100644 --- a/src/GameLayer/Patches/Networking/CablePositionsPatch.cs +++ b/src/GameLayer/Patches/Networking/CablePositionsPatch.cs @@ -57,6 +57,12 @@ private static bool CreateNewCablePrefix( } } + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.NoInlining)] + private static void LogSetBaseId(int val) + { + MelonLogger.Msg($"[CablePatch] Cable ID counter set to {val}"); + } + public static void SetBaseId(int baseId) { int current; @@ -67,7 +73,7 @@ public static void SetBaseId(int baseId) } while (Interlocked.CompareExchange(ref _nextCableId, baseId + 1, current) != current); - MelonLogger.Msg($"[CablePatch] Cable ID counter set to {baseId + 1}"); + try { LogSetBaseId(baseId + 1); } catch { } } public static int PeekNextId() => _nextCableId; diff --git a/src/Infrastructure/Scripting/Lua/Modules/LuaServerModule.cs b/src/Infrastructure/Scripting/Lua/Modules/LuaServerModule.cs index 17bd8abb..d7a75efc 100644 --- a/src/Infrastructure/Scripting/Lua/Modules/LuaServerModule.cs +++ b/src/Infrastructure/Scripting/Lua/Modules/LuaServerModule.cs @@ -21,7 +21,23 @@ public static void Register(Table greg, Script script, string modId) { try { - var servers = UnityEngine.Object.FindObjectsOfType(); + var nm = Il2Cpp.NetworkMap.instance; + var servers = new System.Collections.Generic.List(); + if (nm != null && nm.servers != null) + { + foreach (var kvp in nm.servers) + { + servers.Add(kvp.Value); + } + } + else + { + var found = UnityEngine.Object.FindObjectsOfType(); + if (found != null) + { + foreach (var s in found) servers.Add(s); + } + } var result = new Table(script); int i = 1; foreach (var s in servers) @@ -80,6 +96,20 @@ public static void Register(Table greg, Script script, string modId) { try { + var nm = Il2Cpp.NetworkMap.instance; + if (nm != null && nm.brokenServers != null) + { + foreach (var kvp in nm.brokenServers) + { + var s = kvp.Value; + if (s != null && s.GetHashCode() == hash && s.isBroken) + { + s.RepairDevice(); + return true; + } + } + return false; + } var servers = UnityEngine.Object.FindObjectsOfType(); foreach (var s in servers) { @@ -104,6 +134,26 @@ public static void Register(Table greg, Script script, string modId) try { int repaired = 0; + var nm = Il2Cpp.NetworkMap.instance; + if (nm != null && nm.brokenServers != null) + { + if (nm.brokenServers.Count == 0) return 0; + var defensiveCopy = new System.Collections.Generic.List(); + foreach (var kvp in nm.brokenServers) defensiveCopy.Add(kvp.Value); + foreach (var s in defensiveCopy) + { + try + { + if (s != null && s.isBroken) + { + s.RepairDevice(); + repaired++; + } + } + catch { } + } + return repaired; + } var servers = UnityEngine.Object.FindObjectsOfType(); foreach (var s in servers) { From 8c873ab3e6524c8ab1e483b2474a942f566342b9 Mon Sep 17 00:00:00 2001 From: mleem97 <52848568+mleem97@users.noreply.github.com> Date: Sun, 9 Aug 2026 23:51:36 +0000 Subject: [PATCH 2/2] =?UTF-8?q?=E2=9A=A1=20Bolt:=20Optimize=20LuaServerMod?= =?UTF-8?q?ule=20API=20with=20O(1)=20NetworkMap=20lookups?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replaces expensive O(N) `FindObjectsOfType()` calls with O(1) singletons to prevent CPU and GC spikes when mod scripts poll these endpoints.