diff --git a/.jules/bolt.md b/.jules/bolt.md index d453db43..0208615a 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-23 - Expensive Server Lookups in Lua API +**Learning:** The Lua API (`LuaServerModule`) was using `UnityEngine.Object.FindObjectsOfType()` for fetching all servers (`get_all`), repairing servers (`repair`), and repairing all servers (`repair_all`). `FindObjectsOfType` is O(N) over all loaded objects and generates unnecessary garbage collection pressure and CPU overhead, especially as the data center scales and the number of servers grows. +**Action:** Replace `FindObjectsOfType()` with lookups from the game-managed singleton collections (`Il2Cpp.NetworkMap.instance.servers` and `Il2Cpp.NetworkMap.instance.brokenServers`) to turn these O(N) operations into O(1) dictionary iterations, while keeping `FindObjectsOfType` strictly as a fallback when the game state is uninitialized. diff --git a/src/Infrastructure/Scripting/Lua/Modules/LuaServerModule.cs b/src/Infrastructure/Scripting/Lua/Modules/LuaServerModule.cs index 17bd8abb..2deff4ac 100644 --- a/src/Infrastructure/Scripting/Lua/Modules/LuaServerModule.cs +++ b/src/Infrastructure/Scripting/Lua/Modules/LuaServerModule.cs @@ -17,31 +17,59 @@ public static void Register(Table greg, Script script, string modId) var serverTable = new Table(script); // greg.server.get_all() → table of server info + // Bolt: Optimized to use O(1) NetworkMap dictionary instead of O(N) FindObjectsOfType serverTable["get_all"] = (Func)(() => { try { - var servers = UnityEngine.Object.FindObjectsOfType(); var result = new Table(script); int i = 1; - foreach (var s in servers) + var nm = Il2Cpp.NetworkMap.instance; + if (nm != null && nm.servers != null) + { + foreach (var kvp in nm.servers) + { + var s = kvp.Value; + try + { + var info = new Table(script); + info["id"] = s.ServerID ?? s.GetHashCode().ToString(); + info["hash"] = s.GetHashCode(); + info["is_on"] = s.isOn; + info["is_broken"] = s.isBroken; + info["server_type"] = (int)s.serverType; + info["size_u"] = s.sizeInU; + var pos = s.transform?.position ?? UnityEngine.Vector3.zero; + info["x"] = (double)pos.x; + info["y"] = (double)pos.y; + info["z"] = (double)pos.z; + result[i++] = info; + } + catch { } + } + } + else { - try + var servers = UnityEngine.Object.FindObjectsOfType(); + foreach (var s in servers) { - var info = new Table(script); - info["id"] = s.ServerID ?? s.GetHashCode().ToString(); - info["hash"] = s.GetHashCode(); - info["is_on"] = s.isOn; - info["is_broken"] = s.isBroken; - info["server_type"] = (int)s.serverType; - info["size_u"] = s.sizeInU; - var pos = s.transform?.position ?? UnityEngine.Vector3.zero; - info["x"] = (double)pos.x; - info["y"] = (double)pos.y; - info["z"] = (double)pos.z; - result[i++] = info; + try + { + var info = new Table(script); + info["id"] = s.ServerID ?? s.GetHashCode().ToString(); + info["hash"] = s.GetHashCode(); + info["is_on"] = s.isOn; + info["is_broken"] = s.isBroken; + info["server_type"] = (int)s.serverType; + info["size_u"] = s.sizeInU; + var pos = s.transform?.position ?? UnityEngine.Vector3.zero; + info["x"] = (double)pos.x; + info["y"] = (double)pos.y; + info["z"] = (double)pos.z; + result[i++] = info; + } + catch { } } - catch { } } return result; } @@ -76,22 +104,43 @@ public static void Register(Table greg, Script script, string modId) }); // greg.server.repair(server_hash) → bool + // Bolt: Optimized to use O(1) NetworkMap.brokenServers instead of O(N) FindObjectsOfType serverTable["repair"] = (Func)((hash) => { try { - var servers = UnityEngine.Object.FindObjectsOfType(); - foreach (var s in servers) + var nm = Il2Cpp.NetworkMap.instance; + if (nm != null && nm.brokenServers != null) + { + foreach (var kvp in nm.brokenServers) + { + var s = kvp.Value; + try + { + if (s.GetHashCode() == hash && s.isBroken) + { + s.RepairDevice(); + return true; + } + } + catch { } + } + } + else { - try + var servers = UnityEngine.Object.FindObjectsOfType(); + foreach (var s in servers) { - if (s.GetHashCode() == hash && s.isBroken) + try { - s.RepairDevice(); - return true; + if (s.GetHashCode() == hash && s.isBroken) + { + s.RepairDevice(); + return true; + } } + catch { } } - catch { } } return false; } @@ -99,23 +148,47 @@ public static void Register(Table greg, Script script, string modId) }); // greg.server.repair_all() → number of repaired + // Bolt: Optimized to use O(1) NetworkMap.brokenServers instead of O(N) FindObjectsOfType serverTable["repair_all"] = (Func)(() => { try { int repaired = 0; - var servers = UnityEngine.Object.FindObjectsOfType(); - foreach (var s in servers) + var nm = Il2Cpp.NetworkMap.instance; + if (nm != null && nm.brokenServers != null) + { + if (nm.brokenServers.Count == 0) return 0; + var toRepair = new System.Collections.Generic.List(); + foreach (var kvp in nm.brokenServers) toRepair.Add(kvp.Value); + + foreach (var s in toRepair) + { + try + { + if (s.isBroken) + { + s.RepairDevice(); + repaired++; + } + } + catch { } + } + } + else { - try + var servers = UnityEngine.Object.FindObjectsOfType(); + foreach (var s in servers) { - if (s.isBroken) + try { - s.RepairDevice(); - repaired++; + if (s.isBroken) + { + s.RepairDevice(); + repaired++; + } } + catch { } } - catch { } } return repaired; }