Replace command dispatcher with CommandRegistry, allow to add custom command handlers. (#261)

* Replace hard coded command dispatcher to command registry

* Bug fixed.

* bug fixed.

* bug fixed.

* Bug fixed.

* Fix tests.
main
许兴逸 2025-09-27 06:05:17 +08:00 committed by GitHub
parent b57a3b8500
commit da91f256a2
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 40 additions and 46 deletions

View File

@ -8,18 +8,29 @@ namespace MCPForUnityTests.Editor.Tools
public class CommandRegistryTests public class CommandRegistryTests
{ {
[Test] [Test]
public void GetHandler_ReturnsNull_ForUnknownCommand() public void GetHandler_ThrowException_ForUnknownCommand()
{ {
var unknown = "HandleDoesNotExist"; var unknown = "HandleDoesNotExist";
var handler = CommandRegistry.GetHandler(unknown); try
Assert.IsNull(handler, "Expected null handler for unknown command name."); {
var handler = CommandRegistry.GetHandler(unknown);
Assert.Fail("Should throw InvalidOperation for unknown handler.");
}
catch (InvalidOperationException)
{
}
catch
{
Assert.Fail("Should throw InvalidOperation for unknown handler.");
}
} }
[Test] [Test]
public void GetHandler_ReturnsManageGameObjectHandler() public void GetHandler_ReturnsManageGameObjectHandler()
{ {
var handler = CommandRegistry.GetHandler("HandleManageGameObject"); var handler = CommandRegistry.GetHandler("manage_gameobject");
Assert.IsNotNull(handler, "Expected a handler for HandleManageGameObject."); Assert.IsNotNull(handler, "Expected a handler for manage_gameobject.");
var methodInfo = handler.Method; var methodInfo = handler.Method;
Assert.AreEqual("HandleCommand", methodInfo.Name, "Handler method name should be HandleCommand."); Assert.AreEqual("HandleCommand", methodInfo.Name, "Handler method name should be HandleCommand.");

View File

@ -314,7 +314,7 @@ namespace MCPForUnity.Editor
const int maxImmediateRetries = 3; const int maxImmediateRetries = 3;
const int retrySleepMs = 75; const int retrySleepMs = 75;
int attempt = 0; int attempt = 0;
for (;;) for (; ; )
{ {
try try
{ {
@ -755,7 +755,7 @@ namespace MCPForUnity.Editor
{ {
byte[] header = await ReadExactAsync(stream, 8, timeoutMs, cancel).ConfigureAwait(false); byte[] header = await ReadExactAsync(stream, 8, timeoutMs, cancel).ConfigureAwait(false);
ulong payloadLen = ReadUInt64BigEndian(header); ulong payloadLen = ReadUInt64BigEndian(header);
if (payloadLen > MaxFrameBytes) if (payloadLen > MaxFrameBytes)
{ {
throw new System.IO.IOException($"Invalid framed length: {payloadLen}"); throw new System.IO.IOException($"Invalid framed length: {payloadLen}");
} }
@ -1040,25 +1040,7 @@ namespace MCPForUnity.Editor
// Use JObject for parameters as the new handlers likely expect this // Use JObject for parameters as the new handlers likely expect this
JObject paramsObject = command.@params ?? new JObject(); JObject paramsObject = command.@params ?? new JObject();
// Route command based on the new tool structure from the refactor plan object result = CommandRegistry.GetHandler(command.type)(paramsObject);
object result = command.type switch
{
// Maps the command type (tool name) to the corresponding handler's static HandleCommand method
// Assumes each handler class has a static method named 'HandleCommand' that takes JObject parameters
"manage_script" => ManageScript.HandleCommand(paramsObject),
// Run scene operations on the main thread to avoid deadlocks/hangs (with diagnostics under debug flag)
"manage_scene" => HandleManageScene(paramsObject)
?? throw new TimeoutException($"manage_scene timed out after {FrameIOTimeoutMs} ms on main thread"),
"manage_editor" => ManageEditor.HandleCommand(paramsObject),
"manage_gameobject" => ManageGameObject.HandleCommand(paramsObject),
"manage_asset" => ManageAsset.HandleCommand(paramsObject),
"manage_shader" => ManageShader.HandleCommand(paramsObject),
"read_console" => ReadConsole.HandleCommand(paramsObject),
"manage_menu_item" => ManageMenuItem.HandleCommand(paramsObject),
_ => throw new ArgumentException(
$"Unknown or unsupported command type: {command.type}"
),
};
// Standard success response format // Standard success response format
var response = new { status = "success", result }; var response = new { status = "success", result };

View File

@ -14,14 +14,14 @@ namespace MCPForUnity.Editor.Tools
// to the corresponding static HandleCommand method in the appropriate tool class. // to the corresponding static HandleCommand method in the appropriate tool class.
private static readonly Dictionary<string, Func<JObject, object>> _handlers = new() private static readonly Dictionary<string, Func<JObject, object>> _handlers = new()
{ {
{ "HandleManageScript", ManageScript.HandleCommand }, { "manage_script", ManageScript.HandleCommand },
{ "HandleManageScene", ManageScene.HandleCommand }, { "manage_scene", ManageScene.HandleCommand },
{ "HandleManageEditor", ManageEditor.HandleCommand }, { "manage_editor", ManageEditor.HandleCommand },
{ "HandleManageGameObject", ManageGameObject.HandleCommand }, { "manage_gameobject", ManageGameObject.HandleCommand },
{ "HandleManageAsset", ManageAsset.HandleCommand }, { "manage_asset", ManageAsset.HandleCommand },
{ "HandleReadConsole", ReadConsole.HandleCommand }, { "read_console", ReadConsole.HandleCommand },
{ "HandleManageMenuItem", ManageMenuItem.HandleCommand }, { "manage_menu_item", ManageMenuItem.HandleCommand },
{ "HandleManageShader", ManageShader.HandleCommand}, { "manage_shader", ManageShader.HandleCommand},
}; };
/// <summary> /// <summary>
@ -31,17 +31,18 @@ namespace MCPForUnity.Editor.Tools
/// <returns>The command handler function if found, null otherwise.</returns> /// <returns>The command handler function if found, null otherwise.</returns>
public static Func<JObject, object> GetHandler(string commandName) public static Func<JObject, object> GetHandler(string commandName)
{ {
// Use case-insensitive comparison for flexibility, although Python side should be consistent if (!_handlers.TryGetValue(commandName, out var handler))
return _handlers.TryGetValue(commandName, out var handler) ? handler : null; {
// Consider adding logging here if a handler is not found throw new InvalidOperationException(
/* $"Unknown or unsupported command type: {commandName}");
if (_handlers.TryGetValue(commandName, out var handler)) {
return handler;
} else {
UnityEngine.Debug.LogError($\"[CommandRegistry] No handler found for command: {commandName}\");
return null;
} }
*/
return handler;
}
public static void Add(string commandName, Func<JObject, object> handler)
{
_handlers.Add(commandName, handler);
} }
} }
} }