sex
This commit is contained in:
@@ -47,6 +47,7 @@ namespace KCM.Enums
|
||||
PlaceKeepRandomly = 91,
|
||||
ResyncRequest = 92,
|
||||
ResourceSnapshot = 93,
|
||||
BuildingSnapshot = 94
|
||||
BuildingSnapshot = 94,
|
||||
SpeedRestoreRequest = 95
|
||||
}
|
||||
}
|
||||
|
||||
279
Main.cs
279
Main.cs
@@ -59,6 +59,11 @@ namespace KCM
|
||||
private static int resetInProgress = 0;
|
||||
private static int multiplayerSaveLoadInProgress = 0;
|
||||
private static int suppressVillagerTeleportPackets = 0;
|
||||
private static int lastAppliedSpeedIndex = 0;
|
||||
private static long lastSimWatchdogFixMs = 0;
|
||||
private static int lastNonZeroSpeedIndex = 1;
|
||||
private static int forceAllowClientLocalSpeedSetOnce = 0;
|
||||
private static long lastSimStateLogMs = 0;
|
||||
|
||||
public static bool IsMultiplayerSaveLoadInProgress
|
||||
{
|
||||
@@ -80,6 +85,143 @@ namespace KCM
|
||||
Interlocked.Exchange(ref suppressVillagerTeleportPackets, suppress ? 1 : 0);
|
||||
}
|
||||
|
||||
public static int LastAppliedSpeedIndex
|
||||
{
|
||||
get { return Volatile.Read(ref lastAppliedSpeedIndex); }
|
||||
}
|
||||
|
||||
public static int LastNonZeroSpeedIndex
|
||||
{
|
||||
get { return Volatile.Read(ref lastNonZeroSpeedIndex); }
|
||||
}
|
||||
|
||||
private static void SetLastAppliedSpeedIndex(int idx)
|
||||
{
|
||||
Interlocked.Exchange(ref lastAppliedSpeedIndex, idx);
|
||||
if (idx > 0)
|
||||
Interlocked.Exchange(ref lastNonZeroSpeedIndex, idx);
|
||||
}
|
||||
|
||||
private static void ForceAllowClientLocalSpeedOnce()
|
||||
{
|
||||
Interlocked.Exchange(ref forceAllowClientLocalSpeedSetOnce, 1);
|
||||
}
|
||||
|
||||
private static bool ConsumeForceAllowClientLocalSpeedOnce()
|
||||
{
|
||||
return Interlocked.Exchange(ref forceAllowClientLocalSpeedSetOnce, 0) == 1;
|
||||
}
|
||||
|
||||
private static int TryGetLoadTickDelay(object instance)
|
||||
{
|
||||
if (instance == null)
|
||||
return -1;
|
||||
|
||||
try
|
||||
{
|
||||
FieldInfo loadTickDelayField = instance.GetType().GetField("loadTickDelay", BindingFlags.Instance | BindingFlags.NonPublic);
|
||||
if (loadTickDelayField != null && loadTickDelayField.FieldType == typeof(int))
|
||||
return (int)loadTickDelayField.GetValue(instance);
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
private static void LogSimState(string reason)
|
||||
{
|
||||
try
|
||||
{
|
||||
long now = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
if ((now - lastSimStateLogMs) < 1000)
|
||||
return;
|
||||
|
||||
lastSimStateLogMs = now;
|
||||
|
||||
bool connected = false;
|
||||
try { connected = KCClient.client != null && KCClient.client.IsConnected; } catch { }
|
||||
|
||||
bool focused = true;
|
||||
try { focused = Application.isFocused; } catch { }
|
||||
|
||||
helper?.Log($"SimState ({(reason ?? string.Empty)}): menu={(int)menuState} server={KCServer.IsRunning} connected={connected} focused={focused} timeScale={Time.timeScale:0.###} speedLast={LastAppliedSpeedIndex} speedNonZero={LastNonZeroSpeedIndex}");
|
||||
helper?.Log($"SimState systems: Player={(Player.inst != null)} UnitSys={(UnitSystem.inst != null)} JobSys={(JobSystem.inst != null)} VillagerSys={(VillagerSystem.inst != null)}");
|
||||
|
||||
try { helper?.Log($"SimState enabled: UnitSys={(UnitSystem.inst != null && UnitSystem.inst.enabled)} JobSys={(JobSystem.inst != null && JobSystem.inst.enabled)} VillagerSys={(VillagerSystem.inst != null && VillagerSystem.inst.enabled)}"); } catch { }
|
||||
try { helper?.Log($"SimState loadTickDelay: Player={TryGetLoadTickDelay(Player.inst)} UnitSys={TryGetLoadTickDelay(UnitSystem.inst)} JobSys={TryGetLoadTickDelay(JobSystem.inst)} VillagerSys={TryGetLoadTickDelay(VillagerSystem.inst)}"); } catch { }
|
||||
|
||||
try
|
||||
{
|
||||
int playerWorkers = Player.inst != null ? Player.inst.Workers.Count : -1;
|
||||
int allVillagers = Villager.villagers != null ? Villager.villagers.Count : -1;
|
||||
helper?.Log($"SimState villagers: playerWorkers={playerWorkers} allVillagers={allVillagers}");
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private static void TryForceSetSpeed(int speedIdx, string reason)
|
||||
{
|
||||
if (speedIdx <= 0)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (SpeedControlUI.inst == null)
|
||||
return;
|
||||
|
||||
if (!KCServer.IsRunning)
|
||||
ForceAllowClientLocalSpeedOnce();
|
||||
|
||||
helper?.Log($"ForceSetSpeed: {speedIdx} ({reason ?? string.Empty})");
|
||||
SpeedControlUI.inst.SetSpeed(speedIdx);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
helper?.Log("ForceSetSpeed failed");
|
||||
helper?.Log(ex.ToString());
|
||||
}
|
||||
}
|
||||
|
||||
public static void DumpSimState(string reason)
|
||||
{
|
||||
LogSimState(reason);
|
||||
}
|
||||
|
||||
public static void Unstuck(string reason)
|
||||
{
|
||||
try
|
||||
{
|
||||
bool connected = KCClient.client != null && KCClient.client.IsConnected;
|
||||
if (!connected && !KCServer.IsRunning)
|
||||
return;
|
||||
|
||||
LogSimState("unstuck:" + (reason ?? string.Empty));
|
||||
|
||||
if (!KCServer.IsRunning)
|
||||
{
|
||||
try { new KCM.Packets.Network.SpeedRestoreRequestPacket { reason = reason ?? string.Empty }.Send(); } catch { }
|
||||
}
|
||||
else
|
||||
{
|
||||
int speed = LastNonZeroSpeedIndex > 0 ? LastNonZeroSpeedIndex : 1;
|
||||
TryForceSetSpeed(speed, "unstuck:" + (reason ?? string.Empty));
|
||||
}
|
||||
|
||||
try { RunPostLoadRebuild("unstuck:" + (reason ?? string.Empty)); } catch { }
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static void ResetMultiplayerState(string reason = null)
|
||||
{
|
||||
if (Interlocked.Exchange(ref resetInProgress, 1) == 1)
|
||||
@@ -446,6 +588,123 @@ namespace KCM
|
||||
|
||||
public static int FixedUpdateInterval = 0;
|
||||
|
||||
private void Update()
|
||||
{
|
||||
try
|
||||
{
|
||||
bool connected = KCClient.client != null && KCClient.client.IsConnected;
|
||||
if (!connected && !KCServer.IsRunning)
|
||||
return;
|
||||
|
||||
try
|
||||
{
|
||||
if (Input.GetKeyDown(KeyCode.F8))
|
||||
LogSimState("hotkey:F8");
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.F9) && connected)
|
||||
{
|
||||
if (!KCServer.IsRunning)
|
||||
{
|
||||
try { new KCM.Packets.Network.SpeedRestoreRequestPacket { reason = "hotkey:F9" }.Send(); } catch { }
|
||||
}
|
||||
else
|
||||
{
|
||||
TryForceSetSpeed(LastNonZeroSpeedIndex > 0 ? LastNonZeroSpeedIndex : 1, "hotkey:F9");
|
||||
}
|
||||
|
||||
try { RunPostLoadRebuild("unstuck:hotkey:F9"); } catch { }
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
// Only apply watchdog behavior in playing mode.
|
||||
if ((int)menuState != 200)
|
||||
return;
|
||||
|
||||
// If something pauses the game by setting Time.timeScale without going through SpeedControlUI,
|
||||
// villagers + events will appear "stuck". We reapply the last known speed.
|
||||
if (LastAppliedSpeedIndex > 0 && Time.timeScale == 0f)
|
||||
{
|
||||
long now = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
if ((now - lastSimWatchdogFixMs) >= 2000)
|
||||
{
|
||||
lastSimWatchdogFixMs = now;
|
||||
|
||||
LogSimState("watchdog:timescale0");
|
||||
TryForceSetSpeed(LastAppliedSpeedIndex, "watchdog:timescale0");
|
||||
}
|
||||
}
|
||||
|
||||
// Best-effort recovery if core simulation systems are disabled after load.
|
||||
try { if (UnitSystem.inst != null && !UnitSystem.inst.enabled) UnitSystem.inst.enabled = true; } catch { }
|
||||
try { if (JobSystem.inst != null && !JobSystem.inst.enabled) JobSystem.inst.enabled = true; } catch { }
|
||||
try { if (VillagerSystem.inst != null && !VillagerSystem.inst.enabled) VillagerSystem.inst.enabled = true; } catch { }
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationFocus(bool hasFocus)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!hasFocus)
|
||||
return;
|
||||
|
||||
bool connected = KCClient.client != null && KCClient.client.IsConnected;
|
||||
if (!connected && !KCServer.IsRunning)
|
||||
return;
|
||||
|
||||
if ((int)menuState != 200)
|
||||
return;
|
||||
|
||||
if (Time.timeScale != 0f)
|
||||
return;
|
||||
|
||||
int speed = LastAppliedSpeedIndex > 0 ? LastAppliedSpeedIndex : LastNonZeroSpeedIndex;
|
||||
if (speed <= 0)
|
||||
speed = 1;
|
||||
|
||||
LogSimState("focus-gained");
|
||||
TryForceSetSpeed(speed, "focus-gained");
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void OnApplicationPause(bool pauseStatus)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (pauseStatus)
|
||||
return;
|
||||
|
||||
bool connected = KCClient.client != null && KCClient.client.IsConnected;
|
||||
if (!connected && !KCServer.IsRunning)
|
||||
return;
|
||||
|
||||
if ((int)menuState != 200)
|
||||
return;
|
||||
|
||||
if (Time.timeScale != 0f)
|
||||
return;
|
||||
|
||||
int speed = LastAppliedSpeedIndex > 0 ? LastAppliedSpeedIndex : LastNonZeroSpeedIndex;
|
||||
if (speed <= 0)
|
||||
speed = 1;
|
||||
|
||||
LogSimState("unpaused");
|
||||
TryForceSetSpeed(speed, "unpaused");
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
private void FixedUpdate()
|
||||
{
|
||||
// send batched building placement info
|
||||
@@ -948,6 +1207,15 @@ namespace KCM
|
||||
{
|
||||
if (KCClient.client.IsConnected)
|
||||
{
|
||||
// Only override AddBuilding for remote "Client Player" instances.
|
||||
// The vanilla implementation does important registration work for the local player (jobs, systems, etc).
|
||||
// When loading/unpacking for a remote player we temporarily set Player.inst to that player, so vanilla is safe.
|
||||
if (__instance == null || __instance == Player.inst)
|
||||
return true;
|
||||
|
||||
if (__instance.gameObject == null || __instance.gameObject.name == null || !__instance.gameObject.name.Contains("Client Player"))
|
||||
return true;
|
||||
|
||||
__instance.Buildings.Add(b);
|
||||
IResourceStorage[] storages = b.GetComponents<IResourceStorage>();
|
||||
for (int i = 0; i < storages.Length; i++)
|
||||
@@ -1262,7 +1530,16 @@ namespace KCM
|
||||
if (!KCServer.IsRunning)
|
||||
{
|
||||
if (calledFromPacket)
|
||||
{
|
||||
SetLastAppliedSpeedIndex(idx);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (idx > 0 && ConsumeForceAllowClientLocalSpeedOnce())
|
||||
{
|
||||
SetLastAppliedSpeedIndex(idx);
|
||||
return true;
|
||||
}
|
||||
|
||||
long now = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
if ((now - lastClientBlockLogTime) >= 2000)
|
||||
@@ -1274,6 +1551,8 @@ namespace KCM
|
||||
return false;
|
||||
}
|
||||
|
||||
SetLastAppliedSpeedIndex(idx);
|
||||
|
||||
if (!calledFromPacket)
|
||||
{
|
||||
long now = DateTimeOffset.Now.ToUnixTimeMilliseconds();
|
||||
|
||||
39
Packets/Network/SpeedRestoreRequestPacket.cs
Normal file
39
Packets/Network/SpeedRestoreRequestPacket.cs
Normal file
@@ -0,0 +1,39 @@
|
||||
using KCM.Attributes;
|
||||
using KCM.Packets.Game;
|
||||
using System;
|
||||
|
||||
namespace KCM.Packets.Network
|
||||
{
|
||||
[NoServerRelay]
|
||||
public class SpeedRestoreRequestPacket : Packet
|
||||
{
|
||||
public override ushort packetId => (ushort)Enums.Packets.SpeedRestoreRequest;
|
||||
|
||||
public string reason { get; set; }
|
||||
|
||||
public override void HandlePacketClient()
|
||||
{
|
||||
}
|
||||
|
||||
public override void HandlePacketServer()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!KCServer.IsRunning)
|
||||
return;
|
||||
|
||||
int speed = Main.LastNonZeroSpeedIndex;
|
||||
if (speed <= 0)
|
||||
speed = 1;
|
||||
|
||||
Main.helper.Log($"Speed restore requested by client {clientId} ({reason ?? string.Empty}); broadcasting speed {speed}");
|
||||
new SetSpeed { speed = speed }.SendToAll();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Main.helper.Log("Error handling SpeedRestoreRequestPacket on server");
|
||||
Main.helper.Log(ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,6 +219,36 @@ namespace KCM
|
||||
{
|
||||
if (ChatInput.text.Length > 0)
|
||||
{
|
||||
if (ChatInput.text.Trim().Equals("/simdebug", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
try
|
||||
{
|
||||
Main.DumpSimState("chat:/simdebug");
|
||||
LobbyHandler.AddSystemMessage("Sim state logged to output.txt");
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
ChatInput.text = "";
|
||||
return;
|
||||
}
|
||||
|
||||
if (ChatInput.text.Trim().Equals("/unstuck", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
try
|
||||
{
|
||||
Main.Unstuck("chat:/unstuck");
|
||||
LobbyHandler.AddSystemMessage("Unstuck requested.");
|
||||
}
|
||||
catch
|
||||
{
|
||||
}
|
||||
|
||||
ChatInput.text = "";
|
||||
return;
|
||||
}
|
||||
|
||||
if (ChatInput.text.Trim().Equals("/resync", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
try
|
||||
|
||||
@@ -6,6 +6,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using static KCM.StateManagement.Observers.Observer;
|
||||
|
||||
namespace KCM.StateManagement.BuildingState
|
||||
|
||||
Reference in New Issue
Block a user