using TMPro; using UnityEngine; namespace FloodSWE.Networking { /// /// Binds SweServerRuntime diagnostics to user-provided TMP text fields. /// public sealed class SweServerDiagnosticsHud : MonoBehaviour { [Header("References")] [SerializeField] private SweServerRuntime serverRuntime; [SerializeField] private TMP_Text simulationText; [SerializeField] private TMP_Text connectionText; [SerializeField] private TMP_Text commandText; [SerializeField] private TMP_Text forcingText; [SerializeField] private TMP_Text tileLoadText; [Header("Behavior")] [SerializeField] private bool autoFindRuntime = true; [SerializeField] private float refreshIntervalSeconds = 0.15f; [Header("Labels")] [SerializeField] private string connectedLabel = "Connected"; [SerializeField] private string staleLabel = "Stale"; [SerializeField] private string disconnectedLabel = "Disconnected"; [SerializeField] private string unknownAgeLabel = "n/a"; [SerializeField] private string numberFormat = "0.00"; [Header("Colors")] [SerializeField] private Color connectedColor = new Color(0.4f, 1.0f, 0.4f); [SerializeField] private Color staleColor = new Color(1.0f, 0.9f, 0.3f); [SerializeField] private Color disconnectedColor = new Color(1.0f, 0.5f, 0.5f); private float nextRefreshTime; private void OnEnable() { if (serverRuntime == null && autoFindRuntime) { serverRuntime = FindFirstObjectByType(); } Refresh(true); } private void Update() { if (Time.unscaledTime < nextRefreshTime) { return; } Refresh(false); } private void Refresh(bool immediate) { nextRefreshTime = Time.unscaledTime + Mathf.Max(0.02f, refreshIntervalSeconds); if (serverRuntime == null) { SetText(simulationText, "Sim Speed: n/a"); SetText(connectionText, "Connection: runtime missing"); SetText(commandText, "Last Command: n/a"); SetText(forcingText, "Forcing: n/a"); SetText(tileLoadText, "Tile Load: runtime missing"); if (connectionText != null) { connectionText.color = disconnectedColor; } return; } string simLine = $"Sim Speed: {serverRuntime.SimulatedSecondsPerSecond.ToString(numberFormat)} sim-s/s"; if (!serverRuntime.HasSimulationThroughput) { simLine = "Sim Speed: warming up"; } SetText(simulationText, simLine); string state; Color stateColor; if (!serverRuntime.HasConnectedClients) { state = disconnectedLabel; stateColor = disconnectedColor; } else if (serverRuntime.HasRecentClientSignal) { state = connectedLabel; stateColor = connectedColor; } else { state = staleLabel; stateColor = staleColor; } string connectionLine = $"Connection: {state} | clients={serverRuntime.ConnectedClientCount} | endpoint={serverRuntime.QuestEndpointLabel}"; SetText(connectionText, connectionLine); if (connectionText != null) { connectionText.color = stateColor; } string age = float.IsInfinity(serverRuntime.LastCommandAgeSeconds) ? unknownAgeLabel : $"{serverRuntime.LastCommandAgeSeconds.ToString(numberFormat)}s"; string commandLine = $"Last Command: {serverRuntime.LastCommandName} from {serverRuntime.LastCommandSender} ({age}) | " + $"ctrl={serverRuntime.DecodedControlPackets}/{serverRuntime.ReceivedControlPackets} invalid={serverRuntime.InvalidControlPackets} ack={serverRuntime.AckPacketsSent}"; SetText(commandText, commandLine); string forcingLine = $"Forcing: {serverRuntime.LastForcingStatus} | cells={serverRuntime.LastForcedCellCount} | tile={serverRuntime.ActiveTileLabel}"; SetText(forcingText, forcingLine); string tileLine = $"Tile Load: {serverRuntime.TileLoadSummary} | framePayload={serverRuntime.LastFramePayloadBytes}B " + $"frames={serverRuntime.TransmittedFramePackets} dropped={serverRuntime.DroppedFramePackets}"; SetText(tileLoadText, tileLine); if (immediate) { nextRefreshTime = Time.unscaledTime; } } private static void SetText(TMP_Text target, string value) { if (target == null || target.text == value) { return; } target.text = value; } } }