Skip to main content
Glama
Singtaa
by Singtaa
ConsoleCapture.cs6.46 kB
using System; using System.Collections.Generic; using System.Reflection; using System.Text; using UnityEngine; namespace UnityMcp { /// <summary> /// Captures Unity console logs for MCP access. /// Uses reflection to access Unity's internal console when possible, /// falls back to Application.logMessageReceivedThreaded otherwise. /// </summary> public static class ConsoleCapture { // MARK: Fallback struct FallbackEntry { public LogType type; public string condition; public string stack; public DateTime utc; } static bool _started; static readonly object _lock = new object(); static readonly List<FallbackEntry> _fallback = new List<FallbackEntry>(512); const int MaxFallback = 2000; public static void EnsureStarted() { if (_started) return; _started = true; // Unsubscribe first to avoid duplicates after reload Application.logMessageReceivedThreaded -= OnLogThreaded; Application.logMessageReceivedThreaded += OnLogThreaded; } public static void Shutdown() { if (!_started) return; _started = false; Application.logMessageReceivedThreaded -= OnLogThreaded; lock (_lock) { _fallback.Clear(); } } static void OnLogThreaded(string condition, string stackTrace, LogType type) { if (!_started) return; lock (_lock) { _fallback.Add(new FallbackEntry { type = type, condition = condition, stack = stackTrace, utc = DateTime.UtcNow }); if (_fallback.Count > MaxFallback) { _fallback.RemoveRange(0, _fallback.Count - MaxFallback); } } } public static string GetFallbackText(int maxEntries) { List<FallbackEntry> copy; lock (_lock) { copy = new List<FallbackEntry>(_fallback); } var take = Math.Min(maxEntries, copy.Count); var start = Math.Max(0, copy.Count - take); var sb = new StringBuilder(); sb.AppendLine("source=fallback"); sb.AppendLine($"count={take}"); for (var i = start; i < copy.Count; i++) { var e = copy[i]; sb.AppendLine($"[{e.utc:O}] [{e.type}] {e.condition}"); if (!string.IsNullOrEmpty(e.stack)) { sb.AppendLine(e.stack); } } return sb.ToString(); } // MARK: UnityConsole public static bool TryReadUnityConsole(int maxEntries, out string text) { text = null; try { var logEntriesType = FindType("UnityEditor.LogEntries"); var logEntryType = FindType("UnityEditor.LogEntry"); if (logEntriesType == null || logEntryType == null) return false; var getCount = logEntriesType.GetMethod("GetCount", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); var start = logEntriesType.GetMethod("StartGettingEntries", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); var end = logEntriesType.GetMethod("EndGettingEntries", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); var getEntry = logEntriesType.GetMethod("GetEntryInternal", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static) ?? logEntriesType.GetMethod("GetEntry", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static); if (getCount == null || getEntry == null) return false; var count = (int)getCount.Invoke(null, null); if (count <= 0) { text = "source=unity-console\ncount=0"; return true; } var take = Math.Min(maxEntries, count); var startIndex = Math.Max(0, count - take); var conditionField = logEntryType.GetField("condition", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) ?? logEntryType.GetField("message", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); var stackField = logEntryType.GetField("stacktrace", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance) ?? logEntryType.GetField("stackTrace", BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); start?.Invoke(null, null); var sb = new StringBuilder(); sb.AppendLine("source=unity-console"); sb.AppendLine($"count={take}"); var entry = Activator.CreateInstance(logEntryType); for (var i = startIndex; i < count; i++) { getEntry.Invoke(null, new object[] { i, entry }); var cond = conditionField?.GetValue(entry) as string ?? ""; var stack = stackField?.GetValue(entry) as string ?? ""; sb.AppendLine(cond); if (!string.IsNullOrEmpty(stack)) { sb.AppendLine(stack); } } end?.Invoke(null, null); text = sb.ToString(); return true; } catch { return false; } } static Type FindType(string fullName) { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (var asm in assemblies) { try { var t = asm.GetType(fullName, throwOnError: false); if (t != null) return t; } catch { } } return null; } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Singtaa/UnityMCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server