using Godot; using System.IO; using WeavUtils; public partial class DebugGUI : Control { // Other scripts may use us right off the bat, so we make sure we initialize first public DebugGUI() { ProcessPhysicsPriority = int.MinValue; } static DebugGUI Instance; #region Settings public static class Settings { const string DEBUGGUI_SETTINGS_DIR = "DebugGUI/Settings/"; public static void Init() { if (!Engine.IsEditorHint()) return; // Inits defaults or load current if present Load(); ProjectSettings.SetSetting($"{DEBUGGUI_SETTINGS_DIR}{nameof(enableGraphs)}", enableGraphs); ProjectSettings.SetSetting($"{DEBUGGUI_SETTINGS_DIR}{nameof(enableLogs)}", enableLogs); ProjectSettings.SetSetting($"{DEBUGGUI_SETTINGS_DIR}{nameof(backgroundColor)}", backgroundColor); ProjectSettings.SetSetting($"{DEBUGGUI_SETTINGS_DIR}{nameof(scrubberColor)}", scrubberColor); ProjectSettings.SetSetting($"{DEBUGGUI_SETTINGS_DIR}{nameof(graphWidth)}", graphWidth); ProjectSettings.SetSetting($"{DEBUGGUI_SETTINGS_DIR}{nameof(graphHeight)}", graphHeight); ProjectSettings.SetSetting($"{DEBUGGUI_SETTINGS_DIR}{nameof(temporaryLogLifetime)}", temporaryLogLifetime); ProjectSettings.SetAsBasic($"{DEBUGGUI_SETTINGS_DIR}{nameof(enableGraphs)}", true); ProjectSettings.SetAsBasic($"{DEBUGGUI_SETTINGS_DIR}{nameof(enableLogs)}", true); ProjectSettings.SetAsBasic($"{DEBUGGUI_SETTINGS_DIR}{nameof(backgroundColor)}", true); ProjectSettings.SetAsBasic($"{DEBUGGUI_SETTINGS_DIR}{nameof(scrubberColor)}", true); ProjectSettings.SetAsBasic($"{DEBUGGUI_SETTINGS_DIR}{nameof(graphWidth)}", true); ProjectSettings.SetAsBasic($"{DEBUGGUI_SETTINGS_DIR}{nameof(graphHeight)}", true); ProjectSettings.SetAsBasic($"{DEBUGGUI_SETTINGS_DIR}{nameof(temporaryLogLifetime)}", true); var err = ProjectSettings.Save(); if(err != Error.Ok) { GD.PrintErr(err); } } public static void Load() { textFont = ThemeDB.FallbackFont; enableGraphs = ProjectSettings.GetSetting( $"{DEBUGGUI_SETTINGS_DIR}{nameof(enableGraphs)}", true ).AsBool(); enableLogs = ProjectSettings.GetSetting( $"{DEBUGGUI_SETTINGS_DIR}{nameof(enableLogs)}", true ).AsBool(); backgroundColor = ProjectSettings.GetSetting( $"{DEBUGGUI_SETTINGS_DIR}{nameof(backgroundColor)}", new Color(0f, 0f, 0f, 0.7f) ).AsColor(); scrubberColor = ProjectSettings.GetSetting( $"{DEBUGGUI_SETTINGS_DIR}{nameof(scrubberColor)}", new Color(1f, 1f, 0f, 0.7f) ).AsColor(); graphWidth = ProjectSettings.GetSetting( $"{DEBUGGUI_SETTINGS_DIR}{nameof(graphWidth)}", 300 ).AsInt32(); graphHeight = ProjectSettings.GetSetting( $"{DEBUGGUI_SETTINGS_DIR}{nameof(graphHeight)}", 100 ).AsInt32(); temporaryLogLifetime = ProjectSettings.GetSetting( $"{DEBUGGUI_SETTINGS_DIR}{nameof(temporaryLogLifetime)}", 5 ).AsDouble(); } public static bool enableGraphs; public static bool enableLogs; public static Color backgroundColor; public static Color scrubberColor; public static int graphWidth; public static int graphHeight; public static double temporaryLogLifetime; public static Font textFont; } #endregion #region Graph /// /// Set the properties of a graph. /// /// The graph's key /// The graph's label /// Value at the bottom of the graph box /// Value at the top of the graph box /// The graph's ordinal position on screen /// The graph's color public static void SetGraphProperties(object key, string label, float min, float max, int group, Color color, bool autoScale) { if (Settings.enableGraphs) Instance?.graphWindow.SetGraphProperties(key, label, min, max, group, color, autoScale); } /// /// Set the properties of a graph. /// /// The graph's key /// The graph's label /// Value at the bottom of the graph box /// Value at the top of the graph box /// The graph's ordinal position on screen /// The graph's color public static void SetGraphProperties(GodotObject key, string label, float min, float max, int group, Color color, bool autoScale) { SetGraphProperties((object)key, label, min, max, group, color, autoScale); } /// /// Add a data point to a graph. /// /// The graph's key /// Value to be added public static void Graph(object key, float val) { if (Settings.enableGraphs) Instance?.graphWindow.Graph(key, val); } /// /// Add a data point to a graph. /// /// The graph's key /// Value to be added public static void Graph(GodotObject key, float val) { Graph((object)key, val); } /// /// Remove an existing graph. /// /// The graph's key public static void RemoveGraph(object key) { if (Settings.enableGraphs) Instance?.graphWindow.RemoveGraph(key); } /// /// Remove an existing graph. /// /// The graph's key public static void RemoveGraph(GodotObject key) { RemoveGraph((object)key); } /// /// Resets a graph's data. /// /// The graph's key public static void ClearGraph(object key) { if (Settings.enableGraphs) Instance?.graphWindow.ClearGraph(key); } /// /// Resets a graph's data. /// /// The graph's key public static void ClearGraph(GodotObject key) { ClearGraph((object)key); } /// /// Export graphs to a json file. See path in log. /// public static void ExportGraphs() { if (Instance == null || !Settings.enableGraphs) return; string dateTimeStr = Time.GetDatetimeStringFromSystem().Replace(':', '-'); string filename = $"debuggui_graph_export_{dateTimeStr}.json"; using var file = Godot.FileAccess.Open( "user://" + filename, Godot.FileAccess.ModeFlags.Write ); if (file == null) { GD.Print("DebugGUI graph export failed: " + Godot.FileAccess.GetOpenError()); } else { file.StoreString(Instance.graphWindow.ToJson()); GD.Print($"Wrote graph data to {Path.Combine(OS.GetUserDataDir(), filename)}"); } } #endregion #region Log /// /// Create or update an existing message with the same key. /// public static void LogPersistent(object key, string message) { if (Settings.enableLogs) Instance?.logWindow.LogPersistent(key, message); } /// /// Create or update an existing message with the same key. /// public static void LogPersistent(GodotObject key, string message) { LogPersistent((object)key, message); } /// /// Remove an existing persistent message. /// public static void RemovePersistent(object key) { if (Settings.enableLogs) Instance?.logWindow.RemovePersistent(key); } /// /// Remove an existing persistent message. /// public static void RemovePersistent(GodotObject key) { RemovePersistent((object)key); } /// /// Clears all persistent logs. /// public static void ClearPersistent() { if (Settings.enableLogs) Instance?.logWindow.ClearPersistent(); } /// /// Print a temporary message. /// public static void Log(object message) { Log(message.ToString()); } /// /// Print a temporary message. /// public static void Log(string message) { if (Settings.enableLogs) Instance?.logWindow.Log(message); } #endregion /// /// Re-scans for DebugGUI attribute holders (i.e. [DebugGUIGraph] and [DebugGUIPrint]) /// public static void ForceReinitializeAttributes() { if (Instance == null) return; Instance.graphWindow.ReinitializeAttributes(); Instance.logWindow.ReinitializeAttributes(); } GraphWindow graphWindow; LogWindow logWindow; public override void _Ready() { Instance = this; Settings.Load(); if (Settings.enableGraphs) { AddChild(graphWindow = new()); } if (Settings.enableGraphs) { AddChild(logWindow = new()); } } }