using System.Threading.Tasks; using Newtonsoft.Json.Linq; using Stopwatch = System.Diagnostics.Stopwatch; namespace io.github.hatayama.uLoopMCP { /// /// Class specialized in handling Unity API calls /// Supports new command-based structure /// /// Design document reference: Packages/src/Editor/ARCHITECTURE.md /// /// Related classes: /// - UnityCommandRegistry: Registry that manages all available Unity commands /// - CustomCommandManager: Provides access to the command registry singleton /// - JsonRpcProcessor: Receives JSON-RPC requests and delegates to this handler /// - IUnityCommand: Interface implemented by all command classes /// - AbstractUnityCommand: Base class for all Unity commands /// - BaseCommandResponse: Base response type for all commands /// - MainThreadSwitcher: Ensures command execution on Unity's main thread /// /// Command execution flow: /// 1. JsonRpcProcessor receives request from TypeScript server /// 2. Delegates to ExecuteCommand method with command name and parameters /// 3. Looks up command in registry and executes asynchronously /// 4. Returns command response or error information /// public static class UnityApiHandler { /// /// Get command registry /// Use this registry when adding new commands /// public static UnityToolRegistry CommandRegistry => CustomToolManager.GetRegistry(); /// /// Generic command execution method /// Uses new command-based structure /// /// Command name /// Parameters /// Execution result public static async Task ExecuteCommandAsync(string commandName, JToken paramsToken) { Stopwatch registryAcquireStopwatch = Stopwatch.StartNew(); UnityToolRegistry registry = CustomToolManager.GetRegistry(); registryAcquireStopwatch.Stop(); Stopwatch registryExecuteStopwatch = Stopwatch.StartNew(); BaseToolResponse response = await registry.ExecuteToolAsync(commandName, paramsToken); registryExecuteStopwatch.Stop(); AppendExecuteDynamicCodeTimingsIfSupported( response, $"[Perf] RegistryAcquire: {registryAcquireStopwatch.Elapsed.TotalMilliseconds:F1}ms", $"[Perf] RegistryExecute: {registryExecuteStopwatch.Elapsed.TotalMilliseconds:F1}ms"); return response; } private static void AppendExecuteDynamicCodeTimingsIfSupported( BaseToolResponse response, params string[] timingEntries) { if (response is not ExecuteDynamicCodeResponse executeDynamicCodeResponse) { return; } if (executeDynamicCodeResponse.Timings == null) { executeDynamicCodeResponse.Timings = new System.Collections.Generic.List(); } executeDynamicCodeResponse.Timings.AddRange(timingEntries); } } }