From dc50bf2892185bdbf192a00937a77d4acec2eeeb Mon Sep 17 00:00:00 2001 From: devbeni Date: Sat, 13 Dec 2025 21:46:58 +0100 Subject: [PATCH] fix??? --- KCClient.cs | 1 + Main.cs | 44 +++++++------- .../BuildingState/BuildingStateManager.cs | 57 +++++++++++++++++-- StateManagement/Observers/Observer.cs | 25 ++++++++ 4 files changed, 96 insertions(+), 31 deletions(-) diff --git a/KCClient.cs b/KCClient.cs index 9e530c6..d81a7d4 100644 --- a/KCClient.cs +++ b/KCClient.cs @@ -102,6 +102,7 @@ namespace KCM public static void Connect(string ip) { Main.helper.Log("Trying to connect to: " + ip); + try { Application.runInBackground = true; } catch { } client.Connect(ip, useMessageHandlers: false); } diff --git a/Main.cs b/Main.cs index 03d6b24..b6848e3 100644 --- a/Main.cs +++ b/Main.cs @@ -942,26 +942,14 @@ namespace KCM [HarmonyPatch(typeof(Player), "AddBuilding")] public class PlayerAddBuildingHook { - static int step = 1; - static void LogStep(bool reset = false) - { - if (reset) - step = 1; - - Main.helper.Log(step.ToString()); - step++; - } - public static bool Prefix(Player __instance, Building b) { try { if (KCClient.client.IsConnected) { - LogStep(true); __instance.Buildings.Add(b); IResourceStorage[] storages = b.GetComponents(); - LogStep(); for (int i = 0; i < storages.Length; i++) { bool flag = !storages[i].IsPrivate(); @@ -970,50 +958,38 @@ namespace KCM FreeResourceManager.inst.AddResourceStorage(storages[i]); } } - LogStep(); int landMass = b.LandMass(); Home res = b.GetComponent(); bool flag2 = res != null; - LogStep(); if (flag2) { __instance.Residentials.Add(res); __instance.ResidentialsPerLandmass[landMass].Add(res); } WagePayer wagePayer = b.GetComponent(); - LogStep(); bool flag3 = wagePayer != null; if (flag3) { __instance.WagePayers.Add(wagePayer); } RadiusBonus radiusBonus = b.GetComponent(); - LogStep(); bool flag4 = radiusBonus != null; if (flag4) { __instance.RadiusBonuses.Add(radiusBonus); } - LogStep(); var globalBuildingRegistry = __instance.GetType().GetField("globalBuildingRegistry", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance) as ArrayExt; - LogStep(); var landMassBuildingRegistry = __instance.GetType().GetField("landMassBuildingRegistry", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance) as ArrayExt; - LogStep(); var unbuiltBuildingsPerLandmass = __instance.GetType().GetField("unbuiltBuildingsPerLandmass", BindingFlags.Instance | BindingFlags.NonPublic).GetValue(__instance) as ArrayExt>; - LogStep(); __instance.AddToRegistry(globalBuildingRegistry, b); - LogStep(); __instance.AddToRegistry(landMassBuildingRegistry.data[landMass].registry, b); - LogStep(); landMassBuildingRegistry.data[landMass].buildings.Add(b); - LogStep(); bool flag5 = !b.IsBuilt(); if (flag5) { unbuiltBuildingsPerLandmass.data[landMass].Add(b); } - LogStep(); return false; @@ -1270,6 +1246,8 @@ namespace KCM { private static long lastTime = 0; private static long lastClientBlockLogTime = 0; + private static long lastHostPauseTraceLogTime = 0; + private static int lastSentSpeed = -1; public static bool Prefix(int idx, ref bool __state) { @@ -1299,8 +1277,23 @@ namespace KCM if (!calledFromPacket) { long now = DateTimeOffset.Now.ToUnixTimeMilliseconds(); - if ((now - lastTime) >= 250) // Set speed spam fix / hack + // Ensure that real speed changes are always propagated, even if they happen in quick succession (eg. pause/unpause). + if (idx != lastSentSpeed || (now - lastTime) >= 250) // Set speed spam fix / hack __state = true; + + // Diagnostics for "random pause": log a stack trace when the host hits speed 0 from local code. + if (idx == 0 && (now - lastHostPauseTraceLogTime) >= 2000) + { + lastHostPauseTraceLogTime = now; + try + { + Main.helper.Log("Host speed set to 0 (pause). Call stack:"); + Main.helper.Log(new StackTrace(2, false).ToString()); + } + catch + { + } + } } return true; @@ -1320,6 +1313,7 @@ namespace KCM }.Send(); lastTime = DateTimeOffset.Now.ToUnixTimeMilliseconds(); + lastSentSpeed = idx; } } } diff --git a/StateManagement/BuildingState/BuildingStateManager.cs b/StateManagement/BuildingState/BuildingStateManager.cs index 7be888a..d6b9f23 100644 --- a/StateManagement/BuildingState/BuildingStateManager.cs +++ b/StateManagement/BuildingState/BuildingStateManager.cs @@ -22,27 +22,72 @@ namespace KCM.StateManagement.BuildingState { try { - Observer observer = (Observer)sender; + Observer observer = sender as Observer; + if (observer == null) + return; - Building building = (Building)observer.state; + Building building = observer.state as Building; + if (building == null) + return; //Main.helper.Log("Should send building network update for: " + building.UniqueName); + var t = building.transform; + if (t == null) + return; + + Quaternion rotation = t.rotation; + Vector3 globalPosition = t.position; + Vector3 localPosition = t.localPosition; + + if (t.childCount > 0) + { + try + { + var child = t.GetChild(0); + if (child != null) + { + rotation = child.rotation; + localPosition = child.localPosition; + } + } + catch + { + } + } + + float resourceProgress = 0f; + try + { + var field = building.GetType().GetField("resourceProgress", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); + if (field != null) + { + object value = field.GetValue(building); + if (value is float) + resourceProgress = (float)value; + else if (value != null) + resourceProgress = Convert.ToSingle(value); + } + } + catch + { + } + new BuildingStatePacket() { customName = building.customName, guid = building.guid, uniqueName = building.UniqueName, - rotation = building.transform.GetChild(0).rotation, - globalPosition = building.transform.position, - localPosition = building.transform.GetChild(0).localPosition, + rotation = rotation, + globalPosition = globalPosition, + localPosition = localPosition, built = building.IsBuilt(), placed = building.IsPlaced(), open = building.Open, doBuildAnimation = building.doBuildAnimation, constructionPaused = building.constructionPaused, constructionProgress = building.constructionProgress, - resourceProgress = (float)building.GetType().GetField("resourceProgress", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance).GetValue(building), + resourceProgress = resourceProgress, life = building.Life, ModifiedMaxLife = building.ModifiedMaxLife, yearBuilt = building.YearBuilt, diff --git a/StateManagement/Observers/Observer.cs b/StateManagement/Observers/Observer.cs index 7f7fc77..ef22fbf 100644 --- a/StateManagement/Observers/Observer.cs +++ b/StateManagement/Observers/Observer.cs @@ -128,6 +128,31 @@ namespace KCM.StateManagement.Observers if (this.state == null) return; + // Unity uses "fake null" for destroyed objects. Since our state is stored as object, + // we must explicitly detect that case to avoid exceptions + log spam. + try + { + UnityEngine.Object unityObj = this.state as UnityEngine.Object; + if (this.state is UnityEngine.Object && unityObj == null) + { + try { StateObserver.observers.Remove(this.state.GetHashCode()); } catch { } + try + { + if (observerObject != null) + UnityEngine.Object.Destroy(observerObject); + else + UnityEngine.Object.Destroy(this.gameObject); + } + catch + { + } + return; + } + } + catch + { + } + if (!(currentMs - lastUpdate > updateInterval)) // Don't run if the update interval hasn't passed (default 100 milliseconds); return;