From 219c1ab7b0f0d8a2b8244ebf7a0a34f4955411da Mon Sep 17 00:00:00 2001 From: Ange-Marie MAURIN Date: Sun, 26 Apr 2026 17:30:10 +0200 Subject: [PATCH] adding "init claude code" fonctionality --- INSTALLER.md | 14 +- PythonServer/unreal_mcp.log | 59 --- PythonServer/unreal_mcp.log.bck | 194 ------- .../Commands/UnrealMCPBlueprintCommands.cpp | 115 +++-- .../UnrealMCPBlueprintNodeCommands.cpp | 68 ++- .../Private/Commands/UnrealMCPCommonUtils.cpp | 41 +- .../Commands/UnrealMCPEditorCommands.cpp | 3 +- .../Commands/UnrealMCPProjectCommands.cpp | 1 + .../Private/Commands/UnrealMCPUMGCommands.cpp | 1 + .../UnrealMCP/Private/MCPServerRunnable.cpp | 81 +-- Source/UnrealMCP/Private/UnrealMCPBridge.cpp | 23 +- Source/UnrealMCP/Private/UnrealMCPModule.cpp | 7 +- Source/UnrealMCP/Public/UnrealMCPLog.h | 5 + .../Private/ClaudeCodeInitializer.cpp | 487 ++++++++++++++++++ .../Private/ClaudeCodeMenuExtender.cpp | 48 ++ .../Private/ClaudeCodeResultDialog.cpp | 138 +++++ .../Private/UnrealMCPSetupModule.cpp | 40 ++ .../Public/ClaudeCodeInitializer.h | 27 + .../Public/ClaudeCodeMenuExtender.h | 15 + .../Public/ClaudeCodeResultDialog.h | 27 + .../UnrealMCPSetup/Public/ClaudeCodeTypes.h | 16 + .../UnrealMCPSetup/Public/UnrealMCPSetupLog.h | 5 + .../Public/UnrealMCPSetupModule.h | 14 + Source/UnrealMCPSetup/UnrealMCPSetup.Build.cs | 23 + UnrealMCP.uplugin | 10 + 25 files changed, 1041 insertions(+), 421 deletions(-) delete mode 100644 PythonServer/unreal_mcp.log delete mode 100644 PythonServer/unreal_mcp.log.bck create mode 100644 Source/UnrealMCP/Public/UnrealMCPLog.h create mode 100644 Source/UnrealMCPSetup/Private/ClaudeCodeInitializer.cpp create mode 100644 Source/UnrealMCPSetup/Private/ClaudeCodeMenuExtender.cpp create mode 100644 Source/UnrealMCPSetup/Private/ClaudeCodeResultDialog.cpp create mode 100644 Source/UnrealMCPSetup/Private/UnrealMCPSetupModule.cpp create mode 100644 Source/UnrealMCPSetup/Public/ClaudeCodeInitializer.h create mode 100644 Source/UnrealMCPSetup/Public/ClaudeCodeMenuExtender.h create mode 100644 Source/UnrealMCPSetup/Public/ClaudeCodeResultDialog.h create mode 100644 Source/UnrealMCPSetup/Public/ClaudeCodeTypes.h create mode 100644 Source/UnrealMCPSetup/Public/UnrealMCPSetupLog.h create mode 100644 Source/UnrealMCPSetup/Public/UnrealMCPSetupModule.h create mode 100644 Source/UnrealMCPSetup/UnrealMCPSetup.Build.cs diff --git a/INSTALLER.md b/INSTALLER.md index dfa30b3..ad53126 100644 --- a/INSTALLER.md +++ b/INSTALLER.md @@ -147,5 +147,15 @@ J'aimerais automatiser le processus à la création d'un projet unreal en ajouta -reprenons avec ce que nous savons : -Hier dans l'aprés-midi, nous avons, avec l'ancienne organization de dossier (cloné depuis https://github.com/chongdashu/unreal-mcp), réussi à faire fonctionner le server mcp. maintenant que nous avons modifié cette suructure, nous n'arrivons claude code ne lance plus le server mcp. L'ancienne structure se trouve ici : C:\UE_5.7\unreal-mcp\claude-unreal-mcp. le lock. \ No newline at end of file +l'objectif de cette session de travail est de modifier le code du plugin afin que l'utilisateur puisse dans unreal initialiser le projet unreal complet pour être prêt à utiliser claude code. Donc il faut créer en c++ le bouton "init claude code" dont on a parlé hier. Je veux que le code soit bien découpé (pas tous dans un seul fichier). Nous pouvons même créer dans le plugin un nouveau module pour ces fonctionnalités. +Les étapes utilisateur souhaités: +- clone du repo dans le dossier plugin du projet unreal cible. +- l'utilisateur lance unreal (le plugin compile automatiquement) +- Un menu "Claude code" dans la menu bar est présent maintenant dans l'interface unreal. +- Un bouton "Init Claude Code" avec l'infobulle "Initialisation de claude code pour ce projet" est présent dans ce menu. Il permet à l'utilisateur d'initialiser le projet pour l'utilisation de Claude Code. +- Quand l'utilisateur clique ce bouton, une fenêtre de validation s'affiche pour demander : "étes-vous sûr de vouloir initialiser claude code pour ce projet ?". A noter que claude code ne peux être utilisé qu'avec des projet c++. Si ce n'est pas le cas, afficher un message "Claude Code ne peut être initialisé que dans un projet cpp". +- si l'utilisateur valide l'initialisation, le plugin copie le dossier .claude, ainsi que le .mcp.json à la racine du projet unreal en adaptant les chemins en relatif. +- si l'utilisateur ne valide pas. On ne fait rien. +- Le plugin initialise uv et attend que le venv soit prés avant de continuer. +- Quand tout est fini, il affiche une fenêtre (même en cas d'echec) recapitulant les modifications apportées et le status de l'instalattion (si tout c'est bien passé ou si il y a eu un/des problèmes). +Peux tu faire ce travail ? \ No newline at end of file diff --git a/PythonServer/unreal_mcp.log b/PythonServer/unreal_mcp.log deleted file mode 100644 index e392e89..0000000 --- a/PythonServer/unreal_mcp.log +++ /dev/null @@ -1,59 +0,0 @@ -2026-04-24 09:21:44,597 - mcp.server.lowlevel.server - DEBUG - [server.py:145] - Initializing server 'UnrealMCP' -2026-04-24 09:21:44,598 - mcp.server.lowlevel.server - DEBUG - [server.py:384] - Registering handler for ListToolsRequest -2026-04-24 09:21:44,598 - mcp.server.lowlevel.server - DEBUG - [server.py:406] - Registering handler for CallToolRequest -2026-04-24 09:21:44,598 - mcp.server.lowlevel.server - DEBUG - [server.py:252] - Registering handler for ListResourcesRequest -2026-04-24 09:21:44,598 - mcp.server.lowlevel.server - DEBUG - [server.py:286] - Registering handler for ReadResourceRequest -2026-04-24 09:21:44,598 - mcp.server.lowlevel.server - DEBUG - [server.py:222] - Registering handler for PromptListRequest -2026-04-24 09:21:44,598 - mcp.server.lowlevel.server - DEBUG - [server.py:239] - Registering handler for GetPromptRequest -2026-04-24 09:21:44,598 - mcp.server.lowlevel.server - DEBUG - [server.py:267] - Registering handler for ListResourceTemplatesRequest -2026-04-24 09:21:44,614 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully -2026-04-24 09:21:44,626 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully -2026-04-24 09:21:44,649 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully -2026-04-24 09:21:44,650 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully -2026-04-24 09:21:44,657 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully -2026-04-24 09:21:44,659 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport -2026-04-24 09:21:44,666 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor -2026-04-24 09:21:44,668 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up -2026-04-24 09:21:44,668 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557... -2026-04-24 09:21:44,669 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine -2026-04-24 09:21:44,669 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup -2026-04-24 09:21:44,685 - mcp.server.lowlevel.server - DEBUG - [server.py:491] - Received message: root=InitializedNotification(method='notifications/initialized', params=None, jsonrpc='2.0') -2026-04-24 09:21:44,685 - mcp.server.lowlevel.server - DEBUG - [server.py:491] - Received message: -2026-04-24 09:21:44,686 - mcp.server.lowlevel.server - INFO - [server.py:534] - Processing request of type ListToolsRequest -2026-04-24 09:21:44,686 - mcp.server.lowlevel.server - DEBUG - [server.py:537] - Dispatching request of type ListToolsRequest -2026-04-24 09:21:44,686 - mcp.server.lowlevel.server - DEBUG - [server.py:572] - Response sent -2026-04-24 09:21:44,687 - mcp.server.lowlevel.server - DEBUG - [server.py:491] - Received message: -2026-04-24 09:21:44,688 - mcp.server.lowlevel.server - INFO - [server.py:534] - Processing request of type ListPromptsRequest -2026-04-24 09:21:44,688 - mcp.server.lowlevel.server - DEBUG - [server.py:537] - Dispatching request of type ListPromptsRequest -2026-04-24 09:21:44,688 - mcp.server.lowlevel.server - DEBUG - [server.py:572] - Response sent -2026-04-24 09:21:44,688 - mcp.server.lowlevel.server - DEBUG - [server.py:491] - Received message: -2026-04-24 09:21:44,689 - mcp.server.lowlevel.server - INFO - [server.py:534] - Processing request of type ListResourcesRequest -2026-04-24 09:21:44,689 - mcp.server.lowlevel.server - DEBUG - [server.py:537] - Dispatching request of type ListResourcesRequest -2026-04-24 09:21:44,689 - mcp.server.fastmcp.resources.resource_manager - DEBUG - [resource_manager.py:89] - Listing resources -2026-04-24 09:21:44,689 - mcp.server.lowlevel.server - DEBUG - [server.py:572] - Response sent -2026-04-24 09:21:59,189 - mcp.server.lowlevel.server - DEBUG - [server.py:491] - Received message: -2026-04-24 09:21:59,189 - mcp.server.lowlevel.server - INFO - [server.py:534] - Processing request of type CallToolRequest -2026-04-24 09:21:59,189 - mcp.server.lowlevel.server - DEBUG - [server.py:537] - Dispatching request of type CallToolRequest -2026-04-24 09:21:59,191 - mcp.server.lowlevel.server - DEBUG - [server.py:145] - Initializing server 'UnrealMCP' -2026-04-24 09:21:59,191 - mcp.server.lowlevel.server - DEBUG - [server.py:384] - Registering handler for ListToolsRequest -2026-04-24 09:21:59,191 - mcp.server.lowlevel.server - DEBUG - [server.py:406] - Registering handler for CallToolRequest -2026-04-24 09:21:59,191 - mcp.server.lowlevel.server - DEBUG - [server.py:252] - Registering handler for ListResourcesRequest -2026-04-24 09:21:59,191 - mcp.server.lowlevel.server - DEBUG - [server.py:286] - Registering handler for ReadResourceRequest -2026-04-24 09:21:59,191 - mcp.server.lowlevel.server - DEBUG - [server.py:222] - Registering handler for PromptListRequest -2026-04-24 09:21:59,191 - mcp.server.lowlevel.server - DEBUG - [server.py:239] - Registering handler for GetPromptRequest -2026-04-24 09:21:59,191 - mcp.server.lowlevel.server - DEBUG - [server.py:267] - Registering handler for ListResourceTemplatesRequest -2026-04-24 09:21:59,198 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully -2026-04-24 09:21:59,204 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully -2026-04-24 09:21:59,213 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully -2026-04-24 09:21:59,214 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully -2026-04-24 09:21:59,220 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully -2026-04-24 09:21:59,220 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557... -2026-04-24 09:21:59,221 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine -2026-04-24 09:21:59,221 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557... -2026-04-24 09:21:59,221 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine -2026-04-24 09:21:59,221 - UnrealMCP - INFO - [unreal_mcp_server.py:151] - Sending command: {"type": "get_actors_in_level", "params": {}} -2026-04-24 09:21:59,377 - UnrealMCP - INFO - [unreal_mcp_server.py:101] - Received complete response (3465 bytes) -2026-04-24 09:21:59,377 - UnrealMCP - INFO - [unreal_mcp_server.py:159] - Complete response from Unreal: {'status': 'success', 'result': {'actors': [{'name': 'WorldSettings_1', 'class': 'WorldSettings', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'Brush_1', 'class': 'Brush', 'location': [-90, 110, 20], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'DirectionalLight_0', 'class': 'DirectionalLight', 'location': [0, 0, 400], 'rotation': [-49.51711035415601, -10.308600787602971, 112.36067200001975], 'scale': [1, 1.0000000149011612, 1.0000000149011612]}, {'name': 'SkyAtmosphere_0', 'class': 'SkyAtmosphere', 'location': [0, 0, -6000], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'SkyLight_0', 'class': 'SkyLight', 'location': [0, 0, 600], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'ExponentialHeightFog_0', 'class': 'ExponentialHeightFog', 'location': [-5600, -50, -6850], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'VolumetricCloud_0', 'class': 'VolumetricCloud', 'location': [0, 0, 700], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'PlayerStart_0', 'class': 'PlayerStart', 'location': [0, 0, 92], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'StaticMeshActor_0', 'class': 'StaticMeshActor', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [400, 400, 400]}, {'name': 'Floor_0', 'class': 'StaticMeshActor', 'location': [0, 0, -0.5], 'rotation': [0, 0, 0], 'scale': [8, 8, 8]}, {'name': 'DefaultPhysicsVolume_0', 'class': 'DefaultPhysicsVolume', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'GameplayDebuggerPlayerManager_0', 'class': 'GameplayDebuggerPlayerManager', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'ChaosDebugDrawActor', 'class': 'Actor', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'AbstractNavData-Default', 'class': 'AbstractNavData', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}]}} -2026-04-24 09:21:59,377 - UnrealMCP - INFO - [editor_tools.py:35] - Complete response from Unreal: {'status': 'success', 'result': {'actors': [{'name': 'WorldSettings_1', 'class': 'WorldSettings', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'Brush_1', 'class': 'Brush', 'location': [-90, 110, 20], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'DirectionalLight_0', 'class': 'DirectionalLight', 'location': [0, 0, 400], 'rotation': [-49.51711035415601, -10.308600787602971, 112.36067200001975], 'scale': [1, 1.0000000149011612, 1.0000000149011612]}, {'name': 'SkyAtmosphere_0', 'class': 'SkyAtmosphere', 'location': [0, 0, -6000], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'SkyLight_0', 'class': 'SkyLight', 'location': [0, 0, 600], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'ExponentialHeightFog_0', 'class': 'ExponentialHeightFog', 'location': [-5600, -50, -6850], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'VolumetricCloud_0', 'class': 'VolumetricCloud', 'location': [0, 0, 700], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'PlayerStart_0', 'class': 'PlayerStart', 'location': [0, 0, 92], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'StaticMeshActor_0', 'class': 'StaticMeshActor', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [400, 400, 400]}, {'name': 'Floor_0', 'class': 'StaticMeshActor', 'location': [0, 0, -0.5], 'rotation': [0, 0, 0], 'scale': [8, 8, 8]}, {'name': 'DefaultPhysicsVolume_0', 'class': 'DefaultPhysicsVolume', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'GameplayDebuggerPlayerManager_0', 'class': 'GameplayDebuggerPlayerManager', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'ChaosDebugDrawActor', 'class': 'Actor', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}, {'name': 'AbstractNavData-Default', 'class': 'AbstractNavData', 'location': [0, 0, 0], 'rotation': [0, 0, 0], 'scale': [1, 1, 1]}]}} -2026-04-24 09:21:59,377 - UnrealMCP - INFO - [editor_tools.py:40] - Found 14 actors in level -2026-04-24 09:21:59,378 - mcp.server.lowlevel.server - DEBUG - [server.py:572] - Response sent diff --git a/PythonServer/unreal_mcp.log.bck b/PythonServer/unreal_mcp.log.bck deleted file mode 100644 index 496806a..0000000 --- a/PythonServer/unreal_mcp.log.bck +++ /dev/null @@ -1,194 +0,0 @@ -2026-04-23 19:18:15,190 - mcp.server.lowlevel.server - DEBUG - [server.py:162] - Initializing server 'UnrealMCP' -2026-04-23 19:18:15,190 - mcp.server.lowlevel.server - DEBUG - [server.py:439] - Registering handler for ListToolsRequest -2026-04-23 19:18:15,191 - mcp.server.lowlevel.server - DEBUG - [server.py:519] - Registering handler for CallToolRequest -2026-04-23 19:18:15,191 - mcp.server.lowlevel.server - DEBUG - [server.py:301] - Registering handler for ListResourcesRequest -2026-04-23 19:18:15,191 - mcp.server.lowlevel.server - DEBUG - [server.py:336] - Registering handler for ReadResourceRequest -2026-04-23 19:18:15,191 - mcp.server.lowlevel.server - DEBUG - [server.py:263] - Registering handler for PromptListRequest -2026-04-23 19:18:15,195 - mcp.server.lowlevel.server - DEBUG - [server.py:285] - Registering handler for GetPromptRequest -2026-04-23 19:18:15,195 - mcp.server.lowlevel.server - DEBUG - [server.py:321] - Registering handler for ListResourceTemplatesRequest -2026-04-23 19:18:15,216 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully -2026-04-23 19:18:15,222 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully -2026-04-23 19:18:15,230 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully -2026-04-23 19:18:15,231 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully -2026-04-23 19:18:15,237 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully -2026-04-23 19:18:15,237 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport -2026-04-23 19:18:15,254 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor -2026-04-23 19:18:15,256 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up -2026-04-23 19:18:15,256 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557... -2026-04-23 19:18:17,259 - UnrealMCP - ERROR - [unreal_mcp_server.py:67] - Failed to connect to Unreal: [WinError 10061] Aucune connexion n’a pu être établie car l’ordinateur cible l’a expressément refusée -2026-04-23 19:18:17,259 - UnrealMCP - WARNING - [unreal_mcp_server.py:213] - Could not connect to Unreal Engine -2026-04-23 19:18:17,259 - UnrealMCP - WARNING - [unreal_mcp_server.py:248] - Could not connect to Unreal Engine on startup -2026-04-23 19:18:17,261 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down -2026-04-23 19:28:11,152 - mcp.server.lowlevel.server - DEBUG - [server.py:162] - Initializing server 'UnrealMCP' -2026-04-23 19:28:11,152 - mcp.server.lowlevel.server - DEBUG - [server.py:439] - Registering handler for ListToolsRequest -2026-04-23 19:28:11,152 - mcp.server.lowlevel.server - DEBUG - [server.py:519] - Registering handler for CallToolRequest -2026-04-23 19:28:11,153 - mcp.server.lowlevel.server - DEBUG - [server.py:301] - Registering handler for ListResourcesRequest -2026-04-23 19:28:11,153 - mcp.server.lowlevel.server - DEBUG - [server.py:336] - Registering handler for ReadResourceRequest -2026-04-23 19:28:11,153 - mcp.server.lowlevel.server - DEBUG - [server.py:263] - Registering handler for PromptListRequest -2026-04-23 19:28:11,153 - mcp.server.lowlevel.server - DEBUG - [server.py:285] - Registering handler for GetPromptRequest -2026-04-23 19:28:11,153 - mcp.server.lowlevel.server - DEBUG - [server.py:321] - Registering handler for ListResourceTemplatesRequest -2026-04-23 19:28:11,162 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully -2026-04-23 19:28:11,168 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully -2026-04-23 19:28:11,174 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully -2026-04-23 19:28:11,175 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully -2026-04-23 19:28:11,181 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully -2026-04-23 19:28:11,181 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport -2026-04-23 19:28:11,184 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor -2026-04-23 19:28:11,185 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up -2026-04-23 19:28:11,185 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557... -2026-04-23 19:28:11,196 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine -2026-04-23 19:28:11,196 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup -2026-04-23 19:28:11,197 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down -2026-04-23 20:43:13,141 - mcp.server.lowlevel.server - DEBUG - [server.py:162] - Initializing server 'UnrealMCP' -2026-04-23 20:43:13,141 - mcp.server.lowlevel.server - DEBUG - [server.py:439] - Registering handler for ListToolsRequest -2026-04-23 20:43:13,142 - mcp.server.lowlevel.server - DEBUG - [server.py:519] - Registering handler for CallToolRequest -2026-04-23 20:43:13,142 - mcp.server.lowlevel.server - DEBUG - [server.py:301] - Registering handler for ListResourcesRequest -2026-04-23 20:43:13,142 - mcp.server.lowlevel.server - DEBUG - [server.py:336] - Registering handler for ReadResourceRequest -2026-04-23 20:43:13,142 - mcp.server.lowlevel.server - DEBUG - [server.py:263] - Registering handler for PromptListRequest -2026-04-23 20:43:13,142 - mcp.server.lowlevel.server - DEBUG - [server.py:285] - Registering handler for GetPromptRequest -2026-04-23 20:43:13,142 - mcp.server.lowlevel.server - DEBUG - [server.py:321] - Registering handler for ListResourceTemplatesRequest -2026-04-23 20:43:13,163 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully -2026-04-23 20:43:13,176 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully -2026-04-23 20:43:13,189 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully -2026-04-23 20:43:13,191 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully -2026-04-23 20:43:13,204 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully -2026-04-23 20:44:41,613 - mcp.server.lowlevel.server - DEBUG - [server.py:162] - Initializing server 'UnrealMCP' -2026-04-23 20:44:41,614 - mcp.server.lowlevel.server - DEBUG - [server.py:439] - Registering handler for ListToolsRequest -2026-04-23 20:44:41,614 - mcp.server.lowlevel.server - DEBUG - [server.py:519] - Registering handler for CallToolRequest -2026-04-23 20:44:41,614 - mcp.server.lowlevel.server - DEBUG - [server.py:301] - Registering handler for ListResourcesRequest -2026-04-23 20:44:41,614 - mcp.server.lowlevel.server - DEBUG - [server.py:336] - Registering handler for ReadResourceRequest -2026-04-23 20:44:41,614 - mcp.server.lowlevel.server - DEBUG - [server.py:263] - Registering handler for PromptListRequest -2026-04-23 20:44:41,614 - mcp.server.lowlevel.server - DEBUG - [server.py:285] - Registering handler for GetPromptRequest -2026-04-23 20:44:41,614 - mcp.server.lowlevel.server - DEBUG - [server.py:321] - Registering handler for ListResourceTemplatesRequest -2026-04-23 20:44:41,634 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully -2026-04-23 20:44:41,650 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully -2026-04-23 20:44:41,667 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully -2026-04-23 20:44:41,668 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully -2026-04-23 20:44:41,682 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully -2026-04-23 20:44:41,683 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport -2026-04-23 20:44:41,689 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor -2026-04-23 20:44:41,692 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up -2026-04-23 20:44:41,692 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557... -2026-04-23 20:44:41,693 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine -2026-04-23 20:44:41,693 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup -2026-04-23 20:44:41,695 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down -2026-04-23 21:49:21,797 - mcp.server.lowlevel.server - DEBUG - [server.py:162] - Initializing server 'UnrealMCP' -2026-04-23 21:49:21,798 - mcp.server.lowlevel.server - DEBUG - [server.py:439] - Registering handler for ListToolsRequest -2026-04-23 21:49:21,798 - mcp.server.lowlevel.server - DEBUG - [server.py:519] - Registering handler for CallToolRequest -2026-04-23 21:49:21,798 - mcp.server.lowlevel.server - DEBUG - [server.py:301] - Registering handler for ListResourcesRequest -2026-04-23 21:49:21,798 - mcp.server.lowlevel.server - DEBUG - [server.py:336] - Registering handler for ReadResourceRequest -2026-04-23 21:49:21,798 - mcp.server.lowlevel.server - DEBUG - [server.py:263] - Registering handler for PromptListRequest -2026-04-23 21:49:21,798 - mcp.server.lowlevel.server - DEBUG - [server.py:285] - Registering handler for GetPromptRequest -2026-04-23 21:49:21,798 - mcp.server.lowlevel.server - DEBUG - [server.py:321] - Registering handler for ListResourceTemplatesRequest -2026-04-23 21:49:21,815 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully -2026-04-23 21:49:21,830 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully -2026-04-23 21:49:21,844 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully -2026-04-23 21:49:21,846 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully -2026-04-23 21:49:21,858 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully -2026-04-23 21:49:21,859 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport -2026-04-23 21:49:21,865 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor -2026-04-23 21:49:21,868 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up -2026-04-23 21:49:21,868 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557... -2026-04-23 21:49:21,868 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine -2026-04-23 21:49:21,868 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup -2026-04-23 21:49:21,870 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down -2026-04-23 21:52:38,233 - mcp.server.lowlevel.server - DEBUG - [server.py:162] - Initializing server 'UnrealMCP' -2026-04-23 21:52:38,233 - mcp.server.lowlevel.server - DEBUG - [server.py:439] - Registering handler for ListToolsRequest -2026-04-23 21:52:38,234 - mcp.server.lowlevel.server - DEBUG - [server.py:519] - Registering handler for CallToolRequest -2026-04-23 21:52:38,234 - mcp.server.lowlevel.server - DEBUG - [server.py:301] - Registering handler for ListResourcesRequest -2026-04-23 21:52:38,234 - mcp.server.lowlevel.server - DEBUG - [server.py:336] - Registering handler for ReadResourceRequest -2026-04-23 21:52:38,234 - mcp.server.lowlevel.server - DEBUG - [server.py:263] - Registering handler for PromptListRequest -2026-04-23 21:52:38,234 - mcp.server.lowlevel.server - DEBUG - [server.py:285] - Registering handler for GetPromptRequest -2026-04-23 21:52:38,234 - mcp.server.lowlevel.server - DEBUG - [server.py:321] - Registering handler for ListResourceTemplatesRequest -2026-04-23 21:52:38,251 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully -2026-04-23 21:52:38,263 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully -2026-04-23 21:52:38,278 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully -2026-04-23 21:52:38,279 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully -2026-04-23 21:52:38,291 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully -2026-04-23 21:52:38,292 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport -2026-04-23 21:52:38,296 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor -2026-04-23 21:52:38,299 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up -2026-04-23 21:52:38,299 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557... -2026-04-23 21:52:38,300 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine -2026-04-23 21:52:38,300 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup -2026-04-23 21:52:40,398 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down -2026-04-23 22:06:00,164 - mcp.server.lowlevel.server - DEBUG - [server.py:145] - Initializing server 'UnrealMCP' -2026-04-23 22:06:00,164 - mcp.server.lowlevel.server - DEBUG - [server.py:384] - Registering handler for ListToolsRequest -2026-04-23 22:06:00,164 - mcp.server.lowlevel.server - DEBUG - [server.py:406] - Registering handler for CallToolRequest -2026-04-23 22:06:00,164 - mcp.server.lowlevel.server - DEBUG - [server.py:252] - Registering handler for ListResourcesRequest -2026-04-23 22:06:00,164 - mcp.server.lowlevel.server - DEBUG - [server.py:286] - Registering handler for ReadResourceRequest -2026-04-23 22:06:00,164 - mcp.server.lowlevel.server - DEBUG - [server.py:222] - Registering handler for PromptListRequest -2026-04-23 22:06:00,164 - mcp.server.lowlevel.server - DEBUG - [server.py:239] - Registering handler for GetPromptRequest -2026-04-23 22:06:00,164 - mcp.server.lowlevel.server - DEBUG - [server.py:267] - Registering handler for ListResourceTemplatesRequest -2026-04-23 22:06:00,175 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully -2026-04-23 22:06:00,183 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully -2026-04-23 22:06:00,192 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully -2026-04-23 22:06:00,193 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully -2026-04-23 22:06:00,213 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully -2026-04-23 22:06:00,215 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport -2026-04-23 22:06:00,219 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor -2026-04-23 22:06:00,220 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up -2026-04-23 22:06:00,220 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557... -2026-04-23 22:06:00,221 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine -2026-04-23 22:06:00,221 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup -2026-04-23 22:06:02,502 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down -2026-04-23 22:12:35,787 - mcp.server.lowlevel.server - DEBUG - [server.py:145] - Initializing server 'UnrealMCP' -2026-04-23 22:12:35,787 - mcp.server.lowlevel.server - DEBUG - [server.py:384] - Registering handler for ListToolsRequest -2026-04-23 22:12:35,787 - mcp.server.lowlevel.server - DEBUG - [server.py:406] - Registering handler for CallToolRequest -2026-04-23 22:12:35,787 - mcp.server.lowlevel.server - DEBUG - [server.py:252] - Registering handler for ListResourcesRequest -2026-04-23 22:12:35,787 - mcp.server.lowlevel.server - DEBUG - [server.py:286] - Registering handler for ReadResourceRequest -2026-04-23 22:12:35,787 - mcp.server.lowlevel.server - DEBUG - [server.py:222] - Registering handler for PromptListRequest -2026-04-23 22:12:35,787 - mcp.server.lowlevel.server - DEBUG - [server.py:239] - Registering handler for GetPromptRequest -2026-04-23 22:12:35,788 - mcp.server.lowlevel.server - DEBUG - [server.py:267] - Registering handler for ListResourceTemplatesRequest -2026-04-23 22:12:35,800 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully -2026-04-23 22:12:35,807 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully -2026-04-23 22:12:35,830 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully -2026-04-23 22:12:35,831 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully -2026-04-23 22:12:35,839 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully -2026-04-23 22:12:35,840 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport -2026-04-23 22:12:35,844 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor -2026-04-23 22:12:35,846 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up -2026-04-23 22:12:35,846 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557... -2026-04-23 22:12:35,847 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine -2026-04-23 22:12:35,847 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup -2026-04-23 22:12:35,849 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down -2026-04-23 22:15:02,964 - mcp.server.lowlevel.server - DEBUG - [server.py:145] - Initializing server 'UnrealMCP' -2026-04-23 22:15:02,964 - mcp.server.lowlevel.server - DEBUG - [server.py:384] - Registering handler for ListToolsRequest -2026-04-23 22:15:02,964 - mcp.server.lowlevel.server - DEBUG - [server.py:406] - Registering handler for CallToolRequest -2026-04-23 22:15:02,964 - mcp.server.lowlevel.server - DEBUG - [server.py:252] - Registering handler for ListResourcesRequest -2026-04-23 22:15:02,964 - mcp.server.lowlevel.server - DEBUG - [server.py:286] - Registering handler for ReadResourceRequest -2026-04-23 22:15:02,964 - mcp.server.lowlevel.server - DEBUG - [server.py:222] - Registering handler for PromptListRequest -2026-04-23 22:15:02,964 - mcp.server.lowlevel.server - DEBUG - [server.py:239] - Registering handler for GetPromptRequest -2026-04-23 22:15:02,964 - mcp.server.lowlevel.server - DEBUG - [server.py:267] - Registering handler for ListResourceTemplatesRequest -2026-04-23 22:15:02,976 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully -2026-04-23 22:15:02,985 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully -2026-04-23 22:15:03,006 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully -2026-04-23 22:15:03,007 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully -2026-04-23 22:15:03,014 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully -2026-04-23 22:15:03,015 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport -2026-04-23 22:15:03,020 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor -2026-04-23 22:15:03,022 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up -2026-04-23 22:15:03,023 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557... -2026-04-23 22:15:03,023 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine -2026-04-23 22:15:03,023 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup -2026-04-23 22:15:03,025 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down -2026-04-23 22:18:00,708 - mcp.server.lowlevel.server - DEBUG - [server.py:145] - Initializing server 'UnrealMCP' -2026-04-23 22:18:00,708 - mcp.server.lowlevel.server - DEBUG - [server.py:384] - Registering handler for ListToolsRequest -2026-04-23 22:18:00,709 - mcp.server.lowlevel.server - DEBUG - [server.py:406] - Registering handler for CallToolRequest -2026-04-23 22:18:00,709 - mcp.server.lowlevel.server - DEBUG - [server.py:252] - Registering handler for ListResourcesRequest -2026-04-23 22:18:00,709 - mcp.server.lowlevel.server - DEBUG - [server.py:286] - Registering handler for ReadResourceRequest -2026-04-23 22:18:00,709 - mcp.server.lowlevel.server - DEBUG - [server.py:222] - Registering handler for PromptListRequest -2026-04-23 22:18:00,709 - mcp.server.lowlevel.server - DEBUG - [server.py:239] - Registering handler for GetPromptRequest -2026-04-23 22:18:00,709 - mcp.server.lowlevel.server - DEBUG - [server.py:267] - Registering handler for ListResourceTemplatesRequest -2026-04-23 22:18:00,720 - UnrealMCP - INFO - [editor_tools.py:369] - Editor tools registered successfully -2026-04-23 22:18:00,729 - UnrealMCP - INFO - [blueprint_tools.py:420] - Blueprint tools registered successfully -2026-04-23 22:18:00,749 - UnrealMCP - INFO - [node_tools.py:430] - Blueprint node tools registered successfully -2026-04-23 22:18:00,752 - UnrealMCP - INFO - [project_tools.py:64] - Project tools registered successfully -2026-04-23 22:18:00,760 - UnrealMCP - INFO - [umg_tools.py:333] - UMG tools registered successfully -2026-04-23 22:18:00,760 - UnrealMCP - INFO - [unreal_mcp_server.py:375] - Starting MCP server with stdio transport -2026-04-23 22:18:00,763 - asyncio - DEBUG - [proactor_events.py:634] - Using proactor: IocpProactor -2026-04-23 22:18:00,766 - UnrealMCP - INFO - [unreal_mcp_server.py:242] - UnrealMCP server starting up -2026-04-23 22:18:00,766 - UnrealMCP - INFO - [unreal_mcp_server.py:49] - Connecting to Unreal at 127.0.0.1:55557... -2026-04-23 22:18:00,778 - UnrealMCP - INFO - [unreal_mcp_server.py:63] - Connected to Unreal Engine -2026-04-23 22:18:00,778 - UnrealMCP - INFO - [unreal_mcp_server.py:246] - Connected to Unreal Engine on startup -2026-04-23 22:18:00,782 - UnrealMCP - INFO - [unreal_mcp_server.py:259] - Unreal MCP server shut down diff --git a/Source/UnrealMCP/Private/Commands/UnrealMCPBlueprintCommands.cpp b/Source/UnrealMCP/Private/Commands/UnrealMCPBlueprintCommands.cpp index 6ed8db2..387dd59 100644 --- a/Source/UnrealMCP/Private/Commands/UnrealMCPBlueprintCommands.cpp +++ b/Source/UnrealMCP/Private/Commands/UnrealMCPBlueprintCommands.cpp @@ -1,4 +1,5 @@ #include "Commands/UnrealMCPBlueprintCommands.h" +#include "UnrealMCPLog.h" #include "Commands/UnrealMCPCommonUtils.h" #include "Engine/Blueprint.h" #include "Engine/BlueprintGeneratedClass.h" @@ -130,11 +131,11 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleCreateBlueprint(const if (FoundClass) { SelectedParentClass = FoundClass; - UE_LOG(LogTemp, Log, TEXT("Successfully set parent class to '%s'"), *ClassName); + UE_LOG(LogUnrealMCP, Log, TEXT("Successfully set parent class to '%s'"), *ClassName); } else { - UE_LOG(LogTemp, Warning, TEXT("Could not find specified parent class '%s' at paths: /Script/Engine.%s or /Script/Game.%s, defaulting to AActor"), + UE_LOG(LogUnrealMCP, Warning, TEXT("Could not find specified parent class '%s' at paths: /Script/Engine.%s or /Script/Game.%s, defaulting to AActor"), *ClassName, *ClassName, *ClassName); } } @@ -283,7 +284,7 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( } // Log all input parameters for debugging - UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Blueprint: %s, Component: %s, Property: %s"), + UE_LOG(LogUnrealMCP, Warning, TEXT("SetComponentProperty - Blueprint: %s, Component: %s, Property: %s"), *BlueprintName, *ComponentName, *PropertyName); // Log property_value if available @@ -302,34 +303,34 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( default: ValueType = TEXT("Unknown"); break; } - UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Value Type: %s"), *ValueType); + UE_LOG(LogUnrealMCP, Warning, TEXT("SetComponentProperty - Value Type: %s"), *ValueType); } else { - UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - No property_value provided")); + UE_LOG(LogUnrealMCP, Warning, TEXT("SetComponentProperty - No property_value provided")); } // Find the blueprint UBlueprint* Blueprint = FUnrealMCPCommonUtils::FindBlueprint(BlueprintName); if (!Blueprint) { - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Blueprint not found: %s"), *BlueprintName); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Blueprint not found: %s"), *BlueprintName); return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Blueprint not found: %s"), *BlueprintName)); } else { - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Blueprint found: %s (Class: %s)"), + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Blueprint found: %s (Class: %s)"), *BlueprintName, Blueprint->GeneratedClass ? *Blueprint->GeneratedClass->GetName() : TEXT("NULL")); } // Find the component USCS_Node* ComponentNode = nullptr; - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Searching for component %s in blueprint nodes"), *ComponentName); + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Searching for component %s in blueprint nodes"), *ComponentName); if (!Blueprint->SimpleConstructionScript) { - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - SimpleConstructionScript is NULL for blueprint %s"), *BlueprintName); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - SimpleConstructionScript is NULL for blueprint %s"), *BlueprintName); return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Invalid blueprint construction script")); } @@ -337,7 +338,7 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( { if (Node) { - UE_LOG(LogTemp, Verbose, TEXT("SetComponentProperty - Found node: %s"), *Node->GetVariableName().ToString()); + UE_LOG(LogUnrealMCP, Verbose, TEXT("SetComponentProperty - Found node: %s"), *Node->GetVariableName().ToString()); if (Node->GetVariableName().ToString() == ComponentName) { ComponentNode = Node; @@ -346,18 +347,18 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( } else { - UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Found NULL node in blueprint")); + UE_LOG(LogUnrealMCP, Warning, TEXT("SetComponentProperty - Found NULL node in blueprint")); } } if (!ComponentNode) { - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Component not found: %s"), *ComponentName); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Component not found: %s"), *ComponentName); return FUnrealMCPCommonUtils::CreateErrorResponse(FString::Printf(TEXT("Component not found: %s"), *ComponentName)); } else { - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Component found: %s (Class: %s)"), + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Component found: %s (Class: %s)"), *ComponentName, ComponentNode->ComponentTemplate ? *ComponentNode->ComponentTemplate->GetClass()->GetName() : TEXT("NULL")); } @@ -366,22 +367,22 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( UObject* ComponentTemplate = ComponentNode->ComponentTemplate; if (!ComponentTemplate) { - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Component template is NULL for %s"), *ComponentName); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Component template is NULL for %s"), *ComponentName); return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Invalid component template")); } // Check if this is a Spring Arm component and log special debug info if (ComponentTemplate->GetClass()->GetName().Contains(TEXT("SpringArm"))) { - UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - SpringArm component detected! Class: %s"), + UE_LOG(LogUnrealMCP, Warning, TEXT("SetComponentProperty - SpringArm component detected! Class: %s"), *ComponentTemplate->GetClass()->GetPathName()); // Log all properties of the SpringArm component class - UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - SpringArm properties:")); + UE_LOG(LogUnrealMCP, Warning, TEXT("SetComponentProperty - SpringArm properties:")); for (TFieldIterator PropIt(ComponentTemplate->GetClass()); PropIt; ++PropIt) { FProperty* Prop = *PropIt; - UE_LOG(LogTemp, Warning, TEXT(" - %s (%s)"), *Prop->GetName(), *Prop->GetCPPType()); + UE_LOG(LogUnrealMCP, Warning, TEXT(" - %s (%s)"), *Prop->GetName(), *Prop->GetCPPType()); } // Special handling for Spring Arm properties @@ -393,7 +394,7 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( FProperty* Property = FindFProperty(ComponentTemplate->GetClass(), *PropertyName); if (!Property) { - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Property %s not found on SpringArm component"), *PropertyName); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Property %s not found on SpringArm component"), *PropertyName); return FUnrealMCPCommonUtils::CreateErrorResponse( FString::Printf(TEXT("Property %s not found on SpringArm component"), *PropertyName)); } @@ -427,7 +428,7 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( if (JsonValue->Type == EJson::Number) { const float Value = JsonValue->AsNumber(); - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting float property %s to %f"), *PropertyName, Value); + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Setting float property %s to %f"), *PropertyName, Value); FloatProp->SetPropertyValue_InContainer(ComponentTemplate, Value); bSuccess = true; } @@ -437,14 +438,14 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( if (JsonValue->Type == EJson::Boolean) { const bool Value = JsonValue->AsBool(); - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting bool property %s to %d"), *PropertyName, Value); + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Setting bool property %s to %d"), *PropertyName, Value); BoolProp->SetPropertyValue_InContainer(ComponentTemplate, Value); bSuccess = true; } } else if (FStructProperty* StructProp = CastField(Property)) { - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Handling struct property %s of type %s"), + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Handling struct property %s of type %s"), *PropertyName, *StructProp->Struct->GetName()); // Special handling for common Spring Arm struct properties @@ -489,7 +490,7 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( if (bSuccess) { // Mark the blueprint as modified - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Successfully set SpringArm property %s"), *PropertyName); + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Successfully set SpringArm property %s"), *PropertyName); FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint); TSharedPtr ResultObj = MakeShared(); @@ -500,7 +501,7 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( } else { - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Failed to set SpringArm property %s"), *PropertyName); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Failed to set SpringArm property %s"), *PropertyName); return FUnrealMCPCommonUtils::CreateErrorResponse( FString::Printf(TEXT("Failed to set SpringArm property %s"), *PropertyName)); } @@ -518,15 +519,15 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( FProperty* Property = FindFProperty(ComponentTemplate->GetClass(), *PropertyName); if (!Property) { - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Property %s not found on component %s"), + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Property %s not found on component %s"), *PropertyName, *ComponentName); // List all available properties for this component - UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Available properties for %s:"), *ComponentName); + UE_LOG(LogUnrealMCP, Warning, TEXT("SetComponentProperty - Available properties for %s:"), *ComponentName); for (TFieldIterator PropIt(ComponentTemplate->GetClass()); PropIt; ++PropIt) { FProperty* Prop = *PropIt; - UE_LOG(LogTemp, Warning, TEXT(" - %s (%s)"), *Prop->GetName(), *Prop->GetCPPType()); + UE_LOG(LogUnrealMCP, Warning, TEXT(" - %s (%s)"), *Prop->GetName(), *Prop->GetCPPType()); } return FUnrealMCPCommonUtils::CreateErrorResponse( @@ -534,7 +535,7 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( } else { - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Property found: %s (Type: %s)"), + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Property found: %s (Type: %s)"), *PropertyName, *Property->GetCPPType()); } @@ -542,7 +543,7 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( FString ErrorMessage; // Handle different property types - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Attempting to set property %s"), *PropertyName); + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Attempting to set property %s"), *PropertyName); // Add try-catch block to catch and log any crashes try @@ -550,7 +551,7 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( if (FStructProperty* StructProp = CastField(Property)) { // Handle vector properties - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Property is a struct: %s"), + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Property is a struct: %s"), StructProp->Struct ? *StructProp->Struct->GetName() : TEXT("NULL")); if (StructProp->Struct == TBaseStructure::Get()) @@ -567,7 +568,7 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( Arr[2]->AsNumber() ); void* PropertyAddr = StructProp->ContainerPtrToValuePtr(ComponentTemplate); - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting Vector(%f, %f, %f)"), + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Setting Vector(%f, %f, %f)"), Vec.X, Vec.Y, Vec.Z); StructProp->CopySingleValue(PropertyAddr, &Vec); bSuccess = true; @@ -575,7 +576,7 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( else { ErrorMessage = FString::Printf(TEXT("Vector property requires 3 values, got %d"), Arr.Num()); - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage); } } else if (JsonValue->Type == EJson::Number) @@ -584,7 +585,7 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( float Value = JsonValue->AsNumber(); FVector Vec(Value, Value, Value); void* PropertyAddr = StructProp->ContainerPtrToValuePtr(ComponentTemplate); - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting Vector(%f, %f, %f) from scalar"), + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Setting Vector(%f, %f, %f) from scalar"), Vec.X, Vec.Y, Vec.Z); StructProp->CopySingleValue(PropertyAddr, &Vec); bSuccess = true; @@ -592,30 +593,30 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( else { ErrorMessage = TEXT("Vector property requires either a single number or array of 3 numbers"); - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage); } } else { // Handle other struct properties using default handler - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Using generic struct handler for %s"), + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Using generic struct handler for %s"), *PropertyName); bSuccess = FUnrealMCPCommonUtils::SetObjectProperty(ComponentTemplate, PropertyName, JsonValue, ErrorMessage); if (!bSuccess) { - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Failed to set struct property: %s"), *ErrorMessage); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Failed to set struct property: %s"), *ErrorMessage); } } } else if (FEnumProperty* EnumProp = CastField(Property)) { // Handle enum properties - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Property is an enum")); + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Property is an enum")); if (JsonValue->Type == EJson::String) { FString EnumValueName = JsonValue->AsString(); UEnum* Enum = EnumProp->GetEnum(); - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting enum from string: %s"), *EnumValueName); + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Setting enum from string: %s"), *EnumValueName); if (Enum) { @@ -623,7 +624,7 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( if (EnumValue != INDEX_NONE) { - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Found enum value: %lld"), EnumValue); + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Found enum value: %lld"), EnumValue); EnumProp->GetUnderlyingProperty()->SetIntPropertyValue( ComponentTemplate, EnumValue @@ -633,31 +634,31 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( else { // List all possible enum values - UE_LOG(LogTemp, Warning, TEXT("SetComponentProperty - Available enum values for %s:"), + UE_LOG(LogUnrealMCP, Warning, TEXT("SetComponentProperty - Available enum values for %s:"), *Enum->GetName()); for (int32 i = 0; i < Enum->NumEnums(); i++) { - UE_LOG(LogTemp, Warning, TEXT(" - %s (%lld)"), + UE_LOG(LogUnrealMCP, Warning, TEXT(" - %s (%lld)"), *Enum->GetNameStringByIndex(i), Enum->GetValueByIndex(i)); } ErrorMessage = FString::Printf(TEXT("Invalid enum value '%s' for property %s"), *EnumValueName, *PropertyName); - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage); } } else { ErrorMessage = TEXT("Enum object is NULL"); - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage); } } else if (JsonValue->Type == EJson::Number) { // Allow setting enum by integer value int64 EnumValue = JsonValue->AsNumber(); - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting enum from number: %lld"), EnumValue); + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Setting enum from number: %lld"), EnumValue); EnumProp->GetUnderlyingProperty()->SetIntPropertyValue( ComponentTemplate, EnumValue @@ -667,60 +668,60 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( else { ErrorMessage = TEXT("Enum property requires either a string name or integer value"); - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage); } } else if (FNumericProperty* NumericProp = CastField(Property)) { // Handle numeric properties - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Property is numeric: IsInteger=%d, IsFloat=%d"), + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Property is numeric: IsInteger=%d, IsFloat=%d"), NumericProp->IsInteger(), NumericProp->IsFloatingPoint()); if (JsonValue->Type == EJson::Number) { double Value = JsonValue->AsNumber(); - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Setting numeric value: %f"), Value); + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Setting numeric value: %f"), Value); if (NumericProp->IsInteger()) { NumericProp->SetIntPropertyValue(ComponentTemplate, (int64)Value); - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Set integer value: %lld"), (int64)Value); + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Set integer value: %lld"), (int64)Value); bSuccess = true; } else if (NumericProp->IsFloatingPoint()) { NumericProp->SetFloatingPointPropertyValue(ComponentTemplate, Value); - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Set float value: %f"), Value); + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Set float value: %f"), Value); bSuccess = true; } } else { ErrorMessage = TEXT("Numeric property requires a number value"); - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - %s"), *ErrorMessage); } } else { // Handle all other property types using default handler - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Using generic property handler for %s (Type: %s)"), + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Using generic property handler for %s (Type: %s)"), *PropertyName, *Property->GetCPPType()); bSuccess = FUnrealMCPCommonUtils::SetObjectProperty(ComponentTemplate, PropertyName, JsonValue, ErrorMessage); if (!bSuccess) { - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Failed to set property: %s"), *ErrorMessage); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Failed to set property: %s"), *ErrorMessage); } } } catch (const std::exception& Ex) { - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - EXCEPTION: %s"), ANSI_TO_TCHAR(Ex.what())); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - EXCEPTION: %s"), ANSI_TO_TCHAR(Ex.what())); return FUnrealMCPCommonUtils::CreateErrorResponse( FString::Printf(TEXT("Exception while setting property %s: %s"), *PropertyName, ANSI_TO_TCHAR(Ex.what()))); } catch (...) { - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - UNKNOWN EXCEPTION occurred while setting property %s"), *PropertyName); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - UNKNOWN EXCEPTION occurred while setting property %s"), *PropertyName); return FUnrealMCPCommonUtils::CreateErrorResponse( FString::Printf(TEXT("Unknown exception while setting property %s"), *PropertyName)); } @@ -728,7 +729,7 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( if (bSuccess) { // Mark the blueprint as modified - UE_LOG(LogTemp, Log, TEXT("SetComponentProperty - Successfully set property %s on component %s"), + UE_LOG(LogUnrealMCP, Log, TEXT("SetComponentProperty - Successfully set property %s on component %s"), *PropertyName, *ComponentName); FBlueprintEditorUtils::MarkBlueprintAsModified(Blueprint); @@ -740,13 +741,13 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetComponentProperty( } else { - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Failed to set property %s: %s"), + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Failed to set property %s: %s"), *PropertyName, *ErrorMessage); return FUnrealMCPCommonUtils::CreateErrorResponse(ErrorMessage); } } - UE_LOG(LogTemp, Error, TEXT("SetComponentProperty - Missing 'property_value' parameter")); + UE_LOG(LogUnrealMCP, Error, TEXT("SetComponentProperty - Missing 'property_value' parameter")); return FUnrealMCPCommonUtils::CreateErrorResponse(TEXT("Missing 'property_value' parameter")); } @@ -805,7 +806,7 @@ TSharedPtr FUnrealMCPBlueprintCommands::HandleSetPhysicsProperties( float Mass = Params->GetNumberField(TEXT("mass")); // In UE5.5, use proper overrideMass instead of just scaling PrimComponent->SetMassOverrideInKg(NAME_None, Mass); - UE_LOG(LogTemp, Display, TEXT("Set mass for component %s to %f kg"), *ComponentName, Mass); + UE_LOG(LogUnrealMCP, Display, TEXT("Set mass for component %s to %f kg"), *ComponentName, Mass); } if (Params->HasField(TEXT("linear_damping"))) diff --git a/Source/UnrealMCP/Private/Commands/UnrealMCPBlueprintNodeCommands.cpp b/Source/UnrealMCP/Private/Commands/UnrealMCPBlueprintNodeCommands.cpp index 1bd062c..389f234 100644 --- a/Source/UnrealMCP/Private/Commands/UnrealMCPBlueprintNodeCommands.cpp +++ b/Source/UnrealMCP/Private/Commands/UnrealMCPBlueprintNodeCommands.cpp @@ -1,4 +1,5 @@ #include "Commands/UnrealMCPBlueprintNodeCommands.h" +#include "UnrealMCPLog.h" #include "Commands/UnrealMCPCommonUtils.h" #include "Engine/Blueprint.h" #include "Engine/BlueprintGeneratedClass.h" @@ -17,9 +18,6 @@ #include "Kismet/GameplayStatics.h" #include "EdGraphSchema_K2.h" -// Declare the log category -DEFINE_LOG_CATEGORY_STATIC(LogUnrealMCP, Log, All); - FUnrealMCPBlueprintNodeCommands::FUnrealMCPBlueprintNodeCommands() { } @@ -310,7 +308,7 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct UK2Node_CallFunction* FunctionNode = nullptr; // Add extensive logging for debugging - UE_LOG(LogTemp, Display, TEXT("Looking for function '%s' in target '%s'"), + UE_LOG(LogUnrealMCP, Display, TEXT("Looking for function '%s' in target '%s'"), *FunctionName, Target.IsEmpty() ? TEXT("Blueprint") : *Target); // Check if we have a target class specified @@ -321,7 +319,7 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct // First try without a prefix TargetClass = FindFirstObject(*Target); - UE_LOG(LogTemp, Display, TEXT("Tried to find class '%s': %s"), + UE_LOG(LogUnrealMCP, Display, TEXT("Tried to find class '%s': %s"), *Target, TargetClass ? TEXT("Found") : TEXT("Not found")); // If not found, try with U prefix (common convention for UE classes) @@ -329,7 +327,7 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct { FString TargetWithPrefix = FString(TEXT("U")) + Target; TargetClass = FindFirstObject(*TargetWithPrefix); - UE_LOG(LogTemp, Display, TEXT("Tried to find class '%s': %s"), + UE_LOG(LogUnrealMCP, Display, TEXT("Tried to find class '%s': %s"), *TargetWithPrefix, TargetClass ? TEXT("Found") : TEXT("Not found")); } @@ -346,7 +344,7 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct TargetClass = FindFirstObject(*ClassName); if (TargetClass) { - UE_LOG(LogTemp, Display, TEXT("Found class using alternative name '%s'"), *ClassName); + UE_LOG(LogUnrealMCP, Display, TEXT("Found class using alternative name '%s'"), *ClassName); break; } } @@ -361,7 +359,7 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct { // Try loading it from its known package TargetClass = LoadObject(nullptr, TEXT("/Script/Engine.GameplayStatics")); - UE_LOG(LogTemp, Display, TEXT("Explicitly loading GameplayStatics: %s"), + UE_LOG(LogUnrealMCP, Display, TEXT("Explicitly loading GameplayStatics: %s"), TargetClass ? TEXT("Success") : TEXT("Failed")); } } @@ -369,7 +367,7 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct // If we found a target class, look for the function there if (TargetClass) { - UE_LOG(LogTemp, Display, TEXT("Looking for function '%s' in class '%s'"), + UE_LOG(LogUnrealMCP, Display, TEXT("Looking for function '%s' in class '%s'"), *FunctionName, *TargetClass->GetName()); // First try exact name @@ -379,7 +377,7 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct UClass* CurrentClass = TargetClass; while (!Function && CurrentClass) { - UE_LOG(LogTemp, Display, TEXT("Searching in class: %s"), *CurrentClass->GetName()); + UE_LOG(LogUnrealMCP, Display, TEXT("Searching in class: %s"), *CurrentClass->GetName()); // Try exact match Function = CurrentClass->FindFunctionByName(*FunctionName); @@ -390,11 +388,11 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct for (TFieldIterator FuncIt(CurrentClass); FuncIt; ++FuncIt) { UFunction* AvailableFunc = *FuncIt; - UE_LOG(LogTemp, Display, TEXT(" - Available function: %s"), *AvailableFunc->GetName()); + UE_LOG(LogUnrealMCP, Display, TEXT(" - Available function: %s"), *AvailableFunc->GetName()); if (AvailableFunc->GetName().Equals(FunctionName, ESearchCase::IgnoreCase)) { - UE_LOG(LogTemp, Display, TEXT(" - Found case-insensitive match: %s"), *AvailableFunc->GetName()); + UE_LOG(LogUnrealMCP, Display, TEXT(" - Found case-insensitive match: %s"), *AvailableFunc->GetName()); Function = AvailableFunc; break; } @@ -411,7 +409,7 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct if (TargetClass->GetName() == TEXT("GameplayStatics") && (FunctionName == TEXT("GetActorOfClass") || FunctionName.Equals(TEXT("GetActorOfClass"), ESearchCase::IgnoreCase))) { - UE_LOG(LogTemp, Display, TEXT("Using special case handling for GameplayStatics::GetActorOfClass")); + UE_LOG(LogUnrealMCP, Display, TEXT("Using special case handling for GameplayStatics::GetActorOfClass")); // Create the function node directly FunctionNode = NewObject(EventGraph); @@ -430,12 +428,12 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct FunctionNode->PostPlacedNewNode(); FunctionNode->AllocateDefaultPins(); - UE_LOG(LogTemp, Display, TEXT("Created GetActorOfClass node directly")); + UE_LOG(LogUnrealMCP, Display, TEXT("Created GetActorOfClass node directly")); // List all pins for (UEdGraphPin* Pin : FunctionNode->Pins) { - UE_LOG(LogTemp, Display, TEXT(" - Pin: %s, Direction: %d, Category: %s"), + UE_LOG(LogUnrealMCP, Display, TEXT(" - Pin: %s, Direction: %d, Category: %s"), *Pin->PinName.ToString(), (int32)Pin->Direction, *Pin->PinType.PinCategory.ToString()); } } @@ -447,7 +445,7 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct // If we still haven't found the function, try in the blueprint's class if (!Function && !FunctionNode) { - UE_LOG(LogTemp, Display, TEXT("Trying to find function in blueprint class")); + UE_LOG(LogUnrealMCP, Display, TEXT("Trying to find function in blueprint class")); Function = Blueprint->GeneratedClass->FindFunctionByName(*FunctionName); } @@ -478,12 +476,12 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct UEdGraphPin* ParamPin = FUnrealMCPCommonUtils::FindPin(FunctionNode, ParamName, EGPD_Input); if (ParamPin) { - UE_LOG(LogTemp, Display, TEXT("Found parameter pin '%s' of category '%s'"), + UE_LOG(LogUnrealMCP, Display, TEXT("Found parameter pin '%s' of category '%s'"), *ParamName, *ParamPin->PinType.PinCategory.ToString()); - UE_LOG(LogTemp, Display, TEXT(" Current default value: '%s'"), *ParamPin->DefaultValue); + UE_LOG(LogUnrealMCP, Display, TEXT(" Current default value: '%s'"), *ParamPin->DefaultValue); if (ParamPin->PinType.PinSubCategoryObject.IsValid()) { - UE_LOG(LogTemp, Display, TEXT(" Pin subcategory: '%s'"), + UE_LOG(LogUnrealMCP, Display, TEXT(" Pin subcategory: '%s'"), *ParamPin->PinType.PinSubCategoryObject->GetName()); } @@ -491,7 +489,7 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct if (ParamValue->Type == EJson::String) { FString StringVal = ParamValue->AsString(); - UE_LOG(LogTemp, Display, TEXT(" Setting string parameter '%s' to: '%s'"), + UE_LOG(LogUnrealMCP, Display, TEXT(" Setting string parameter '%s' to: '%s'"), *ParamName, *StringVal); // Handle class reference parameters (e.g., ActorClass in GetActorOfClass) @@ -547,7 +545,7 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct // Ensure we're using an integer value (no decimal) int32 IntValue = FMath::RoundToInt(ParamValue->AsNumber()); ParamPin->DefaultValue = FString::FromInt(IntValue); - UE_LOG(LogTemp, Display, TEXT(" Set integer parameter '%s' to: %d (string: '%s')"), + UE_LOG(LogUnrealMCP, Display, TEXT(" Set integer parameter '%s' to: %d (string: '%s')"), *ParamName, IntValue, *ParamPin->DefaultValue); } else if (ParamPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Float) @@ -555,14 +553,14 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct // For other numeric types float FloatValue = ParamValue->AsNumber(); ParamPin->DefaultValue = FString::SanitizeFloat(FloatValue); - UE_LOG(LogTemp, Display, TEXT(" Set float parameter '%s' to: %f (string: '%s')"), + UE_LOG(LogUnrealMCP, Display, TEXT(" Set float parameter '%s' to: %f (string: '%s')"), *ParamName, FloatValue, *ParamPin->DefaultValue); } else if (ParamPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Boolean) { bool BoolValue = ParamValue->AsBool(); ParamPin->DefaultValue = BoolValue ? TEXT("true") : TEXT("false"); - UE_LOG(LogTemp, Display, TEXT(" Set boolean parameter '%s' to: %s"), + UE_LOG(LogUnrealMCP, Display, TEXT(" Set boolean parameter '%s' to: %s"), *ParamName, *ParamPin->DefaultValue); } else if (ParamPin->PinType.PinCategory == UEdGraphSchema_K2::PC_Struct && ParamPin->PinType.PinSubCategoryObject == TBaseStructure::Get()) @@ -582,14 +580,14 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct FString VectorString = FString::Printf(TEXT("(X=%f,Y=%f,Z=%f)"), X, Y, Z); ParamPin->DefaultValue = VectorString; - UE_LOG(LogTemp, Display, TEXT(" Set vector parameter '%s' to: %s"), + UE_LOG(LogUnrealMCP, Display, TEXT(" Set vector parameter '%s' to: %s"), *ParamName, *VectorString); - UE_LOG(LogTemp, Display, TEXT(" Final pin value: '%s'"), + UE_LOG(LogUnrealMCP, Display, TEXT(" Final pin value: '%s'"), *ParamPin->DefaultValue); } else { - UE_LOG(LogTemp, Warning, TEXT("Array parameter type not fully supported yet")); + UE_LOG(LogUnrealMCP, Warning, TEXT("Array parameter type not fully supported yet")); } } } @@ -602,7 +600,7 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct // Ensure we're using an integer value (no decimal) int32 IntValue = FMath::RoundToInt(ParamValue->AsNumber()); ParamPin->DefaultValue = FString::FromInt(IntValue); - UE_LOG(LogTemp, Display, TEXT(" Set integer parameter '%s' to: %d (string: '%s')"), + UE_LOG(LogUnrealMCP, Display, TEXT(" Set integer parameter '%s' to: %d (string: '%s')"), *ParamName, IntValue, *ParamPin->DefaultValue); } else @@ -610,7 +608,7 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct // For other numeric types float FloatValue = ParamValue->AsNumber(); ParamPin->DefaultValue = FString::SanitizeFloat(FloatValue); - UE_LOG(LogTemp, Display, TEXT(" Set float parameter '%s' to: %f (string: '%s')"), + UE_LOG(LogUnrealMCP, Display, TEXT(" Set float parameter '%s' to: %f (string: '%s')"), *ParamName, FloatValue, *ParamPin->DefaultValue); } } @@ -618,12 +616,12 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct { bool BoolValue = ParamValue->AsBool(); ParamPin->DefaultValue = BoolValue ? TEXT("true") : TEXT("false"); - UE_LOG(LogTemp, Display, TEXT(" Set boolean parameter '%s' to: %s"), + UE_LOG(LogUnrealMCP, Display, TEXT(" Set boolean parameter '%s' to: %s"), *ParamName, *ParamPin->DefaultValue); } else if (ParamValue->Type == EJson::Array) { - UE_LOG(LogTemp, Display, TEXT(" Processing array parameter '%s'"), *ParamName); + UE_LOG(LogUnrealMCP, Display, TEXT(" Processing array parameter '%s'"), *ParamName); // Handle array parameters - like Vector parameters const TArray>* ArrayValue; if (ParamValue->TryGetArray(ArrayValue)) @@ -641,14 +639,14 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct FString VectorString = FString::Printf(TEXT("(X=%f,Y=%f,Z=%f)"), X, Y, Z); ParamPin->DefaultValue = VectorString; - UE_LOG(LogTemp, Display, TEXT(" Set vector parameter '%s' to: %s"), + UE_LOG(LogUnrealMCP, Display, TEXT(" Set vector parameter '%s' to: %s"), *ParamName, *VectorString); - UE_LOG(LogTemp, Display, TEXT(" Final pin value: '%s'"), + UE_LOG(LogUnrealMCP, Display, TEXT(" Final pin value: '%s'"), *ParamPin->DefaultValue); } else { - UE_LOG(LogTemp, Warning, TEXT("Array parameter type not fully supported yet")); + UE_LOG(LogUnrealMCP, Warning, TEXT("Array parameter type not fully supported yet")); } } } @@ -656,7 +654,7 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleAddBlueprintFunct } else { - UE_LOG(LogTemp, Warning, TEXT("Parameter pin '%s' not found"), *ParamName); + UE_LOG(LogUnrealMCP, Warning, TEXT("Parameter pin '%s' not found"), *ParamName); } } } @@ -910,7 +908,7 @@ TSharedPtr FUnrealMCPBlueprintNodeCommands::HandleFindBlueprintNode UK2Node_Event* EventNode = Cast(Node); if (EventNode && EventNode->EventReference.GetMemberName() == FName(*EventName)) { - UE_LOG(LogTemp, Display, TEXT("Found event node with name %s: %s"), *EventName, *EventNode->NodeGuid.ToString()); + UE_LOG(LogUnrealMCP, Display, TEXT("Found event node with name %s: %s"), *EventName, *EventNode->NodeGuid.ToString()); NodeGuidArray.Add(MakeShared(EventNode->NodeGuid.ToString())); } } diff --git a/Source/UnrealMCP/Private/Commands/UnrealMCPCommonUtils.cpp b/Source/UnrealMCP/Private/Commands/UnrealMCPCommonUtils.cpp index e5e2c00..8c7574d 100644 --- a/Source/UnrealMCP/Private/Commands/UnrealMCPCommonUtils.cpp +++ b/Source/UnrealMCP/Private/Commands/UnrealMCPCommonUtils.cpp @@ -1,4 +1,5 @@ #include "Commands/UnrealMCPCommonUtils.h" +#include "UnrealMCPLog.h" #include "GameFramework/Actor.h" #include "Engine/Blueprint.h" #include "EdGraph/EdGraph.h" @@ -199,7 +200,7 @@ UK2Node_Event* FUnrealMCPCommonUtils::CreateEventNode(UEdGraph* Graph, const FSt UK2Node_Event* EventNode = Cast(Node); if (EventNode && EventNode->EventReference.GetMemberName() == FName(*EventName)) { - UE_LOG(LogTemp, Display, TEXT("Using existing event node with name %s (ID: %s)"), + UE_LOG(LogUnrealMCP, Display, TEXT("Using existing event node with name %s (ID: %s)"), *EventName, *EventNode->NodeGuid.ToString()); return EventNode; } @@ -221,12 +222,12 @@ UK2Node_Event* FUnrealMCPCommonUtils::CreateEventNode(UEdGraph* Graph, const FSt Graph->AddNode(EventNode, true); EventNode->PostPlacedNewNode(); EventNode->AllocateDefaultPins(); - UE_LOG(LogTemp, Display, TEXT("Created new event node with name %s (ID: %s)"), + UE_LOG(LogUnrealMCP, Display, TEXT("Created new event node with name %s (ID: %s)"), *EventName, *EventNode->NodeGuid.ToString()); } else { - UE_LOG(LogTemp, Error, TEXT("Failed to find function for event name: %s"), *EventName); + UE_LOG(LogUnrealMCP, Error, TEXT("Failed to find function for event name: %s"), *EventName); } return EventNode; @@ -370,12 +371,12 @@ UEdGraphPin* FUnrealMCPCommonUtils::FindPin(UEdGraphNode* Node, const FString& P } // Log all pins for debugging - UE_LOG(LogTemp, Display, TEXT("FindPin: Looking for pin '%s' (Direction: %d) in node '%s'"), + UE_LOG(LogUnrealMCP, Display, TEXT("FindPin: Looking for pin '%s' (Direction: %d) in node '%s'"), *PinName, (int32)Direction, *Node->GetName()); for (UEdGraphPin* Pin : Node->Pins) { - UE_LOG(LogTemp, Display, TEXT(" - Available pin: '%s', Direction: %d, Category: %s"), + UE_LOG(LogUnrealMCP, Display, TEXT(" - Available pin: '%s', Direction: %d, Category: %s"), *Pin->PinName.ToString(), (int32)Pin->Direction, *Pin->PinType.PinCategory.ToString()); } @@ -384,7 +385,7 @@ UEdGraphPin* FUnrealMCPCommonUtils::FindPin(UEdGraphNode* Node, const FString& P { if (Pin->PinName.ToString() == PinName && (Direction == EGPD_MAX || Pin->Direction == Direction)) { - UE_LOG(LogTemp, Display, TEXT(" - Found exact matching pin: '%s'"), *Pin->PinName.ToString()); + UE_LOG(LogUnrealMCP, Display, TEXT(" - Found exact matching pin: '%s'"), *Pin->PinName.ToString()); return Pin; } } @@ -395,7 +396,7 @@ UEdGraphPin* FUnrealMCPCommonUtils::FindPin(UEdGraphNode* Node, const FString& P if (Pin->PinName.ToString().Equals(PinName, ESearchCase::IgnoreCase) && (Direction == EGPD_MAX || Pin->Direction == Direction)) { - UE_LOG(LogTemp, Display, TEXT(" - Found case-insensitive matching pin: '%s'"), *Pin->PinName.ToString()); + UE_LOG(LogUnrealMCP, Display, TEXT(" - Found case-insensitive matching pin: '%s'"), *Pin->PinName.ToString()); return Pin; } } @@ -407,13 +408,13 @@ UEdGraphPin* FUnrealMCPCommonUtils::FindPin(UEdGraphNode* Node, const FString& P { if (Pin->Direction == EGPD_Output && Pin->PinType.PinCategory != UEdGraphSchema_K2::PC_Exec) { - UE_LOG(LogTemp, Display, TEXT(" - Found fallback data output pin: '%s'"), *Pin->PinName.ToString()); + UE_LOG(LogUnrealMCP, Display, TEXT(" - Found fallback data output pin: '%s'"), *Pin->PinName.ToString()); return Pin; } } } - UE_LOG(LogTemp, Warning, TEXT(" - No matching pin found for '%s'"), *PinName); + UE_LOG(LogUnrealMCP, Warning, TEXT(" - No matching pin found for '%s'"), *PinName); return nullptr; } @@ -501,7 +502,7 @@ UK2Node_Event* FUnrealMCPCommonUtils::FindExistingEventNode(UEdGraph* Graph, con UK2Node_Event* EventNode = Cast(Node); if (EventNode && EventNode->EventReference.GetMemberName() == FName(*EventName)) { - UE_LOG(LogTemp, Display, TEXT("Found existing event node with name: %s"), *EventName); + UE_LOG(LogUnrealMCP, Display, TEXT("Found existing event node with name: %s"), *EventName); return EventNode; } } @@ -567,7 +568,7 @@ bool FUnrealMCPCommonUtils::SetObjectProperty(UObject* Object, const FString& Pr uint8 ByteValue = static_cast(Value->AsNumber()); ByteProp->SetPropertyValue(PropertyAddr, ByteValue); - UE_LOG(LogTemp, Display, TEXT("Setting enum property %s to numeric value: %d"), + UE_LOG(LogUnrealMCP, Display, TEXT("Setting enum property %s to numeric value: %d"), *PropertyName, ByteValue); return true; } @@ -582,7 +583,7 @@ bool FUnrealMCPCommonUtils::SetObjectProperty(UObject* Object, const FString& Pr uint8 ByteValue = FCString::Atoi(*EnumValueName); ByteProp->SetPropertyValue(PropertyAddr, ByteValue); - UE_LOG(LogTemp, Display, TEXT("Setting enum property %s to numeric string value: %s -> %d"), + UE_LOG(LogUnrealMCP, Display, TEXT("Setting enum property %s to numeric string value: %s -> %d"), *PropertyName, *EnumValueName, ByteValue); return true; } @@ -604,17 +605,17 @@ bool FUnrealMCPCommonUtils::SetObjectProperty(UObject* Object, const FString& Pr { ByteProp->SetPropertyValue(PropertyAddr, static_cast(EnumValue)); - UE_LOG(LogTemp, Display, TEXT("Setting enum property %s to name value: %s -> %lld"), + UE_LOG(LogUnrealMCP, Display, TEXT("Setting enum property %s to name value: %s -> %lld"), *PropertyName, *EnumValueName, EnumValue); return true; } else { // Log all possible enum values for debugging - UE_LOG(LogTemp, Warning, TEXT("Could not find enum value for '%s'. Available options:"), *EnumValueName); + UE_LOG(LogUnrealMCP, Warning, TEXT("Could not find enum value for '%s'. Available options:"), *EnumValueName); for (int32 i = 0; i < EnumDef->NumEnums(); i++) { - UE_LOG(LogTemp, Warning, TEXT(" - %s (value: %d)"), + UE_LOG(LogUnrealMCP, Warning, TEXT(" - %s (value: %d)"), *EnumDef->GetNameStringByIndex(i), EnumDef->GetValueByIndex(i)); } @@ -645,7 +646,7 @@ bool FUnrealMCPCommonUtils::SetObjectProperty(UObject* Object, const FString& Pr int64 EnumValue = static_cast(Value->AsNumber()); UnderlyingNumericProp->SetIntPropertyValue(PropertyAddr, EnumValue); - UE_LOG(LogTemp, Display, TEXT("Setting enum property %s to numeric value: %lld"), + UE_LOG(LogUnrealMCP, Display, TEXT("Setting enum property %s to numeric value: %lld"), *PropertyName, EnumValue); return true; } @@ -660,7 +661,7 @@ bool FUnrealMCPCommonUtils::SetObjectProperty(UObject* Object, const FString& Pr int64 EnumValue = FCString::Atoi64(*EnumValueName); UnderlyingNumericProp->SetIntPropertyValue(PropertyAddr, EnumValue); - UE_LOG(LogTemp, Display, TEXT("Setting enum property %s to numeric string value: %s -> %lld"), + UE_LOG(LogUnrealMCP, Display, TEXT("Setting enum property %s to numeric string value: %s -> %lld"), *PropertyName, *EnumValueName, EnumValue); return true; } @@ -682,17 +683,17 @@ bool FUnrealMCPCommonUtils::SetObjectProperty(UObject* Object, const FString& Pr { UnderlyingNumericProp->SetIntPropertyValue(PropertyAddr, EnumValue); - UE_LOG(LogTemp, Display, TEXT("Setting enum property %s to name value: %s -> %lld"), + UE_LOG(LogUnrealMCP, Display, TEXT("Setting enum property %s to name value: %s -> %lld"), *PropertyName, *EnumValueName, EnumValue); return true; } else { // Log all possible enum values for debugging - UE_LOG(LogTemp, Warning, TEXT("Could not find enum value for '%s'. Available options:"), *EnumValueName); + UE_LOG(LogUnrealMCP, Warning, TEXT("Could not find enum value for '%s'. Available options:"), *EnumValueName); for (int32 i = 0; i < EnumDef->NumEnums(); i++) { - UE_LOG(LogTemp, Warning, TEXT(" - %s (value: %d)"), + UE_LOG(LogUnrealMCP, Warning, TEXT(" - %s (value: %d)"), *EnumDef->GetNameStringByIndex(i), EnumDef->GetValueByIndex(i)); } diff --git a/Source/UnrealMCP/Private/Commands/UnrealMCPEditorCommands.cpp b/Source/UnrealMCP/Private/Commands/UnrealMCPEditorCommands.cpp index 445d601..318e4a3 100644 --- a/Source/UnrealMCP/Private/Commands/UnrealMCPEditorCommands.cpp +++ b/Source/UnrealMCP/Private/Commands/UnrealMCPEditorCommands.cpp @@ -1,4 +1,5 @@ #include "Commands/UnrealMCPEditorCommands.h" +#include "UnrealMCPLog.h" #include "Commands/UnrealMCPCommonUtils.h" #include "Editor.h" #include "EditorViewportClient.h" @@ -40,7 +41,7 @@ TSharedPtr FUnrealMCPEditorCommands::HandleCommand(const FString& C { if (CommandType == TEXT("create_actor")) { - UE_LOG(LogTemp, Warning, TEXT("'create_actor' command is deprecated and will be removed in a future version. Please use 'spawn_actor' instead.")); + UE_LOG(LogUnrealMCP, Warning, TEXT("'create_actor' command is deprecated and will be removed in a future version. Please use 'spawn_actor' instead.")); } return HandleSpawnActor(Params); } diff --git a/Source/UnrealMCP/Private/Commands/UnrealMCPProjectCommands.cpp b/Source/UnrealMCP/Private/Commands/UnrealMCPProjectCommands.cpp index bbf85af..8093f81 100644 --- a/Source/UnrealMCP/Private/Commands/UnrealMCPProjectCommands.cpp +++ b/Source/UnrealMCP/Private/Commands/UnrealMCPProjectCommands.cpp @@ -1,4 +1,5 @@ #include "Commands/UnrealMCPProjectCommands.h" +#include "UnrealMCPLog.h" #include "Commands/UnrealMCPCommonUtils.h" #include "GameFramework/InputSettings.h" diff --git a/Source/UnrealMCP/Private/Commands/UnrealMCPUMGCommands.cpp b/Source/UnrealMCP/Private/Commands/UnrealMCPUMGCommands.cpp index ceb4193..cec360b 100644 --- a/Source/UnrealMCP/Private/Commands/UnrealMCPUMGCommands.cpp +++ b/Source/UnrealMCP/Private/Commands/UnrealMCPUMGCommands.cpp @@ -1,4 +1,5 @@ #include "Commands/UnrealMCPUMGCommands.h" +#include "UnrealMCPLog.h" #include "Commands/UnrealMCPCommonUtils.h" #include "Editor.h" #include "EditorAssetLibrary.h" diff --git a/Source/UnrealMCP/Private/MCPServerRunnable.cpp b/Source/UnrealMCP/Private/MCPServerRunnable.cpp index 5f5f3cc..907a765 100644 --- a/Source/UnrealMCP/Private/MCPServerRunnable.cpp +++ b/Source/UnrealMCP/Private/MCPServerRunnable.cpp @@ -1,4 +1,5 @@ #include "MCPServerRunnable.h" +#include "UnrealMCPLog.h" #include "UnrealMCPBridge.h" #include "Sockets.h" #include "SocketSubsystem.h" @@ -19,7 +20,7 @@ FMCPServerRunnable::FMCPServerRunnable(UUnrealMCPBridge* InBridge, TSharedPtrHasPendingConnection(bPending) && bPending) { - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Client connection pending, accepting...")); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Client connection pending, accepting...")); ClientSocket = MakeShareable(ListenerSocket->Accept(TEXT("MCPClient"))); if (ClientSocket.IsValid()) { - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Client connection accepted")); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Client connection accepted")); // Set socket options to improve connection stability ClientSocket->SetNoDelay(true); @@ -64,14 +65,14 @@ uint32 FMCPServerRunnable::Run() { if (BytesRead == 0) { - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Client disconnected (zero bytes)")); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Client disconnected (zero bytes)")); break; } // Convert received data to string Buffer[BytesRead] = '\0'; FString ReceivedText = UTF8_TO_TCHAR(Buffer); - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Received: %s"), *ReceivedText); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Received: %s"), *ReceivedText); // Parse JSON TSharedPtr JsonObject; @@ -87,26 +88,26 @@ uint32 FMCPServerRunnable::Run() FString Response = Bridge->ExecuteCommand(CommandType, JsonObject->GetObjectField(TEXT("params"))); // Log response for debugging - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Sending response: %s"), *Response); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Sending response: %s"), *Response); // Send response int32 BytesSent = 0; if (!ClientSocket->Send((uint8*)TCHAR_TO_UTF8(*Response), Response.Len(), BytesSent)) { - UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Failed to send response")); + UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Failed to send response")); } else { - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Response sent successfully, bytes: %d"), BytesSent); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Response sent successfully, bytes: %d"), BytesSent); } } else { - UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Missing 'type' field in command")); + UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Missing 'type' field in command")); } } else { - UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Failed to parse JSON from: %s"), *ReceivedText); + UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Failed to parse JSON from: %s"), *ReceivedText); } } else @@ -118,7 +119,7 @@ uint32 FMCPServerRunnable::Run() // Check for "would block" error which isn't a real error for non-blocking sockets if (LastError == SE_EWOULDBLOCK) { - UE_LOG(LogTemp, Verbose, TEXT("MCPServerRunnable: Socket would block, continuing...")); + UE_LOG(LogUnrealMCP, Verbose, TEXT("MCPServerRunnable: Socket would block, continuing...")); bShouldBreak = false; // Small sleep to prevent tight loop when no data FPlatformProcess::Sleep(0.01f); @@ -126,12 +127,12 @@ uint32 FMCPServerRunnable::Run() // Check for other transient errors we might want to tolerate else if (LastError == SE_EINTR) // Interrupted system call { - UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Socket read interrupted, continuing...")); + UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Socket read interrupted, continuing...")); bShouldBreak = false; } else { - UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Client disconnected or error. Last error code: %d"), LastError); + UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Client disconnected or error. Last error code: %d"), LastError); } if (bShouldBreak) @@ -143,7 +144,7 @@ uint32 FMCPServerRunnable::Run() } else { - UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Failed to accept client connection")); + UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Failed to accept client connection")); } } @@ -151,7 +152,7 @@ uint32 FMCPServerRunnable::Run() FPlatformProcess::Sleep(0.1f); } - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Server thread stopping")); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Server thread stopping")); return 0; } @@ -168,44 +169,44 @@ void FMCPServerRunnable::HandleClientConnection(TSharedPtr InClientSock { if (!InClientSocket.IsValid()) { - UE_LOG(LogTemp, Error, TEXT("MCPServerRunnable: Invalid client socket passed to HandleClientConnection")); + UE_LOG(LogUnrealMCP, Error, TEXT("MCPServerRunnable: Invalid client socket passed to HandleClientConnection")); return; } - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Starting to handle client connection")); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Starting to handle client connection")); // Set socket options for better connection stability InClientSocket->SetNonBlocking(false); - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Set socket to blocking mode")); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Set socket to blocking mode")); // Properly read full message with timeout const int32 MaxBufferSize = 4096; uint8 Buffer[MaxBufferSize]; FString MessageBuffer; - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Starting message receive loop")); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Starting message receive loop")); while (bRunning && InClientSocket.IsValid()) { // Log socket state bool bIsConnected = InClientSocket->GetConnectionState() == SCS_Connected; - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Socket state - Connected: %s"), + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Socket state - Connected: %s"), bIsConnected ? TEXT("true") : TEXT("false")); // Log pending data status before receive uint32 PendingDataSize = 0; bool HasPendingData = InClientSocket->HasPendingData(PendingDataSize); - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Before Recv - HasPendingData=%s, Size=%d"), + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Before Recv - HasPendingData=%s, Size=%d"), HasPendingData ? TEXT("true") : TEXT("false"), PendingDataSize); // Try to receive data with timeout int32 BytesRead = 0; bool bReadSuccess = false; - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Attempting to receive data...")); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Attempting to receive data...")); bReadSuccess = InClientSocket->Recv(Buffer, MaxBufferSize, BytesRead, ESocketReceiveFlags::None); - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Recv attempt complete - Success=%s, BytesRead=%d"), + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Recv attempt complete - Success=%s, BytesRead=%d"), bReadSuccess ? TEXT("true") : TEXT("false"), BytesRead); if (BytesRead > 0) @@ -216,13 +217,13 @@ void FMCPServerRunnable::HandleClientConnection(TSharedPtr InClientSock { HexData += FString::Printf(TEXT("%02X "), Buffer[i]); } - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Raw data (first 50 bytes hex): %s%s"), + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Raw data (first 50 bytes hex): %s%s"), *HexData, BytesRead > 50 ? TEXT("...") : TEXT("")); // Convert and log received data Buffer[BytesRead] = 0; // Null terminate FString ReceivedData = UTF8_TO_TCHAR(Buffer); - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Received data as string: '%s'"), *ReceivedData); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Received data as string: '%s'"), *ReceivedData); // Append to message buffer MessageBuffer.Append(ReceivedData); @@ -230,34 +231,34 @@ void FMCPServerRunnable::HandleClientConnection(TSharedPtr InClientSock // Process complete messages (messages are terminated with newline) if (MessageBuffer.Contains(TEXT("\n"))) { - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Newline detected in buffer, processing messages")); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Newline detected in buffer, processing messages")); TArray Messages; MessageBuffer.ParseIntoArray(Messages, TEXT("\n"), true); - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Found %d message(s) in buffer"), Messages.Num()); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Found %d message(s) in buffer"), Messages.Num()); // Process all complete messages for (int32 i = 0; i < Messages.Num() - 1; ++i) { - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Processing message %d: '%s'"), + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Processing message %d: '%s'"), i + 1, *Messages[i]); ProcessMessage(InClientSocket, Messages[i]); } // Keep any incomplete message in the buffer MessageBuffer = Messages.Last(); - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Remaining buffer after processing: %s"), + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Remaining buffer after processing: %s"), *MessageBuffer); } else { - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: No complete message yet (no newline detected)")); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: No complete message yet (no newline detected)")); } } else if (!bReadSuccess) { - UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Connection closed or error occurred - Last error: %d"), + UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Connection closed or error occurred - Last error: %d"), (int32)ISocketSubsystem::Get()->GetLastErrorCode()); break; } @@ -266,12 +267,12 @@ void FMCPServerRunnable::HandleClientConnection(TSharedPtr InClientSock FPlatformProcess::Sleep(0.01f); } - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Exited message receive loop")); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Exited message receive loop")); } void FMCPServerRunnable::ProcessMessage(TSharedPtr Client, const FString& Message) { - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Processing message: %s"), *Message); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Processing message: %s"), *Message); // Parse message as JSON TSharedPtr JsonMessage; @@ -279,7 +280,7 @@ void FMCPServerRunnable::ProcessMessage(TSharedPtr Client, const FStrin if (!FJsonSerializer::Deserialize(Reader, JsonMessage) || !JsonMessage.IsValid()) { - UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Failed to parse message as JSON")); + UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Failed to parse message as JSON")); return; } @@ -289,7 +290,7 @@ void FMCPServerRunnable::ProcessMessage(TSharedPtr Client, const FStrin if (!JsonMessage->TryGetStringField(TEXT("command"), CommandType)) { - UE_LOG(LogTemp, Warning, TEXT("MCPServerRunnable: Message missing 'command' field")); + UE_LOG(LogUnrealMCP, Warning, TEXT("MCPServerRunnable: Message missing 'command' field")); return; } @@ -303,7 +304,7 @@ void FMCPServerRunnable::ProcessMessage(TSharedPtr Client, const FStrin } } - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Executing command: %s"), *CommandType); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Executing command: %s"), *CommandType); // Execute command FString Response = Bridge->ExecuteCommand(CommandType, Params); @@ -312,10 +313,10 @@ void FMCPServerRunnable::ProcessMessage(TSharedPtr Client, const FStrin Response += TEXT("\n"); int32 BytesSent = 0; - UE_LOG(LogTemp, Display, TEXT("MCPServerRunnable: Sending response: %s"), *Response); + UE_LOG(LogUnrealMCP, Display, TEXT("MCPServerRunnable: Sending response: %s"), *Response); if (!Client->Send((uint8*)TCHAR_TO_UTF8(*Response), Response.Len(), BytesSent)) { - UE_LOG(LogTemp, Error, TEXT("MCPServerRunnable: Failed to send response")); + UE_LOG(LogUnrealMCP, Error, TEXT("MCPServerRunnable: Failed to send response")); } } \ No newline at end of file diff --git a/Source/UnrealMCP/Private/UnrealMCPBridge.cpp b/Source/UnrealMCP/Private/UnrealMCPBridge.cpp index e76c47b..c4993d2 100644 --- a/Source/UnrealMCP/Private/UnrealMCPBridge.cpp +++ b/Source/UnrealMCP/Private/UnrealMCPBridge.cpp @@ -1,4 +1,5 @@ #include "UnrealMCPBridge.h" +#include "UnrealMCPLog.h" #include "MCPServerRunnable.h" #include "Sockets.h" #include "SocketSubsystem.h" @@ -83,7 +84,7 @@ UUnrealMCPBridge::~UUnrealMCPBridge() // Initialize subsystem void UUnrealMCPBridge::Initialize(FSubsystemCollectionBase& Collection) { - UE_LOG(LogTemp, Display, TEXT("UnrealMCPBridge: Initializing")); + UE_LOG(LogUnrealMCP, Display, TEXT("UnrealMCPBridge: Initializing")); bIsRunning = false; ListenerSocket = nullptr; @@ -99,7 +100,7 @@ void UUnrealMCPBridge::Initialize(FSubsystemCollectionBase& Collection) // Clean up resources when subsystem is destroyed void UUnrealMCPBridge::Deinitialize() { - UE_LOG(LogTemp, Display, TEXT("UnrealMCPBridge: Shutting down")); + UE_LOG(LogUnrealMCP, Display, TEXT("UnrealMCPBridge: Shutting down")); StopServer(); } @@ -108,7 +109,7 @@ void UUnrealMCPBridge::StartServer() { if (bIsRunning) { - UE_LOG(LogTemp, Warning, TEXT("UnrealMCPBridge: Server is already running")); + UE_LOG(LogUnrealMCP, Warning, TEXT("UnrealMCPBridge: Server is already running")); return; } @@ -116,7 +117,7 @@ void UUnrealMCPBridge::StartServer() ISocketSubsystem* SocketSubsystem = ISocketSubsystem::Get(PLATFORM_SOCKETSUBSYSTEM); if (!SocketSubsystem) { - UE_LOG(LogTemp, Error, TEXT("UnrealMCPBridge: Failed to get socket subsystem")); + UE_LOG(LogUnrealMCP, Error, TEXT("UnrealMCPBridge: Failed to get socket subsystem")); return; } @@ -124,7 +125,7 @@ void UUnrealMCPBridge::StartServer() TSharedPtr NewListenerSocket = MakeShareable(SocketSubsystem->CreateSocket(NAME_Stream, TEXT("UnrealMCPListener"), false)); if (!NewListenerSocket.IsValid()) { - UE_LOG(LogTemp, Error, TEXT("UnrealMCPBridge: Failed to create listener socket")); + UE_LOG(LogUnrealMCP, Error, TEXT("UnrealMCPBridge: Failed to create listener socket")); return; } @@ -136,20 +137,20 @@ void UUnrealMCPBridge::StartServer() FIPv4Endpoint Endpoint(ServerAddress, Port); if (!NewListenerSocket->Bind(*Endpoint.ToInternetAddr())) { - UE_LOG(LogTemp, Error, TEXT("UnrealMCPBridge: Failed to bind listener socket to %s:%d"), *ServerAddress.ToString(), Port); + UE_LOG(LogUnrealMCP, Error, TEXT("UnrealMCPBridge: Failed to bind listener socket to %s:%d"), *ServerAddress.ToString(), Port); return; } // Start listening if (!NewListenerSocket->Listen(5)) { - UE_LOG(LogTemp, Error, TEXT("UnrealMCPBridge: Failed to start listening")); + UE_LOG(LogUnrealMCP, Error, TEXT("UnrealMCPBridge: Failed to start listening")); return; } ListenerSocket = NewListenerSocket; bIsRunning = true; - UE_LOG(LogTemp, Display, TEXT("UnrealMCPBridge: Server started on %s:%d"), *ServerAddress.ToString(), Port); + UE_LOG(LogUnrealMCP, Display, TEXT("UnrealMCPBridge: Server started on %s:%d"), *ServerAddress.ToString(), Port); // Start server thread ServerThread = FRunnableThread::Create( @@ -160,7 +161,7 @@ void UUnrealMCPBridge::StartServer() if (!ServerThread) { - UE_LOG(LogTemp, Error, TEXT("UnrealMCPBridge: Failed to create server thread")); + UE_LOG(LogUnrealMCP, Error, TEXT("UnrealMCPBridge: Failed to create server thread")); StopServer(); return; } @@ -197,13 +198,13 @@ void UUnrealMCPBridge::StopServer() ListenerSocket.Reset(); } - UE_LOG(LogTemp, Display, TEXT("UnrealMCPBridge: Server stopped")); + UE_LOG(LogUnrealMCP, Display, TEXT("UnrealMCPBridge: Server stopped")); } // Execute a command received from a client FString UUnrealMCPBridge::ExecuteCommand(const FString& CommandType, const TSharedPtr& Params) { - UE_LOG(LogTemp, Display, TEXT("UnrealMCPBridge: Executing command: %s"), *CommandType); + UE_LOG(LogUnrealMCP, Display, TEXT("UnrealMCPBridge: Executing command: %s"), *CommandType); // Create a promise to wait for the result TPromise Promise; diff --git a/Source/UnrealMCP/Private/UnrealMCPModule.cpp b/Source/UnrealMCP/Private/UnrealMCPModule.cpp index f53c55f..7e111b7 100644 --- a/Source/UnrealMCP/Private/UnrealMCPModule.cpp +++ b/Source/UnrealMCP/Private/UnrealMCPModule.cpp @@ -1,19 +1,22 @@ #include "UnrealMCPModule.h" +#include "UnrealMCPLog.h" #include "UnrealMCPBridge.h" #include "Modules/ModuleManager.h" #include "EditorSubsystem.h" #include "Editor.h" +DEFINE_LOG_CATEGORY(LogUnrealMCP); + #define LOCTEXT_NAMESPACE "FUnrealMCPModule" void FUnrealMCPModule::StartupModule() { - UE_LOG(LogTemp, Display, TEXT("Unreal MCP Module has started")); + UE_LOG(LogUnrealMCP, Display, TEXT("Unreal MCP Module has started")); } void FUnrealMCPModule::ShutdownModule() { - UE_LOG(LogTemp, Display, TEXT("Unreal MCP Module has shut down")); + UE_LOG(LogUnrealMCP, Display, TEXT("Unreal MCP Module has shut down")); } #undef LOCTEXT_NAMESPACE diff --git a/Source/UnrealMCP/Public/UnrealMCPLog.h b/Source/UnrealMCP/Public/UnrealMCPLog.h new file mode 100644 index 0000000..48b119c --- /dev/null +++ b/Source/UnrealMCP/Public/UnrealMCPLog.h @@ -0,0 +1,5 @@ +#pragma once + +#include "Logging/LogMacros.h" + +DECLARE_LOG_CATEGORY_EXTERN(LogUnrealMCP, Log, All); diff --git a/Source/UnrealMCPSetup/Private/ClaudeCodeInitializer.cpp b/Source/UnrealMCPSetup/Private/ClaudeCodeInitializer.cpp new file mode 100644 index 0000000..2df4d93 --- /dev/null +++ b/Source/UnrealMCPSetup/Private/ClaudeCodeInitializer.cpp @@ -0,0 +1,487 @@ +#include "ClaudeCodeInitializer.h" +#include "UnrealMCPSetupLog.h" +#include "ClaudeCodeResultDialog.h" +#include "Dom/JsonObject.h" +#include "HAL/FileManager.h" +#include "HAL/PlatformFileManager.h" +#include "HAL/PlatformProcess.h" +#include "Misc/App.h" +#include "Misc/FileHelper.h" +#include "Misc/MessageDialog.h" +#include "Misc/Paths.h" +#include "Misc/ScopedSlowTask.h" +#include "Interfaces/IPluginManager.h" +#include "Serialization/JsonReader.h" +#include "Serialization/JsonSerializer.h" + +#define LOCTEXT_NAMESPACE "FClaudeCodeInitializer" + +void FClaudeCodeInitializer::Run() +{ + if (!IsCppProject()) + { + UE_LOG(LogUnrealMCPSetup, Warning, TEXT("Init aborted: not a C++ project (no .Build.cs found)")); + FMessageDialog::Open(EAppMsgType::Ok, + LOCTEXT("NotCppError", "Claude Code ne peut être initialisé que dans un projet C++.")); + return; + } + + const EAppReturnType::Type Answer = FMessageDialog::Open(EAppMsgType::YesNo, + LOCTEXT("ConfirmInit", "Êtes-vous sûr de vouloir initialiser Claude Code pour ce projet ?")); + if (Answer != EAppReturnType::Yes) + { + UE_LOG(LogUnrealMCPSetup, Display, TEXT("Init cancelled by user")); + return; + } + + TSharedPtr Plugin = IPluginManager::Get().FindPlugin(TEXT("UnrealMCP")); + if (!Plugin.IsValid()) + { + UE_LOG(LogUnrealMCPSetup, Error, TEXT("Plugin 'UnrealMCP' not found via IPluginManager")); + FMessageDialog::Open(EAppMsgType::Ok, + LOCTEXT("PluginNotFound", "Plugin UnrealMCP introuvable.")); + return; + } + + const FString PluginDir = Plugin->GetBaseDir(); + const FString ProjectDir = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir()); + UE_LOG(LogUnrealMCPSetup, Display, TEXT("Starting Claude Code init — PluginDir: %s | ProjectDir: %s"), *PluginDir, *ProjectDir); + + FInitResult Result; + { + FScopedSlowTask SlowTask(5.0f, LOCTEXT("Working", "Initialisation de Claude Code...")); + SlowTask.MakeDialog(false, false); + + SlowTask.EnterProgressFrame(1.0f, LOCTEXT("StepCopy", "Vérification du serveur Python...")); + Result.Steps.Add(CopyPythonServer(PluginDir, ProjectDir)); + + SlowTask.EnterProgressFrame(1.0f, LOCTEXT("StepClaudeAssets", "Copie des commandes et skills Claude Code...")); + Result.Steps.Add(CopyClaudeAssets(PluginDir, ProjectDir)); + + SlowTask.EnterProgressFrame(1.0f, LOCTEXT("StepMcp", "Vérification de .mcp.json...")); + Result.Steps.Add(WriteMcpJson(ProjectDir)); + + SlowTask.EnterProgressFrame(1.0f, LOCTEXT("StepSettings", "Vérification des permissions MCP...")); + Result.Steps.Add(WriteClaudeLocalSettings(ProjectDir)); + + SlowTask.EnterProgressFrame(1.0f, LOCTEXT("StepUv", "Initialisation de l'environnement Python (uv sync)...")); + Result.Steps.Add(RunUvSync(ProjectDir)); + } + + Result.bOverallSuccess = true; + for (const FInitStep& Step : Result.Steps) + { + if (!Step.bSuccess) + { + Result.bOverallSuccess = false; + break; + } + } + + if (Result.bOverallSuccess) + { + UE_LOG(LogUnrealMCPSetup, Display, TEXT("Claude Code init completed successfully")); + } + else + { + UE_LOG(LogUnrealMCPSetup, Error, TEXT("Claude Code init finished with errors — check Output Log for details")); + } + + FClaudeCodeResultDialog::Show(Result); +} + +bool FClaudeCodeInitializer::IsCppProject() +{ + const FString BuildCs = FPaths::ProjectDir() + / TEXT("Source") + / FApp::GetProjectName() + / (FString(FApp::GetProjectName()) + TEXT(".Build.cs")); + return FPaths::FileExists(BuildCs); +} + +// --------------------------------------------------------------------------- +// Validation helpers +// --------------------------------------------------------------------------- + +bool FClaudeCodeInitializer::IsMcpJsonValid(const FString& ProjectDir) +{ + const FString FilePath = ProjectDir / TEXT(".mcp.json"); + FString RawContent; + if (!FFileHelper::LoadFileToString(RawContent, *FilePath)) + { + return false; + } + + TSharedPtr Root; + TSharedRef> Reader = TJsonReaderFactory<>::Create(RawContent); + if (!FJsonSerializer::Deserialize(Reader, Root) || !Root.IsValid()) + { + return false; + } + + const TSharedPtr* McpServers; + if (!Root->TryGetObjectField(TEXT("mcpServers"), McpServers)) + { + return false; + } + + const TSharedPtr* UnrealMCP; + if (!(*McpServers)->TryGetObjectField(TEXT("unrealMCP"), UnrealMCP)) + { + return false; + } + + FString Command; + if (!(*UnrealMCP)->TryGetStringField(TEXT("command"), Command) || Command != TEXT("uv")) + { + return false; + } + + const TArray>* Args; + if (!(*UnrealMCP)->TryGetArrayField(TEXT("args"), Args)) + { + return false; + } + + for (const TSharedPtr& Arg : *Args) + { + if (Arg->AsString() == TEXT("unreal_mcp_server.py")) + { + return true; + } + } + return false; +} + +bool FClaudeCodeInitializer::IsMcpServerFolderValid(const FString& ProjectDir) +{ + const FString EntryPoint = ProjectDir / TEXT(".claude/mcp-server/unreal_mcp_server.py"); + return FPaths::FileExists(EntryPoint); +} + +bool FClaudeCodeInitializer::IsClaudeLocalSettingsValid(const FString& ProjectDir) +{ + const FString FilePath = ProjectDir / TEXT(".claude/settings.local.json"); + FString RawContent; + if (!FFileHelper::LoadFileToString(RawContent, *FilePath)) + { + return false; + } + + TSharedPtr Root; + TSharedRef> Reader = TJsonReaderFactory<>::Create(RawContent); + if (!FJsonSerializer::Deserialize(Reader, Root) || !Root.IsValid()) + { + return false; + } + + const TSharedPtr* Permissions; + if (!Root->TryGetObjectField(TEXT("permissions"), Permissions)) + { + return false; + } + + const TArray>* Allow; + if (!(*Permissions)->TryGetArrayField(TEXT("allow"), Allow)) + { + return false; + } + + for (const TSharedPtr& Entry : *Allow) + { + if (Entry->AsString().StartsWith(TEXT("mcp__unrealMCP__"))) + { + return true; + } + } + return false; +} + +// --------------------------------------------------------------------------- +// Init steps — validate first, act only if needed +// --------------------------------------------------------------------------- + +bool FClaudeCodeInitializer::CopyDirectoryFiltered(const FString& Src, const FString& Dst) +{ + static const TArray SkippedDirs = { TEXT(".venv"), TEXT("__pycache__"), TEXT("unreal_mcp.egg-info") }; + static const TArray SkippedExts = { TEXT(".log"), TEXT(".bck") }; + + IPlatformFile& PF = FPlatformFileManager::Get().GetPlatformFile(); + PF.CreateDirectoryTree(*Dst); + + bool bAllOk = true; + PF.IterateDirectory(*Src, [&](const TCHAR* Path, bool bIsDirectory) -> bool + { + const FString Name = FPaths::GetCleanFilename(Path); + const FString DstPath = Dst / Name; + + if (bIsDirectory) + { + if (!SkippedDirs.Contains(Name)) + { + bAllOk &= CopyDirectoryFiltered(Path, DstPath); + } + } + else + { + const FString Ext = FPaths::GetExtension(Name, /*bIncludeDot*/ true).ToLower(); + if (!SkippedExts.Contains(Ext)) + { + if (!PF.CopyFile(*DstPath, Path)) + { + UE_LOG(LogUnrealMCPSetup, Warning, TEXT("[Copy] Failed to copy file: %s"), Path); + bAllOk = false; + } + } + } + return true; + }); + return bAllOk; +} + +FInitStep FClaudeCodeInitializer::CopyPythonServer(const FString& PluginDir, const FString& ProjectDir) +{ + FInitStep Step; + Step.Name = LOCTEXT("StepNameCopy", "Serveur Python (.claude/mcp-server)"); + + if (IsMcpServerFolderValid(ProjectDir)) + { + UE_LOG(LogUnrealMCPSetup, Display, TEXT("[PythonServer] Already valid — skipping copy")); + Step.bSuccess = true; + Step.Details = TEXT("Déjà présent et valide — aucune modification."); + return Step; + } + + const FString Src = PluginDir / TEXT("PythonServer"); + const FString Dst = ProjectDir / TEXT(".claude/mcp-server"); + + if (!FPaths::DirectoryExists(Src)) + { + UE_LOG(LogUnrealMCPSetup, Error, TEXT("[PythonServer] Source folder not found: %s"), *Src); + Step.bSuccess = false; + Step.Details = FString::Printf(TEXT("Dossier source introuvable : %s"), *Src); + return Step; + } + + UE_LOG(LogUnrealMCPSetup, Display, TEXT("[PythonServer] Copying %s → %s (skipping .venv, __pycache__, egg-info)"), *Src, *Dst); + if (CopyDirectoryFiltered(Src, Dst)) + { + UE_LOG(LogUnrealMCPSetup, Display, TEXT("[PythonServer] Copy successful")); + Step.bSuccess = true; + Step.Details = FString::Printf(TEXT("Copié : %s → %s"), *Src, *Dst); + } + else + { + UE_LOG(LogUnrealMCPSetup, Error, TEXT("[PythonServer] Copy failed to %s"), *Dst); + Step.bSuccess = false; + Step.Details = FString::Printf(TEXT("Échec de la copie vers %s"), *Dst); + } + return Step; +} + +FInitStep FClaudeCodeInitializer::CopyClaudeAssets(const FString& PluginDir, const FString& ProjectDir) +{ + FInitStep Step; + Step.Name = LOCTEXT("StepNameClaudeAssets", "Commandes & Skills (.claude/commands, .claude/skills)"); + + const FString ClaudeSrc = PluginDir / TEXT(".claude"); + const FString ClaudeDst = ProjectDir / TEXT(".claude"); + + // Check if both directories are already present + const bool bCommandsOk = FPaths::DirectoryExists(ClaudeDst / TEXT("commands")); + const bool bSkillsOk = FPaths::DirectoryExists(ClaudeDst / TEXT("skills")); + if (bCommandsOk && bSkillsOk) + { + UE_LOG(LogUnrealMCPSetup, Display, TEXT("[ClaudeAssets] commands/ and skills/ already present — skipping")); + Step.bSuccess = true; + Step.Details = TEXT("Déjà présents — aucune modification."); + return Step; + } + + UE_LOG(LogUnrealMCPSetup, Display, TEXT("[ClaudeAssets] Copying commands/ and skills/ from %s → %s"), *ClaudeSrc, *ClaudeDst); + + bool bAllOk = true; + for (const FString& SubDir : { FString(TEXT("commands")), FString(TEXT("skills")) }) + { + const FString Src = ClaudeSrc / SubDir; + const FString Dst = ClaudeDst / SubDir; + if (FPaths::DirectoryExists(Src)) + { + if (!CopyDirectoryFiltered(Src, Dst)) + { + UE_LOG(LogUnrealMCPSetup, Error, TEXT("[ClaudeAssets] Failed to copy %s"), *Src); + bAllOk = false; + } + } + else + { + UE_LOG(LogUnrealMCPSetup, Warning, TEXT("[ClaudeAssets] Source not found, skipping: %s"), *Src); + } + } + + Step.bSuccess = bAllOk; + Step.Details = bAllOk + ? FString::Printf(TEXT("Copié : commands/ et skills/ → %s"), *ClaudeDst) + : FString::Printf(TEXT("Échec partiel — voir Output Log pour les détails")); + return Step; +} + +FInitStep FClaudeCodeInitializer::WriteMcpJson(const FString& ProjectDir) +{ + FInitStep Step; + Step.Name = LOCTEXT("StepNameMcp", ".mcp.json"); + + if (IsMcpJsonValid(ProjectDir)) + { + UE_LOG(LogUnrealMCPSetup, Display, TEXT("[.mcp.json] Already valid — skipping write")); + Step.bSuccess = true; + Step.Details = TEXT("Déjà configuré et valide — aucune modification."); + return Step; + } + + const FString FilePath = ProjectDir / TEXT(".mcp.json"); + UE_LOG(LogUnrealMCPSetup, Display, TEXT("[.mcp.json] Writing %s"), *FilePath); + const FString Content = + TEXT("{\n") + TEXT(" \"mcpServers\": {\n") + TEXT(" \"unrealMCP\": {\n") + TEXT(" \"type\": \"stdio\",\n") + TEXT(" \"command\": \"uv\",\n") + TEXT(" \"args\": [\"--directory\", \".claude/mcp-server\", \"run\", \"unreal_mcp_server.py\"]\n") + TEXT(" }\n") + TEXT(" }\n") + TEXT("}\n"); + + if (FFileHelper::SaveStringToFile(Content, *FilePath, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM)) + { + UE_LOG(LogUnrealMCPSetup, Display, TEXT("[.mcp.json] Written successfully")); + Step.bSuccess = true; + Step.Details = FString::Printf(TEXT("Créé : %s"), *FilePath); + } + else + { + UE_LOG(LogUnrealMCPSetup, Error, TEXT("[.mcp.json] Failed to write %s"), *FilePath); + Step.bSuccess = false; + Step.Details = FString::Printf(TEXT("Impossible d'écrire %s"), *FilePath); + } + return Step; +} + +FInitStep FClaudeCodeInitializer::WriteClaudeLocalSettings(const FString& ProjectDir) +{ + FInitStep Step; + Step.Name = LOCTEXT("StepNameSettings", "Permissions MCP (.claude/settings.local.json)"); + + if (IsClaudeLocalSettingsValid(ProjectDir)) + { + UE_LOG(LogUnrealMCPSetup, Display, TEXT("[settings.local.json] Already valid — skipping write")); + Step.bSuccess = true; + Step.Details = TEXT("Déjà configuré et valide — aucune modification."); + return Step; + } + + const FString ClaudeDir = ProjectDir / TEXT(".claude"); + const FString FilePath = ClaudeDir / TEXT("settings.local.json"); + UE_LOG(LogUnrealMCPSetup, Display, TEXT("[settings.local.json] Writing %s"), *FilePath); + + IPlatformFile& PF = FPlatformFileManager::Get().GetPlatformFile(); + if (!PF.DirectoryExists(*ClaudeDir)) + { + PF.CreateDirectoryTree(*ClaudeDir); + } + + const FString Content = + TEXT("{\n") + TEXT(" \"permissions\": {\n") + TEXT(" \"allow\": [\n") + TEXT(" \"mcp__unrealMCP__get_actors_in_level\",\n") + TEXT(" \"mcp__unrealMCP__find_actors_by_name\",\n") + TEXT(" \"mcp__unrealMCP__spawn_actor\",\n") + TEXT(" \"mcp__unrealMCP__delete_actor\",\n") + TEXT(" \"mcp__unrealMCP__set_actor_transform\",\n") + TEXT(" \"mcp__unrealMCP__get_actor_properties\",\n") + TEXT(" \"mcp__unrealMCP__set_actor_property\",\n") + TEXT(" \"mcp__unrealMCP__spawn_blueprint_actor\",\n") + TEXT(" \"mcp__unrealMCP__create_blueprint\",\n") + TEXT(" \"mcp__unrealMCP__add_component_to_blueprint\",\n") + TEXT(" \"mcp__unrealMCP__set_static_mesh_properties\",\n") + TEXT(" \"mcp__unrealMCP__set_component_property\",\n") + TEXT(" \"mcp__unrealMCP__set_physics_properties\",\n") + TEXT(" \"mcp__unrealMCP__set_blueprint_property\",\n") + TEXT(" \"mcp__unrealMCP__compile_blueprint\",\n") + TEXT(" \"mcp__unrealMCP__find_blueprint_nodes\",\n") + TEXT(" \"mcp__unrealMCP__add_blueprint_event_node\",\n") + TEXT(" \"mcp__unrealMCP__add_blueprint_input_action_node\",\n") + TEXT(" \"mcp__unrealMCP__add_blueprint_function_node\",\n") + TEXT(" \"mcp__unrealMCP__add_blueprint_variable\",\n") + TEXT(" \"mcp__unrealMCP__add_blueprint_self_reference\",\n") + TEXT(" \"mcp__unrealMCP__add_blueprint_get_self_component_reference\",\n") + TEXT(" \"mcp__unrealMCP__connect_blueprint_nodes\",\n") + TEXT(" \"mcp__unrealMCP__create_input_mapping\",\n") + TEXT(" \"mcp__unrealMCP__create_umg_widget_blueprint\",\n") + TEXT(" \"mcp__unrealMCP__add_text_block_to_widget\",\n") + TEXT(" \"mcp__unrealMCP__add_button_to_widget\",\n") + TEXT(" \"mcp__unrealMCP__bind_widget_event\",\n") + TEXT(" \"mcp__unrealMCP__set_text_block_binding\",\n") + TEXT(" \"mcp__unrealMCP__add_widget_to_viewport\"\n") + TEXT(" ]\n") + TEXT(" }\n") + TEXT("}\n"); + + if (FFileHelper::SaveStringToFile(Content, *FilePath, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM)) + { + UE_LOG(LogUnrealMCPSetup, Display, TEXT("[settings.local.json] Written successfully")); + Step.bSuccess = true; + Step.Details = FString::Printf(TEXT("Créé : %s"), *FilePath); + } + else + { + UE_LOG(LogUnrealMCPSetup, Error, TEXT("[settings.local.json] Failed to write %s"), *FilePath); + Step.bSuccess = false; + Step.Details = FString::Printf(TEXT("Impossible d'écrire %s"), *FilePath); + } + return Step; +} + +FInitStep FClaudeCodeInitializer::RunUvSync(const FString& ProjectDir) +{ + FInitStep Step; + Step.Name = LOCTEXT("StepNameUv", "Environnement Python (uv sync)"); + + const FString McpServerDir = ProjectDir / TEXT(".claude/mcp-server"); + // --no-install-project: install dependencies only, skip building the local package. + // Building it spawns a grandchild Python process that inherits invalid pipe handles + // from FPlatformProcess::ExecProcess on Windows (os error 6 / ERROR_INVALID_HANDLE). + // unreal_mcp_server.py is a plain script — it doesn't need to be installed as a package. + const FString Args = FString::Printf(TEXT("sync --directory \"%s\" --no-install-project"), *McpServerDir); + + UE_LOG(LogUnrealMCPSetup, Display, TEXT("[uv sync] Running: uv %s"), *Args); + + int32 ReturnCode = 0; + FString StdOut, StdErr; + const bool bLaunched = FPlatformProcess::ExecProcess( + TEXT("uv"), *Args, &ReturnCode, &StdOut, &StdErr); + + if (!bLaunched) + { + UE_LOG(LogUnrealMCPSetup, Error, TEXT("[uv sync] 'uv' not found in PATH")); + Step.bSuccess = false; + Step.Details = TEXT("uv introuvable dans le PATH. Installez uv (https://docs.astral.sh/uv/) puis relancez."); + } + else if (ReturnCode == 0) + { + UE_LOG(LogUnrealMCPSetup, Display, TEXT("[uv sync] Completed successfully")); + Step.bSuccess = true; + Step.Details = TEXT("Environnement Python prêt."); + } + else + { + const FString ErrorMsg = StdErr.IsEmpty() ? StdOut : StdErr; + UE_LOG(LogUnrealMCPSetup, Error, TEXT("[uv sync] Failed (code %d): %s"), ReturnCode, *ErrorMsg); + Step.bSuccess = false; + Step.Details = ErrorMsg; + } + return Step; +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/UnrealMCPSetup/Private/ClaudeCodeMenuExtender.cpp b/Source/UnrealMCPSetup/Private/ClaudeCodeMenuExtender.cpp new file mode 100644 index 0000000..22329e7 --- /dev/null +++ b/Source/UnrealMCPSetup/Private/ClaudeCodeMenuExtender.cpp @@ -0,0 +1,48 @@ +#include "ClaudeCodeMenuExtender.h" +#include "UnrealMCPSetupLog.h" +#include "ClaudeCodeInitializer.h" +#include "ToolMenus.h" +#include "ToolMenu.h" +#include "ToolMenuSection.h" + +#define LOCTEXT_NAMESPACE "FClaudeCodeMenuExtender" + +void FClaudeCodeMenuExtender::Register() +{ + // Pattern from UE5 source (LevelEditorMenu.cpp ExtendMenuBar): + // - ExtendMenu + FindOrAddSection(NAME_None) + // - InsertPosition on the ENTRY, not the section + // - Anchor name is "Help" (short form, not the full qualified name) + UToolMenu* MenuBar = UToolMenus::Get()->ExtendMenu("LevelEditor.MainMenu"); + FToolMenuSection& Section = MenuBar->FindOrAddSection(NAME_None); + + FToolMenuEntry& SubMenu = Section.AddSubMenu( + "ClaudeCodeMenu", + LOCTEXT("MenuLabel", "Claude Code"), + LOCTEXT("MenuTooltip", "Claude Code tools for this project"), + FNewToolMenuDelegate::CreateStatic(&FClaudeCodeMenuExtender::BuildMenu) + ); + // "Actor" in the menu bar is the entry named "Actions" (dynamic label, see LevelEditorMenu.cpp). + // Using After("Actions") places Claude Code between Actor and Help. + SubMenu.InsertPosition = FToolMenuInsert("Actions", EToolMenuInsertType::After); +} + +void FClaudeCodeMenuExtender::BuildMenu(UToolMenu* Menu) +{ + FToolMenuSection& Section = Menu->FindOrAddSection("ClaudeCodeActions"); + Section.AddMenuEntry( + "InitClaudeCode", + LOCTEXT("InitLabel", "Init Claude Code"), + LOCTEXT("InitTooltip", "Initialisation de Claude Code pour ce projet"), + FSlateIcon(), + FUIAction(FExecuteAction::CreateStatic(&FClaudeCodeMenuExtender::OnInitClaudeCodeClicked)) + ); +} + +void FClaudeCodeMenuExtender::OnInitClaudeCodeClicked() +{ + UE_LOG(LogUnrealMCPSetup, Display, TEXT("User clicked 'Init Claude Code'")); + FClaudeCodeInitializer::Run(); +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/UnrealMCPSetup/Private/ClaudeCodeResultDialog.cpp b/Source/UnrealMCPSetup/Private/ClaudeCodeResultDialog.cpp new file mode 100644 index 0000000..48dd057 --- /dev/null +++ b/Source/UnrealMCPSetup/Private/ClaudeCodeResultDialog.cpp @@ -0,0 +1,138 @@ +#include "ClaudeCodeResultDialog.h" +#include "Framework/Application/SlateApplication.h" +#include "Widgets/SBoxPanel.h" +#include "Widgets/SWindow.h" +#include "Widgets/Input/SButton.h" +#include "Widgets/Layout/SBorder.h" +#include "Widgets/Layout/SScrollBox.h" +#include "Widgets/Layout/SSeparator.h" +#include "Widgets/Text/STextBlock.h" + +#define LOCTEXT_NAMESPACE "FClaudeCodeResultDialog" + +void FClaudeCodeResultDialog::Show(const FInitResult& Result) +{ + TSharedRef Window = SNew(SWindow) + .Title(LOCTEXT("DialogTitle", "Claude Code — Résultat de l'initialisation")) + .ClientSize(FVector2D(540, 340)) + .SupportsMaximize(false) + .SupportsMinimize(false) + .IsTopmostWindow(true); + + Window->SetContent( + SNew(SClaudeCodeResultDialog) + .InitResult(Result) + .ParentWindow(Window) + ); + + FSlateApplication::Get().AddModalWindow(Window, FSlateApplication::Get().GetActiveTopLevelWindow()); +} + +void SClaudeCodeResultDialog::Construct(const FArguments& InArgs) +{ + ParentWindow = InArgs._ParentWindow; + const FInitResult& Result = InArgs._InitResult; + + TSharedRef StepsList = SNew(SVerticalBox); + + for (const FInitStep& Step : Result.Steps) + { + const FString Icon = Step.bSuccess ? TEXT("✓") : TEXT("✗"); + const FSlateColor IconColor = Step.bSuccess + ? FSlateColor(FLinearColor(0.2f, 0.85f, 0.2f)) + : FSlateColor(FLinearColor(0.9f, 0.2f, 0.2f)); + + StepsList->AddSlot() + .AutoHeight() + .Padding(4.f, 4.f) + [ + SNew(SHorizontalBox) + + SHorizontalBox::Slot() + .AutoWidth() + .VAlign(VAlign_Top) + .Padding(0.f, 1.f, 10.f, 0.f) + [ + SNew(STextBlock) + .Text(FText::FromString(Icon)) + .ColorAndOpacity(IconColor) + ] + + SHorizontalBox::Slot() + .FillWidth(1.f) + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .AutoHeight() + [ + SNew(STextBlock) + .Text(Step.Name) + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(0.f, 2.f, 0.f, 0.f) + [ + SNew(STextBlock) + .Text(FText::FromString(Step.Details)) + .ColorAndOpacity(FSlateColor(FLinearColor(0.55f, 0.55f, 0.55f))) + .AutoWrapText(true) + ] + ] + ]; + } + + const FText StatusText = Result.bOverallSuccess + ? LOCTEXT("StatusOk", "Initialisation réussie. Redémarrez Claude Code pour activer le serveur MCP.") + : LOCTEXT("StatusFail", "L'initialisation a rencontré des erreurs. Consultez les détails ci-dessus."); + + const FSlateColor StatusColor = Result.bOverallSuccess + ? FSlateColor(FLinearColor(0.2f, 0.85f, 0.2f)) + : FSlateColor(FLinearColor(0.9f, 0.2f, 0.2f)); + + ChildSlot + [ + SNew(SVerticalBox) + + SVerticalBox::Slot() + .FillHeight(1.f) + .Padding(14.f, 12.f) + [ + SNew(SScrollBox) + + SScrollBox::Slot() + [ + StepsList + ] + ] + + SVerticalBox::Slot() + .AutoHeight() + [ + SNew(SSeparator) + ] + + SVerticalBox::Slot() + .AutoHeight() + .Padding(14.f, 8.f) + [ + SNew(STextBlock) + .Text(StatusText) + .ColorAndOpacity(StatusColor) + .AutoWrapText(true) + ] + + SVerticalBox::Slot() + .AutoHeight() + .HAlign(HAlign_Right) + .Padding(14.f, 4.f, 14.f, 14.f) + [ + SNew(SButton) + .Text(LOCTEXT("CloseBtn", "Fermer")) + .OnClicked(this, &SClaudeCodeResultDialog::OnCloseClicked) + ] + ]; +} + +FReply SClaudeCodeResultDialog::OnCloseClicked() +{ + if (ParentWindow.IsValid()) + { + ParentWindow->RequestDestroyWindow(); + } + return FReply::Handled(); +} + +#undef LOCTEXT_NAMESPACE diff --git a/Source/UnrealMCPSetup/Private/UnrealMCPSetupModule.cpp b/Source/UnrealMCPSetup/Private/UnrealMCPSetupModule.cpp new file mode 100644 index 0000000..14e0968 --- /dev/null +++ b/Source/UnrealMCPSetup/Private/UnrealMCPSetupModule.cpp @@ -0,0 +1,40 @@ +#include "UnrealMCPSetupModule.h" +#include "UnrealMCPSetupLog.h" +#include "ClaudeCodeMenuExtender.h" +#include "ToolMenus.h" +#include "Editor.h" + +DEFINE_LOG_CATEGORY(LogUnrealMCPSetup); + +#define LOCTEXT_NAMESPACE "FUnrealMCPSetupModule" + +void FUnrealMCPSetupModule::StartupModule() +{ + UE_LOG(LogUnrealMCPSetup, Display, TEXT("UnrealMCPSetup module started")); + if (GEditor) + { + UToolMenus::RegisterStartupCallback( + FSimpleMulticastDelegate::FDelegate::CreateRaw( + this, &FUnrealMCPSetupModule::RegisterMenus)); + } + else + { + UE_LOG(LogUnrealMCPSetup, Warning, TEXT("GEditor not available — Claude Code menu will not be registered")); + } +} + +void FUnrealMCPSetupModule::ShutdownModule() +{ + UE_LOG(LogUnrealMCPSetup, Display, TEXT("UnrealMCPSetup module shut down")); + UToolMenus::UnRegisterStartupCallback(this); +} + +void FUnrealMCPSetupModule::RegisterMenus() +{ + UE_LOG(LogUnrealMCPSetup, Display, TEXT("Registering Claude Code menu")); + FClaudeCodeMenuExtender::Register(); +} + +#undef LOCTEXT_NAMESPACE + +IMPLEMENT_MODULE(FUnrealMCPSetupModule, UnrealMCPSetup) diff --git a/Source/UnrealMCPSetup/Public/ClaudeCodeInitializer.h b/Source/UnrealMCPSetup/Public/ClaudeCodeInitializer.h new file mode 100644 index 0000000..6eda473 --- /dev/null +++ b/Source/UnrealMCPSetup/Public/ClaudeCodeInitializer.h @@ -0,0 +1,27 @@ +#pragma once + +#include "CoreMinimal.h" +#include "ClaudeCodeTypes.h" + +class FClaudeCodeInitializer +{ +public: + static void Run(); + +private: + static bool IsCppProject(); + + static FInitStep CopyPythonServer(const FString& PluginDir, const FString& ProjectDir); + static FInitStep CopyClaudeAssets(const FString& PluginDir, const FString& ProjectDir); + static FInitStep WriteMcpJson(const FString& ProjectDir); + static FInitStep WriteClaudeLocalSettings(const FString& ProjectDir); + static FInitStep RunUvSync(const FString& ProjectDir); + + // Validation helpers (used when .claude/ already exists) + static bool IsMcpJsonValid(const FString& ProjectDir); + static bool IsMcpServerFolderValid(const FString& ProjectDir); + static bool IsClaudeLocalSettingsValid(const FString& ProjectDir); + + // Recursive copy that skips .venv, __pycache__, *.egg-info (locked / generated files) + static bool CopyDirectoryFiltered(const FString& Src, const FString& Dst); +}; diff --git a/Source/UnrealMCPSetup/Public/ClaudeCodeMenuExtender.h b/Source/UnrealMCPSetup/Public/ClaudeCodeMenuExtender.h new file mode 100644 index 0000000..1ac1727 --- /dev/null +++ b/Source/UnrealMCPSetup/Public/ClaudeCodeMenuExtender.h @@ -0,0 +1,15 @@ +#pragma once + +#include "CoreMinimal.h" + +class UToolMenu; + +class FClaudeCodeMenuExtender +{ +public: + static void Register(); + +private: + static void BuildMenu(UToolMenu* Menu); + static void OnInitClaudeCodeClicked(); +}; diff --git a/Source/UnrealMCPSetup/Public/ClaudeCodeResultDialog.h b/Source/UnrealMCPSetup/Public/ClaudeCodeResultDialog.h new file mode 100644 index 0000000..289b101 --- /dev/null +++ b/Source/UnrealMCPSetup/Public/ClaudeCodeResultDialog.h @@ -0,0 +1,27 @@ +#pragma once + +#include "CoreMinimal.h" +#include "ClaudeCodeTypes.h" +#include "Widgets/SCompoundWidget.h" + +class FClaudeCodeResultDialog +{ +public: + static void Show(const FInitResult& Result); +}; + +class SClaudeCodeResultDialog : public SCompoundWidget +{ +public: + SLATE_BEGIN_ARGS(SClaudeCodeResultDialog) {} + SLATE_ARGUMENT(FInitResult, InitResult) + SLATE_ARGUMENT(TSharedPtr, ParentWindow) + SLATE_END_ARGS() + + void Construct(const FArguments& InArgs); + +private: + FReply OnCloseClicked(); + + TSharedPtr ParentWindow; +}; diff --git a/Source/UnrealMCPSetup/Public/ClaudeCodeTypes.h b/Source/UnrealMCPSetup/Public/ClaudeCodeTypes.h new file mode 100644 index 0000000..643cbb3 --- /dev/null +++ b/Source/UnrealMCPSetup/Public/ClaudeCodeTypes.h @@ -0,0 +1,16 @@ +#pragma once + +#include "CoreMinimal.h" + +struct FInitStep +{ + FText Name; + bool bSuccess = false; + FString Details; +}; + +struct FInitResult +{ + bool bOverallSuccess = false; + TArray Steps; +}; diff --git a/Source/UnrealMCPSetup/Public/UnrealMCPSetupLog.h b/Source/UnrealMCPSetup/Public/UnrealMCPSetupLog.h new file mode 100644 index 0000000..2b64d09 --- /dev/null +++ b/Source/UnrealMCPSetup/Public/UnrealMCPSetupLog.h @@ -0,0 +1,5 @@ +#pragma once + +#include "Logging/LogMacros.h" + +DECLARE_LOG_CATEGORY_EXTERN(LogUnrealMCPSetup, Log, All); diff --git a/Source/UnrealMCPSetup/Public/UnrealMCPSetupModule.h b/Source/UnrealMCPSetup/Public/UnrealMCPSetupModule.h new file mode 100644 index 0000000..e16983e --- /dev/null +++ b/Source/UnrealMCPSetup/Public/UnrealMCPSetupModule.h @@ -0,0 +1,14 @@ +#pragma once + +#include "CoreMinimal.h" +#include "Modules/ModuleManager.h" + +class FUnrealMCPSetupModule : public IModuleInterface +{ +public: + virtual void StartupModule() override; + virtual void ShutdownModule() override; + +private: + void RegisterMenus(); +}; diff --git a/Source/UnrealMCPSetup/UnrealMCPSetup.Build.cs b/Source/UnrealMCPSetup/UnrealMCPSetup.Build.cs new file mode 100644 index 0000000..f4b5d1b --- /dev/null +++ b/Source/UnrealMCPSetup/UnrealMCPSetup.Build.cs @@ -0,0 +1,23 @@ +using UnrealBuildTool; + +public class UnrealMCPSetup : ModuleRules +{ + public UnrealMCPSetup(ReadOnlyTargetRules Target) : base(Target) + { + PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; + IWYUSupport = IWYUSupport.Full; + + PrivateDependencyModuleNames.AddRange(new string[] + { + "Core", + "CoreUObject", + "Engine", + "Slate", + "SlateCore", + "UnrealEd", + "ToolMenus", + "Projects", + "Json", + }); + } +} diff --git a/UnrealMCP.uplugin b/UnrealMCP.uplugin index b2fcff9..2d4730c 100644 --- a/UnrealMCP.uplugin +++ b/UnrealMCP.uplugin @@ -24,6 +24,16 @@ "Mac", "Linux" ] + }, + { + "Name": "UnrealMCPSetup", + "Type": "Editor", + "LoadingPhase": "PostEngineInit", + "WhitelistPlatforms": [ + "Win64", + "Mac", + "Linux" + ] } ], "Plugins": [