using System; using System.Collections.Generic; using System.Diagnostics; using UnityEngine; using UnityEngine.SceneManagement; #if UNITY_EDITOR using UnityEditor; #endif using System.Reflection; using System.Linq; namespace SynapticPro { /// /// Real-time Execution State Monitoring Class /// Monitors Unity Editor execution state, performance, and memory usage /// public static class NexusRuntimeMonitor { private static float lastFPS = 0f; private static long lastGCMemory = 0L; private static DateTime lastUpdateTime = DateTime.Now; /// /// Get Unity execution state information /// public static string GetRuntimeStatus(Dictionary parameters) { try { var includePerformance = parameters.GetValueOrDefault("includePerformance", "true") == "true"; var includeMemory = parameters.GetValueOrDefault("includeMemory", "true") == "true"; var includeErrors = parameters.GetValueOrDefault("includeErrors", "true") == "true"; var status = new Dictionary { ["playMode"] = Application.isPlaying, ["isEditor"] = Application.isEditor, ["isPaused"] = GetEditorPausedState(), ["isCompiling"] = GetEditorCompilingState(), ["platform"] = Application.platform.ToString(), ["unityVersion"] = Application.unityVersion, ["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") }; if (includePerformance) { status["performance"] = GetPerformanceData(); } if (includeMemory) { status["memory"] = GetMemoryData(); } if (includeErrors) { status["errors"] = GetErrorStatus(); } return FormatStatusReport(status); } catch (Exception e) { return $"Error getting runtime status: {e.Message}"; } } /// /// Get performance metrics /// public static string GetPerformanceMetrics(Dictionary parameters) { try { var duration = float.Parse(parameters.GetValueOrDefault("duration", "5")); var includeFrameTime = parameters.GetValueOrDefault("includeFrameTime", "true") == "true"; var includeGPUUsage = parameters.GetValueOrDefault("includeGPUUsage", "true") == "true"; var includeBatches = parameters.GetValueOrDefault("includeBatches", "true") == "true"; var metrics = new Dictionary(); // FPS calculation if (Application.isPlaying) { var currentFPS = 1.0f / Time.unscaledDeltaTime; lastFPS = currentFPS; metrics["fps"] = Math.Round(currentFPS, 1); metrics["deltaTime"] = Math.Round(Time.unscaledDeltaTime * 1000, 2); // ms } else { metrics["fps"] = "Editor Mode"; metrics["deltaTime"] = "N/A"; } if (includeFrameTime) { metrics["frameTime"] = Application.isPlaying ? Math.Round(Time.unscaledDeltaTime * 1000, 2) : 0; metrics["timeScale"] = Time.timeScale; metrics["fixedDeltaTime"] = Time.fixedDeltaTime; } if (includeGPUUsage) { // Use Profiler API (Editor only) if (Application.isEditor) { metrics["gpuMemory"] = SystemInfo.graphicsMemorySize + " MB"; metrics["graphicsDevice"] = SystemInfo.graphicsDeviceName; metrics["graphicsAPI"] = SystemInfo.graphicsDeviceType.ToString(); } } if (includeBatches) { // Rendering statistics (game runtime only) if (Application.isPlaying) { metrics["triangles"] = "Available in Game View"; metrics["batches"] = "Available in Game View"; } else { metrics["triangles"] = "N/A (Editor Mode)"; metrics["batches"] = "N/A (Editor Mode)"; } } // System information metrics["systemMemory"] = SystemInfo.systemMemorySize + " MB"; metrics["processorCount"] = SystemInfo.processorCount; metrics["processorType"] = SystemInfo.processorType; return FormatPerformanceReport(metrics); } catch (Exception e) { return $"Error getting performance metrics: {e.Message}"; } } /// /// Get detailed memory usage /// public static string GetMemoryUsage(Dictionary parameters) { try { var includeBreakdown = parameters.GetValueOrDefault("includeBreakdown", "true") == "true"; var includeGC = parameters.GetValueOrDefault("includeGC", "true") == "true"; var includeProfiler = parameters.GetValueOrDefault("includeProfiler", "false") == "true"; var memory = new Dictionary(); // Basic memory information var gcMemory = GC.GetTotalMemory(false); memory["gcMemory"] = FormatBytes(gcMemory); memory["gcGeneration0"] = GC.CollectionCount(0); memory["gcGeneration1"] = GC.CollectionCount(1); memory["gcGeneration2"] = GC.CollectionCount(2); if (includeGC) { var memoryDiff = gcMemory - lastGCMemory; memory["memoryDelta"] = FormatBytes(memoryDiff); lastGCMemory = gcMemory; } // Unity-specific memory information if (Application.isEditor) { memory["unityReserved"] = "Available via Profiler"; memory["gfxDriver"] = SystemInfo.graphicsMemorySize + " MB"; } if (includeBreakdown) { // Memory usage by asset type (estimated) var textures = Resources.FindObjectsOfTypeAll(); var meshes = Resources.FindObjectsOfTypeAll(); var audioClips = Resources.FindObjectsOfTypeAll(); memory["textureCount"] = textures.Length; memory["meshCount"] = meshes.Length; memory["audioClipCount"] = audioClips.Length; // Estimated memory size long textureMemory = 0; foreach (var tex in textures) { if (tex != null) { textureMemory += UnityEngine.Profiling.Profiler.GetRuntimeMemorySizeLong(tex); } } memory["textureMemory"] = FormatBytes(textureMemory); } if (includeProfiler && Application.isPlaying) { memory["profilerMemory"] = UnityEngine.Profiling.Profiler.GetTotalAllocatedMemoryLong(); memory["profilerReserved"] = UnityEngine.Profiling.Profiler.GetTotalReservedMemoryLong(); } return FormatMemoryReport(memory); } catch (Exception e) { return $"Error getting memory usage: {e.Message}"; } } /// /// Get current error status /// public static string GetErrorStatus(Dictionary parameters) { try { var includeWarnings = parameters.GetValueOrDefault("includeWarnings", "true") == "true"; var includeStackTrace = parameters.GetValueOrDefault("includeStackTrace", "false") == "true"; var maxErrors = int.Parse(parameters.GetValueOrDefault("maxErrors", "20")); var isCompiling = GetEditorCompilingState(); var hasCompileErrors = GetEditorCompileErrorState(); var errors = new Dictionary { ["isCompiling"] = isCompiling, ["hasCompileErrors"] = hasCompileErrors, ["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss") }; // Console log retrieval is limited, so only basic information errors["status"] = isCompiling ? "Compiling..." : hasCompileErrors ? "Compile Errors" : "No Errors"; return FormatErrorReport(errors); } catch (Exception e) { return $"Error getting error status: {e.Message}"; } } /// /// Start monitoring Play state changes /// public static string MonitorPlayState(Dictionary parameters) { try { var enableNotifications = parameters.GetValueOrDefault("enableNotifications", "true") == "true"; var includeTimestamp = parameters.GetValueOrDefault("includeTimestamp", "true") == "true"; if (enableNotifications) { #if UNITY_EDITOR // Set up EditorApplication event handlers EditorApplication.playModeStateChanged += OnPlayModeStateChanged; #endif } var currentState = new Dictionary { ["isPlaying"] = Application.isPlaying, #if UNITY_EDITOR ["isPaused"] = EditorApplication.isPaused, ["isCompiling"] = EditorApplication.isCompiling, #else ["isPaused"] = false, ["isCompiling"] = false, #endif ["monitoringEnabled"] = enableNotifications }; if (includeTimestamp) { currentState["timestamp"] = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); } return FormatPlayStateReport(currentState); } catch (Exception e) { return $"Error setting up play state monitoring: {e.Message}"; } } /// /// Get build status /// public static string GetBuildStatus(Dictionary parameters) { try { var includeSettings = parameters.GetValueOrDefault("includeSettings", "true") == "true"; var includeErrors = parameters.GetValueOrDefault("includeErrors", "true") == "true"; var buildInfo = new Dictionary(); if (includeSettings) { #if UNITY_EDITOR buildInfo["targetPlatform"] = EditorUserBuildSettings.activeBuildTarget.ToString(); buildInfo["developmentBuild"] = EditorUserBuildSettings.development; buildInfo["scriptDebugging"] = EditorUserBuildSettings.allowDebugging; buildInfo["buildAppBundle"] = EditorUserBuildSettings.buildAppBundle; #else buildInfo["targetPlatform"] = Application.platform.ToString(); buildInfo["developmentBuild"] = UnityEngine.Debug.isDebugBuild; buildInfo["scriptDebugging"] = false; buildInfo["buildAppBundle"] = false; #endif } #if UNITY_EDITOR // Scene information var scenes = EditorBuildSettings.scenes; buildInfo["sceneCount"] = scenes.Length; buildInfo["enabledScenes"] = scenes.Count(s => s.enabled); #else buildInfo["sceneCount"] = SceneManager.sceneCountInBuildSettings; buildInfo["enabledScenes"] = SceneManager.sceneCountInBuildSettings; #endif if (includeErrors) { #if UNITY_EDITOR buildInfo["canBuild"] = !EditorApplication.isCompiling && !EditorUtility.scriptCompilationFailed; buildInfo["compilationStatus"] = EditorApplication.isCompiling ? "Compiling" : EditorUtility.scriptCompilationFailed ? "Error" : "Ready"; #else buildInfo["canBuild"] = true; buildInfo["compilationStatus"] = "Ready"; #endif } return FormatBuildReport(buildInfo); } catch (Exception e) { return $"Error getting build status: {e.Message}"; } } // ===== Helper Methods ===== #if UNITY_EDITOR private static void OnPlayModeStateChanged(PlayModeStateChange state) { var message = $"Play Mode State Changed: {state} at {DateTime.Now:HH:mm:ss}"; UnityEngine.Debug.Log($"[Nexus Monitor] {message}"); // Real-time notification via WebSocket is also possible // NexusWebSocketClient.Instance?.SendMessage(new { type = "play_state_changed", state = state.ToString() }); } #endif private static Dictionary GetPerformanceData() { return new Dictionary { ["fps"] = Application.isPlaying ? Math.Round(1.0f / Time.unscaledDeltaTime, 1) : 0, ["frameTime"] = Application.isPlaying ? Math.Round(Time.unscaledDeltaTime * 1000, 2) : 0, ["timeScale"] = Time.timeScale }; } private static Dictionary GetMemoryData() { var gcMemory = GC.GetTotalMemory(false); return new Dictionary { ["gcMemory"] = FormatBytes(gcMemory), ["systemMemory"] = SystemInfo.systemMemorySize + " MB", ["graphicsMemory"] = SystemInfo.graphicsMemorySize + " MB" }; } private static Dictionary GetErrorStatus() { var isCompiling = GetEditorCompilingState(); var hasErrors = GetEditorCompileErrorState(); return new Dictionary { ["isCompiling"] = isCompiling, ["hasErrors"] = hasErrors, ["status"] = isCompiling ? "Compiling" : hasErrors ? "Errors" : "OK" }; } private static string FormatBytes(long bytes) { string[] suffixes = { "B", "KB", "MB", "GB" }; int counter = 0; decimal number = (decimal)bytes; while (Math.Round(number / 1024) >= 1) { number = number / 1024; counter++; } return string.Format("{0:n1} {1}", number, suffixes[counter]); } private static string FormatStatusReport(Dictionary status) { var report = "=== Unity Runtime Status ===\n"; report += $"Play Mode: {status["playMode"]}\n"; report += $"Paused: {status["isPaused"]}\n"; report += $"Compiling: {status["isCompiling"]}\n"; report += $"Platform: {status["platform"]}\n"; report += $"Unity Version: {status["unityVersion"]}\n"; report += $"Timestamp: {status["timestamp"]}\n"; if (status.ContainsKey("performance")) { var perf = (Dictionary)status["performance"]; report += $"\n--- Performance ---\n"; report += $"FPS: {perf["fps"]}\n"; report += $"Frame Time: {perf["frameTime"]} ms\n"; report += $"Time Scale: {perf["timeScale"]}\n"; } if (status.ContainsKey("memory")) { var mem = (Dictionary)status["memory"]; report += $"\n--- Memory ---\n"; report += $"GC Memory: {mem["gcMemory"]}\n"; report += $"System Memory: {mem["systemMemory"]}\n"; report += $"Graphics Memory: {mem["graphicsMemory"]}\n"; } if (status.ContainsKey("errors")) { var err = (Dictionary)status["errors"]; report += $"\n--- Status ---\n"; report += $"Compilation: {err["status"]}\n"; } return report; } private static string FormatPerformanceReport(Dictionary metrics) { var report = "=== Performance Metrics ===\n"; foreach (var kvp in metrics) { report += $"{kvp.Key}: {kvp.Value}\n"; } return report; } private static string FormatMemoryReport(Dictionary memory) { var report = "=== Memory Usage ===\n"; foreach (var kvp in memory) { report += $"{kvp.Key}: {kvp.Value}\n"; } return report; } private static string FormatErrorReport(Dictionary errors) { var report = "=== Error Status ===\n"; foreach (var kvp in errors) { report += $"{kvp.Key}: {kvp.Value}\n"; } return report; } private static string FormatPlayStateReport(Dictionary state) { var report = "=== Play State Monitoring ===\n"; foreach (var kvp in state) { report += $"{kvp.Key}: {kvp.Value}\n"; } return report; } private static string FormatBuildReport(Dictionary buildInfo) { var report = "=== Build Status ===\n"; foreach (var kvp in buildInfo) { report += $"{kvp.Key}: {kvp.Value}\n"; } return report; } /// /// Safely access Editor-only APIs using Reflection /// private static bool GetEditorPausedState() { try { var editorAppType = Type.GetType("UnityEditor.EditorApplication, UnityEditor"); if (editorAppType != null) { var isPausedProperty = editorAppType.GetProperty("isPaused"); if (isPausedProperty != null) { return (bool)isPausedProperty.GetValue(null); } } } catch { // Return false if Editor API is not available } return false; } private static bool GetEditorCompilingState() { try { var editorAppType = Type.GetType("UnityEditor.EditorApplication, UnityEditor"); if (editorAppType != null) { var isCompilingProperty = editorAppType.GetProperty("isCompiling"); if (isCompilingProperty != null) { return (bool)isCompilingProperty.GetValue(null); } } } catch { // Return false if Editor API is not available } return false; } private static bool GetEditorCompileErrorState() { try { var editorUtilityType = Type.GetType("UnityEditor.EditorUtility, UnityEditor"); if (editorUtilityType != null) { var scriptCompilationFailedProperty = editorUtilityType.GetProperty("scriptCompilationFailed"); if (scriptCompilationFailedProperty != null) { return (bool)scriptCompilationFailedProperty.GetValue(null); } } } catch { // Return false if Editor API is not available } return false; } } }