using Mirror; using QSB.ClientServerStateSync; using QSB.EchoesOfTheEye.Ghosts.WorldObjects; using QSB.HUD; using QSB.Player; using QSB.Player.TransformSync; using QSB.ShipSync; using QSB.ShipSync.TransformSync; using QSB.ShipSync.WorldObjects; using QSB.TimeSync; using QSB.WorldSync; using System; using System.Linq; using QSB.Menus; using UnityEngine; namespace QSB.Utility; public class DebugGUI : MonoBehaviour, IAddComponentOnStart { private const float _debugLineSpacing = 8f; private const float FixedWidth = 200f; private const float Column1 = 20f; private static float column1Offset = 10f; private const float Column2 = Column1 + FixedWidth; private static float column2Offset = 10f; private const float Column3 = Column2 + FixedWidth; private static float column3Offset = 10f; private const float Column4 = Column3 + FixedWidth; private static float column4Offset = 10f; private const int MaxLabelSize = 15; private const float MaxLabelDistance = 150; private static readonly GUIStyle guiGUIStyle = new(); private static readonly GUIStyle labelGUIStyle = new(); private void Awake() { enabled = QSBCore.DebugSettings.DebugMode; guiGUIStyle.fontSize = 9; } private static void WriteLine(int columnID, string text) { var currentOffset = 0f; var x = 0f; switch (columnID) { case 1: x = Column1; currentOffset = column1Offset; column1Offset += _debugLineSpacing; break; case 2: x = Column2; currentOffset = column2Offset; column2Offset += _debugLineSpacing; break; case 3: x = Column3; currentOffset = column3Offset; column3Offset += _debugLineSpacing; break; case 4: x = Column4; currentOffset = column4Offset; column4Offset += _debugLineSpacing; break; } GUI.Label(new Rect(x, currentOffset, 0, 0), text, guiGUIStyle); } private static void WriteLine(int columnID, string text, Color color) { guiGUIStyle.normal.textColor = color; WriteLine(columnID, text); guiGUIStyle.normal.textColor = Color.white; } public void OnGUI() { if (Event.current.type != EventType.Repaint) { return; } DrawGui(); DrawWorldObjectLabels(); } private static void DrawGui() { guiGUIStyle.normal.textColor = Color.white; GUI.contentColor = Color.white; column1Offset = 10f; column2Offset = 10f; column3Offset = 10f; column4Offset = 10f; #region Column1 - Server data WriteLine(1, $"FPS : {Mathf.Round(1f / Time.smoothDeltaTime)}"); WriteLine(1, $"Ping : {Math.Round(NetworkTime.rtt * 1000.0)} ms"); if (!QSBCore.DebugSettings.DrawGui) { return; } WriteLine(1, $"IsHost : {QSBCore.IsHost} (WillBeHost : {(MenuManager.Instance != null ? MenuManager.Instance.WillBeHost : "MenuManager null")})"); WriteLine(1, $"HasWokenUp : {QSBWorldSync.AllObjectsReady}"); if (WakeUpSync.LocalInstance != null) { WriteLine(1, $"Server State : {ServerStateManager.Instance.GetServerState()}"); var currentState = WakeUpSync.LocalInstance.CurrentState; WriteLine(1, $"WakeUpSync State : {currentState}"); var reason = WakeUpSync.LocalInstance.CurrentReason; if (currentState == WakeUpSync.State.FastForwarding && reason != null) { WriteLine(1, $"Reason : {(FastForwardReason)reason}"); } else if (currentState == WakeUpSync.State.Pausing && reason != null) { WriteLine(1, $"Reason : {(PauseReason)reason}"); } else if (currentState != WakeUpSync.State.Loaded && currentState != WakeUpSync.State.NotLoaded && reason == null) { WriteLine(1, "Reason : NULL", Color.red); } WriteLine(1, $"Time Difference : {WakeUpSync.LocalInstance.GetTimeDifference()}"); WriteLine(1, $"Timescale : {OWTime.GetTimeScale()}"); WriteLine(1, $"Time Remaining : {Mathf.Floor(TimeLoop.GetSecondsRemaining() / 60f)}:{Mathf.Round(TimeLoop.GetSecondsRemaining() % 60f * 100f / 100f)}"); WriteLine(1, $"Loop Count : {TimeLoop.GetLoopCount()}"); WriteLine(1, $"TimeLoop Initialized : {TimeLoop._initialized}"); if (TimeLoop._initialized) { WriteLine(1, $"TimeLoop IsTimeFlowing : {TimeLoop.IsTimeFlowing()}"); WriteLine(1, $"TimeLoop IsTimeLoopEnabled : {TimeLoop.IsTimeLoopEnabled()}"); } WriteLine(1, $"Selected WorldObject : {(DebugActions.WorldObjectSelection == null ? "All" : DebugActions.WorldObjectSelection.Name)}"); if (Locator.GetDeathManager() != null) { WriteLine(1, $"Invincible : {Locator.GetDeathManager()._invincible}"); } if (Locator.GetToolModeSwapper() != null) { WriteLine(1, $"Tool Mode : {Locator.GetToolModeSwapper().GetToolMode()}"); } WriteLine(1, $"Input Mode Stack :"); foreach (var item in OWInput.GetInputModeStack()) { WriteLine(1, $" - {item}"); } WriteLine(1, $"HUD Icon Stack :"); foreach (var item in MultiplayerHUDManager.HUDIconStack) { WriteLine(1, $" - {item}"); } WriteLine(1, $"Sectors :"); foreach (var sector in PlayerTransformSync.LocalInstance.SectorDetector.SectorList) { WriteLine(1, $"- {sector.Name}"); } } #endregion #region Column2 - Player data WriteLine(2, "Player data :"); foreach (var player in QSBPlayerManager.PlayerList) { WriteLine(2, player.ToString(), Color.cyan); WriteLine(2, $"State : {player.State}"); WriteLine(2, $"Eye State : {player.EyeState}"); WriteLine(2, $"Dead : {player.IsDead}"); WriteLine(2, $"Ready : {player.IsReady}"); WriteLine(2, $"Suited Up : {player.SuitedUp}"); WriteLine(2, $"In Suited Up State : {player.AnimationSync?.InSuitedUpState}"); WriteLine(2, $"InDreamWorld : {player.InDreamWorld}"); if (player.IsReady && QSBWorldSync.AllObjectsReady) { WriteLine(2, $"Illuminated : {player.LightSensor?.IsIlluminated()}"); var singleLightSensor = (SingleLightSensor)player.LightSensor; // will be null for remote player light sensors if (singleLightSensor?._lightSources != null) { foreach (var item in singleLightSensor._lightSources) { WriteLine(2, $"- {item.GetLightSourceType()}"); } } var networkTransform = player.TransformSync; var referenceSector = networkTransform.ReferenceSector; var referenceTransform = networkTransform.ReferenceTransform; WriteLine(2, $" - Ref. Sector : {(referenceSector == null ? "NULL" : referenceSector.Name)}", referenceSector == null ? Color.red : Color.white); WriteLine(2, $" - Ref. Transform : {(referenceTransform == null ? "NULL" : referenceTransform.name)}", referenceTransform == null ? Color.red : Color.white); WriteLine(2, $" - Local Position : {player.Body.transform.localPosition}"); WriteLine(2, $" - Position : {player.Body.transform.position}"); WriteLine(2, $" - Entangled Object: {(player.EntangledObject == null ? "NULL" : player.EntangledObject.Name)}"); } } #endregion if (QSBSceneManager.CurrentScene == OWScene.SolarSystem) { #region Column3 - Ship data WriteLine(3, $"Current Flyer : {ShipManager.Instance.CurrentFlyer}"); if (ShipTransformSync.LocalInstance != null) { var instance = ShipTransformSync.LocalInstance; if (QSBCore.IsHost) { var currentOwner = instance.netIdentity.connectionToClient; if (currentOwner == null) { WriteLine(3, "Current Owner : NULL"); } else { WriteLine(3, $"Current Owner : {currentOwner.GetPlayerId()}"); } } var sector = instance.ReferenceSector; WriteLine(3, $"Ref. Sector : {(sector != null ? sector.Name : "NULL")}", sector == null ? Color.red : Color.white); var transform = instance.ReferenceTransform; WriteLine(3, $"Ref. Transform : {(transform != null ? transform.name : "NULL")}", transform == null ? Color.red : Color.white); } else { WriteLine(3, "ShipTransformSync.LocalInstance is null.", Color.red); } WriteLine(3, "QSBShipComponent"); foreach (var component in QSBWorldSync.GetWorldObjects()) { var attachedObject = component.AttachedObject; if (attachedObject == null) { WriteLine(3, $"- {component.ObjectId} NULL ATTACHEDOBJECT", Color.red); } else { WriteLine(3, $"- {component.AttachedObject.name} RepairFraction:{component.AttachedObject._repairFraction}"); } } WriteLine(3, "QSBShipHull"); foreach (var hull in QSBWorldSync.GetWorldObjects()) { var attachedObject = hull.AttachedObject; if (attachedObject == null) { WriteLine(3, $"- {hull.ObjectId} NULL ATTACHEDOBJECT", Color.red); } else { WriteLine(3, $"- {hull.AttachedObject.name}, Integrity:{hull.AttachedObject.integrity}"); } } #endregion if (QSBWorldSync.AllObjectsReady && QSBCore.DLCInstalled) { var ghost = QSBWorldSync.GetWorldObjects().First(x => x.AttachedObject._name == "Kamaji"); WriteLine(4, ghost.AttachedObject._name); WriteLine(4, $"Action:{ghost.GetCurrentActionName()}"); WriteLine(4, $"Threat Awareness:{ghost.GetThreatAwareness()}"); var interestedPlayer = ghost._data.interestedPlayer; WriteLine(4, $"InterestedPlayer:{(interestedPlayer == null ? "NULL" : interestedPlayer.player.PlayerId)}"); foreach (var player in ghost._data.players.Values) { WriteLine(4, $"{player.player.PlayerId}"); WriteLine(4, $"- isPlayerVisible:{player.sensor.isPlayerVisible}"); WriteLine(4, $"- isPlayerHeldLanternVisible:{player.sensor.isPlayerHeldLanternVisible}"); WriteLine(4, $"- isIlluminatedByPlayer:{player.sensor.isIlluminatedByPlayer}"); WriteLine(4, $"- isPlayerLocationKnown:{player.isPlayerLocationKnown}"); WriteLine(4, $"- timeSincePlayerLocationKnown:{player.timeSincePlayerLocationKnown}"); var lantern = player.player.AssignedSimulationLantern; if (lantern != null) { WriteLine(4, $"- IsHeldByPlayer:{lantern.AttachedObject.GetLanternController().IsHeldByPlayer()}"); WriteLine(4, $"- Concealed:{lantern.AttachedObject.GetLanternController().IsConcealed()}"); } else { WriteLine(4, "- LANTERN NULL", Color.red); } var playerCamera = player.player.Camera; if (playerCamera != null) { var position = playerCamera.transform.position; WriteLine(4, $"- Camera in vision cone:{ghost.AttachedObject._sensors.CheckPointInVisionCone(position)}"); WriteLine(4, $"- CheckLineOccluded:{ghost.AttachedObject._sensors.CheckLineOccluded(ghost.AttachedObject._sensors._sightOrigin.position, position)}"); } else { WriteLine(4, "- CAMERA NULL", Color.red); } } } } /* #region Column4 - Quantum Object Possesion foreach (var player in QSBPlayerManager.PlayerList) { WriteLine(4, $"- {player}"); var allQuantumObjects = QSBWorldSync.GetWorldObjects(); var ownedQuantumObjects = allQuantumObjects.Where(x => x.ControllingPlayer == player.PlayerId); foreach (var quantumObject in ownedQuantumObjects) { WriteLine(4, $"{quantumObject.Name} ({quantumObject.ObjectId})"); WriteLine(4, $" - IsIlluminated:{quantumObject.GetVisibilityObject().IsIlluminated()}"); WriteLine(4, $" - IsVisible:{quantumObject.GetVisibilityObject().IsVisible()}"); foreach (var tracker in quantumObject.GetVisibilityObject()._visibilityTrackers) { WriteLine(4, $" - {tracker.name}"); WriteLine(4, $" - IsVisible:{tracker.IsVisible()}"); WriteLine(4, $" - IsVisibleUsingCameraFrustum:{tracker.IsVisibleUsingCameraFrustum()}"); } } } WriteLine(4, ""); WriteLine(4, "Enabled QuantumObjects :"); foreach (var qo in QSBWorldSync.GetWorldObjects()) { if (qo.ControllingPlayer != 0) { continue; } if (qo.IsEnabled) { WriteLine(4, $"{qo.Name} ({qo.ObjectId})"); } } #endregion */ } private static void DrawWorldObjectLabels() { if (QSBCore.DebugSettings.DrawLabels) { var list = DebugActions.WorldObjectSelection == null ? QSBWorldSync.GetWorldObjects() : QSBWorldSync.GetWorldObjects(DebugActions.WorldObjectSelection); foreach (var obj in list) { if (obj.ShouldDisplayDebug()) { DrawLabel(obj.AttachedObject.transform, obj.ReturnLabel()); } } } else if (QSBCore.DebugSettings.DrawGhostAI) { foreach (var obj in QSBWorldSync.GetWorldObjects()) { if (obj.ShouldDisplayDebug()) { DrawLabel(obj.AttachedObject.transform, obj.ReturnLabel()); } } } } public void OnRenderObject() => DrawWorldObjectLines(); private static void DrawWorldObjectLines() { if (QSBCore.DebugSettings.DrawLines) { var list = DebugActions.WorldObjectSelection == null ? QSBWorldSync.GetWorldObjects() : QSBWorldSync.GetWorldObjects(DebugActions.WorldObjectSelection); foreach (var obj in list) { if (obj.ShouldDisplayDebug()) { obj.DisplayLines(); } } } else if (QSBCore.DebugSettings.DrawGhostAI) { foreach (var obj in QSBWorldSync.GetWorldObjects()) { if (obj.ShouldDisplayDebug()) { obj.DisplayLines(); } } } } public static void DrawLabel(Transform obj, string label) { var camera = Locator.GetPlayerCamera(); if (camera == null) { return; } if (obj == null) { return; } labelGUIStyle.normal.textColor = Color.white; GUI.contentColor = Color.white; var difference = obj.transform.position - camera.transform.position; if (Vector3.Dot(difference.normalized, camera.transform.forward) < 0) { return; } var cheapDistance = difference.sqrMagnitude; if (cheapDistance > MaxLabelDistance * MaxLabelDistance) { return; } var screenPosition = camera.WorldToScreenPoint(obj.position); var distance = screenPosition.z; if (distance <= 0.05f) { return; } if (distance > MaxLabelDistance) { return; } if (screenPosition.x < 0 || screenPosition.x > Screen.width) { return; } if (screenPosition.y < 0 || screenPosition.y > Screen.height) { return; } var mappedFontSize = (int)distance.Map(0, MaxLabelDistance, MaxLabelSize, 0, true); if (mappedFontSize <= 0) { return; } if (mappedFontSize > MaxLabelSize) { return; } labelGUIStyle.fontSize = mappedFontSize; // WorldToScreenPoint's (0,0) is at screen bottom left, GUI's (0,0) is at screen top left. grrrr screenPosition.y = Screen.height - screenPosition.y; GUI.Label(new Rect(screenPosition, Vector2.zero), label, labelGUIStyle); } }