@akiojin/unity-mcp-server 2.14.14

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +206 -0
  3. package/bin/unity-mcp-server +2 -0
  4. package/package.json +73 -0
  5. package/src/core/codeIndex.js +163 -0
  6. package/src/core/codeIndexDb.js +96 -0
  7. package/src/core/config.js +165 -0
  8. package/src/core/indexWatcher.js +52 -0
  9. package/src/core/projectInfo.js +111 -0
  10. package/src/core/server.js +294 -0
  11. package/src/core/unityConnection.js +426 -0
  12. package/src/handlers/analysis/AnalyzeSceneContentsToolHandler.js +35 -0
  13. package/src/handlers/analysis/FindByComponentToolHandler.js +20 -0
  14. package/src/handlers/analysis/GetAnimatorStateToolHandler.js +37 -0
  15. package/src/handlers/analysis/GetComponentValuesToolHandler.js +20 -0
  16. package/src/handlers/analysis/GetGameObjectDetailsToolHandler.js +35 -0
  17. package/src/handlers/analysis/GetInputActionsStateToolHandler.js +37 -0
  18. package/src/handlers/analysis/GetObjectReferencesToolHandler.js +20 -0
  19. package/src/handlers/asset/AssetDatabaseToolHandler.js +221 -0
  20. package/src/handlers/asset/AssetDependencyToolHandler.js +201 -0
  21. package/src/handlers/asset/AssetImportSettingsToolHandler.js +170 -0
  22. package/src/handlers/asset/CreateMaterialToolHandler.js +96 -0
  23. package/src/handlers/asset/CreatePrefabToolHandler.js +78 -0
  24. package/src/handlers/asset/ExitPrefabModeToolHandler.js +83 -0
  25. package/src/handlers/asset/InstantiatePrefabToolHandler.js +133 -0
  26. package/src/handlers/asset/ModifyMaterialToolHandler.js +76 -0
  27. package/src/handlers/asset/ModifyPrefabToolHandler.js +72 -0
  28. package/src/handlers/asset/OpenPrefabToolHandler.js +121 -0
  29. package/src/handlers/asset/SavePrefabToolHandler.js +106 -0
  30. package/src/handlers/base/BaseToolHandler.js +133 -0
  31. package/src/handlers/compilation/GetCompilationStateToolHandler.js +90 -0
  32. package/src/handlers/component/AddComponentToolHandler.js +126 -0
  33. package/src/handlers/component/GetComponentTypesToolHandler.js +100 -0
  34. package/src/handlers/component/ListComponentsToolHandler.js +85 -0
  35. package/src/handlers/component/ModifyComponentToolHandler.js +143 -0
  36. package/src/handlers/component/RemoveComponentToolHandler.js +108 -0
  37. package/src/handlers/console/ClearConsoleToolHandler.js +160 -0
  38. package/src/handlers/console/ReadConsoleToolHandler.js +276 -0
  39. package/src/handlers/editor/LayerManagementToolHandler.js +160 -0
  40. package/src/handlers/editor/SelectionToolHandler.js +141 -0
  41. package/src/handlers/editor/TagManagementToolHandler.js +129 -0
  42. package/src/handlers/editor/ToolManagementToolHandler.js +135 -0
  43. package/src/handlers/editor/WindowManagementToolHandler.js +125 -0
  44. package/src/handlers/gameobject/CreateGameObjectToolHandler.js +131 -0
  45. package/src/handlers/gameobject/DeleteGameObjectToolHandler.js +101 -0
  46. package/src/handlers/gameobject/FindGameObjectToolHandler.js +119 -0
  47. package/src/handlers/gameobject/GetHierarchyToolHandler.js +132 -0
  48. package/src/handlers/gameobject/ModifyGameObjectToolHandler.js +128 -0
  49. package/src/handlers/index.js +389 -0
  50. package/src/handlers/input/AddInputActionToolHandler.js +20 -0
  51. package/src/handlers/input/AddInputBindingToolHandler.js +20 -0
  52. package/src/handlers/input/CreateActionMapToolHandler.js +20 -0
  53. package/src/handlers/input/CreateCompositeBindingToolHandler.js +20 -0
  54. package/src/handlers/input/GamepadSimulationHandler.js +116 -0
  55. package/src/handlers/input/InputSystemHandler.js +80 -0
  56. package/src/handlers/input/KeyboardSimulationHandler.js +79 -0
  57. package/src/handlers/input/ManageControlSchemesToolHandler.js +20 -0
  58. package/src/handlers/input/MouseSimulationHandler.js +107 -0
  59. package/src/handlers/input/RemoveActionMapToolHandler.js +20 -0
  60. package/src/handlers/input/RemoveAllBindingsToolHandler.js +20 -0
  61. package/src/handlers/input/RemoveInputActionToolHandler.js +20 -0
  62. package/src/handlers/input/RemoveInputBindingToolHandler.js +20 -0
  63. package/src/handlers/input/TouchSimulationHandler.js +142 -0
  64. package/src/handlers/menu/ExecuteMenuItemToolHandler.js +304 -0
  65. package/src/handlers/package/PackageManagerToolHandler.js +248 -0
  66. package/src/handlers/package/RegistryConfigToolHandler.js +198 -0
  67. package/src/handlers/playmode/GetEditorStateToolHandler.js +81 -0
  68. package/src/handlers/playmode/PauseToolHandler.js +44 -0
  69. package/src/handlers/playmode/PlayToolHandler.js +91 -0
  70. package/src/handlers/playmode/StopToolHandler.js +77 -0
  71. package/src/handlers/playmode/WaitForEditorStateToolHandler.js +45 -0
  72. package/src/handlers/scene/CreateSceneToolHandler.js +91 -0
  73. package/src/handlers/scene/GetSceneInfoToolHandler.js +20 -0
  74. package/src/handlers/scene/ListScenesToolHandler.js +58 -0
  75. package/src/handlers/scene/LoadSceneToolHandler.js +92 -0
  76. package/src/handlers/scene/SaveSceneToolHandler.js +76 -0
  77. package/src/handlers/screenshot/AnalyzeScreenshotToolHandler.js +238 -0
  78. package/src/handlers/screenshot/CaptureScreenshotToolHandler.js +692 -0
  79. package/src/handlers/script/BuildCodeIndexToolHandler.js +163 -0
  80. package/src/handlers/script/ScriptCreateClassFileToolHandler.js +60 -0
  81. package/src/handlers/script/ScriptEditStructuredToolHandler.js +173 -0
  82. package/src/handlers/script/ScriptIndexStatusToolHandler.js +61 -0
  83. package/src/handlers/script/ScriptPackagesListToolHandler.js +103 -0
  84. package/src/handlers/script/ScriptReadToolHandler.js +106 -0
  85. package/src/handlers/script/ScriptRefactorRenameToolHandler.js +83 -0
  86. package/src/handlers/script/ScriptRefsFindToolHandler.js +144 -0
  87. package/src/handlers/script/ScriptRemoveSymbolToolHandler.js +79 -0
  88. package/src/handlers/script/ScriptSearchToolHandler.js +320 -0
  89. package/src/handlers/script/ScriptSymbolFindToolHandler.js +117 -0
  90. package/src/handlers/script/ScriptSymbolsGetToolHandler.js +96 -0
  91. package/src/handlers/settings/GetProjectSettingsToolHandler.js +161 -0
  92. package/src/handlers/settings/UpdateProjectSettingsToolHandler.js +272 -0
  93. package/src/handlers/system/GetCommandStatsToolHandler.js +25 -0
  94. package/src/handlers/system/PingToolHandler.js +53 -0
  95. package/src/handlers/system/RefreshAssetsToolHandler.js +45 -0
  96. package/src/handlers/ui/ClickUIElementToolHandler.js +110 -0
  97. package/src/handlers/ui/FindUIElementsToolHandler.js +63 -0
  98. package/src/handlers/ui/GetUIElementStateToolHandler.js +50 -0
  99. package/src/handlers/ui/SetUIElementValueToolHandler.js +49 -0
  100. package/src/handlers/ui/SimulateUIInputToolHandler.js +156 -0
  101. package/src/handlers/video/CaptureVideoForToolHandler.js +96 -0
  102. package/src/handlers/video/CaptureVideoStartToolHandler.js +38 -0
  103. package/src/handlers/video/CaptureVideoStatusToolHandler.js +30 -0
  104. package/src/handlers/video/CaptureVideoStopToolHandler.js +32 -0
  105. package/src/lsp/CSharpLspUtils.js +134 -0
  106. package/src/lsp/LspProcessManager.js +60 -0
  107. package/src/lsp/LspRpcClient.js +133 -0
  108. package/src/tools/analysis/analyzeSceneContents.js +100 -0
  109. package/src/tools/analysis/findByComponent.js +87 -0
  110. package/src/tools/analysis/getAnimatorState.js +326 -0
  111. package/src/tools/analysis/getComponentValues.js +182 -0
  112. package/src/tools/analysis/getGameObjectDetails.js +159 -0
  113. package/src/tools/analysis/getInputActionsState.js +329 -0
  114. package/src/tools/analysis/getObjectReferences.js +86 -0
  115. package/src/tools/input/inputActionsEditor.js +556 -0
  116. package/src/tools/scene/createScene.js +112 -0
  117. package/src/tools/scene/getSceneInfo.js +95 -0
  118. package/src/tools/scene/listScenes.js +82 -0
  119. package/src/tools/scene/loadScene.js +122 -0
  120. package/src/tools/scene/saveScene.js +91 -0
  121. package/src/tools/system/ping.js +72 -0
  122. package/src/tools/video/recordFor.js +31 -0
  123. package/src/tools/video/recordPlayMode.js +61 -0
  124. package/src/utils/csharpParse.js +88 -0
  125. package/src/utils/validators.js +90 -0
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Tool definition for get_scene_info
3
+ */
4
+ export const getSceneInfoToolDefinition = {
5
+ name: 'get_scene_info',
6
+ description: 'Get detailed information about a scene',
7
+ inputSchema: {
8
+ type: 'object',
9
+ properties: {
10
+ scenePath: {
11
+ type: 'string',
12
+ description: 'Full path to the scene file. If not provided, gets info about current scene.'
13
+ },
14
+ sceneName: {
15
+ type: 'string',
16
+ description: 'Name of the scene. Use either scenePath or sceneName, not both.'
17
+ },
18
+ includeGameObjects: {
19
+ type: 'boolean',
20
+ description: 'Include list of root GameObjects in the scene (only for loaded scenes). Default: false'
21
+ }
22
+ },
23
+ required: []
24
+ }
25
+ };
26
+
27
+ /**
28
+ * Handler for get_scene_info tool
29
+ */
30
+ export async function getSceneInfoHandler(unityConnection, args) {
31
+ try {
32
+ // Check connection
33
+ if (!unityConnection.isConnected()) {
34
+ return {
35
+ content: [
36
+ {
37
+ type: 'text',
38
+ text: 'Failed to get scene info: Unity connection not available'
39
+ }
40
+ ],
41
+ isError: true
42
+ };
43
+ }
44
+
45
+ // Validate that only one identifier is provided if any
46
+ if (args.scenePath && args.sceneName) {
47
+ return {
48
+ content: [
49
+ {
50
+ type: 'text',
51
+ text: 'Failed to get scene info: Provide either scenePath or sceneName, not both'
52
+ }
53
+ ],
54
+ isError: true
55
+ };
56
+ }
57
+
58
+ // Send command to Unity
59
+ const result = await unityConnection.sendCommand('get_scene_info', args);
60
+
61
+ // Handle Unity response
62
+ if (result.status === 'error') {
63
+ return {
64
+ content: [
65
+ {
66
+ type: 'text',
67
+ text: `Failed to get scene info: ${result.error}`
68
+ }
69
+ ],
70
+ isError: true
71
+ };
72
+ }
73
+
74
+ // Success response
75
+ return {
76
+ content: [
77
+ {
78
+ type: 'text',
79
+ text: result.result.summary || `Scene information retrieved`
80
+ }
81
+ ],
82
+ isError: false
83
+ };
84
+ } catch (error) {
85
+ return {
86
+ content: [
87
+ {
88
+ type: 'text',
89
+ text: `Failed to get scene info: ${error.message}`
90
+ }
91
+ ],
92
+ isError: true
93
+ };
94
+ }
95
+ }
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Tool definition for list_scenes
3
+ */
4
+ export const listScenesToolDefinition = {
5
+ name: 'list_scenes',
6
+ description: 'List all scenes in the Unity project',
7
+ inputSchema: {
8
+ type: 'object',
9
+ properties: {
10
+ includeLoadedOnly: {
11
+ type: 'boolean',
12
+ description: 'Only include currently loaded scenes (default: false)'
13
+ },
14
+ includeBuildScenesOnly: {
15
+ type: 'boolean',
16
+ description: 'Only include scenes in build settings (default: false)'
17
+ },
18
+ includePath: {
19
+ type: 'string',
20
+ description: 'Filter scenes by path pattern (e.g., "Levels" to find scenes in Levels folder)'
21
+ }
22
+ },
23
+ required: []
24
+ }
25
+ };
26
+
27
+ /**
28
+ * Handler for list_scenes tool
29
+ */
30
+ export async function listScenesHandler(unityConnection, args) {
31
+ try {
32
+ // Check connection
33
+ if (!unityConnection.isConnected()) {
34
+ return {
35
+ content: [
36
+ {
37
+ type: 'text',
38
+ text: 'Failed to list scenes: Unity connection not available'
39
+ }
40
+ ],
41
+ isError: true
42
+ };
43
+ }
44
+
45
+ // Send command to Unity
46
+ const result = await unityConnection.sendCommand('list_scenes', args);
47
+
48
+ // Handle Unity response
49
+ if (result.status === 'error') {
50
+ return {
51
+ content: [
52
+ {
53
+ type: 'text',
54
+ text: `Failed to list scenes: ${result.error}`
55
+ }
56
+ ],
57
+ isError: true
58
+ };
59
+ }
60
+
61
+ // Success response
62
+ return {
63
+ content: [
64
+ {
65
+ type: 'text',
66
+ text: result.result.summary || `Found ${result.result.totalCount} scenes`
67
+ }
68
+ ],
69
+ isError: false
70
+ };
71
+ } catch (error) {
72
+ return {
73
+ content: [
74
+ {
75
+ type: 'text',
76
+ text: `Failed to list scenes: ${error.message}`
77
+ }
78
+ ],
79
+ isError: true
80
+ };
81
+ }
82
+ }
@@ -0,0 +1,122 @@
1
+ /**
2
+ * Tool definition for load_scene
3
+ */
4
+ export const loadSceneToolDefinition = {
5
+ name: 'load_scene',
6
+ description: 'Load a scene in Unity',
7
+ inputSchema: {
8
+ type: 'object',
9
+ properties: {
10
+ scenePath: {
11
+ type: 'string',
12
+ description: 'Full path to the scene file (e.g., "Assets/Scenes/MainMenu.unity")'
13
+ },
14
+ sceneName: {
15
+ type: 'string',
16
+ description: 'Name of the scene to load (must be in build settings). Use either scenePath or sceneName, not both.'
17
+ },
18
+ loadMode: {
19
+ type: 'string',
20
+ enum: ['Single', 'Additive'],
21
+ description: 'How to load the scene. Single replaces current scene(s), Additive adds to current scene(s) (default: Single)'
22
+ }
23
+ },
24
+ required: []
25
+ }
26
+ };
27
+
28
+ /**
29
+ * Handler for load_scene tool
30
+ */
31
+ export async function loadSceneHandler(unityConnection, args) {
32
+ try {
33
+ // Check connection
34
+ if (!unityConnection.isConnected()) {
35
+ return {
36
+ content: [
37
+ {
38
+ type: 'text',
39
+ text: 'Failed to load scene: Unity connection not available'
40
+ }
41
+ ],
42
+ isError: true
43
+ };
44
+ }
45
+
46
+ // Validate that either scenePath or sceneName is provided
47
+ if (!args.scenePath && !args.sceneName) {
48
+ return {
49
+ content: [
50
+ {
51
+ type: 'text',
52
+ text: 'Failed to load scene: Either scenePath or sceneName must be provided'
53
+ }
54
+ ],
55
+ isError: true
56
+ };
57
+ }
58
+
59
+ // Validate that only one is provided
60
+ if (args.scenePath && args.sceneName) {
61
+ return {
62
+ content: [
63
+ {
64
+ type: 'text',
65
+ text: 'Failed to load scene: Provide either scenePath or sceneName, not both'
66
+ }
67
+ ],
68
+ isError: true
69
+ };
70
+ }
71
+
72
+ // Validate load mode
73
+ if (args.loadMode && !['Single', 'Additive'].includes(args.loadMode)) {
74
+ return {
75
+ content: [
76
+ {
77
+ type: 'text',
78
+ text: 'Failed to load scene: Invalid load mode. Must be "Single" or "Additive"'
79
+ }
80
+ ],
81
+ isError: true
82
+ };
83
+ }
84
+
85
+ // Send command to Unity
86
+ const result = await unityConnection.sendCommand('load_scene', args);
87
+
88
+ // Handle Unity response
89
+ if (result.status === 'error') {
90
+ return {
91
+ content: [
92
+ {
93
+ type: 'text',
94
+ text: `Failed to load scene: ${result.error}`
95
+ }
96
+ ],
97
+ isError: true
98
+ };
99
+ }
100
+
101
+ // Success response
102
+ return {
103
+ content: [
104
+ {
105
+ type: 'text',
106
+ text: result.result.summary || `Scene loaded successfully`
107
+ }
108
+ ],
109
+ isError: false
110
+ };
111
+ } catch (error) {
112
+ return {
113
+ content: [
114
+ {
115
+ type: 'text',
116
+ text: `Failed to load scene: ${error.message}`
117
+ }
118
+ ],
119
+ isError: true
120
+ };
121
+ }
122
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Tool definition for save_scene
3
+ */
4
+ export const saveSceneToolDefinition = {
5
+ name: 'save_scene',
6
+ description: 'Save the current scene in Unity',
7
+ inputSchema: {
8
+ type: 'object',
9
+ properties: {
10
+ scenePath: {
11
+ type: 'string',
12
+ description: 'Path where to save the scene. If not provided, saves to current scene path. Required if saveAs is true.'
13
+ },
14
+ saveAs: {
15
+ type: 'boolean',
16
+ description: 'Whether to save as a new scene (creates a copy). Default: false'
17
+ }
18
+ },
19
+ required: []
20
+ }
21
+ };
22
+
23
+ /**
24
+ * Handler for save_scene tool
25
+ */
26
+ export async function saveSceneHandler(unityConnection, args) {
27
+ try {
28
+ // Check connection
29
+ if (!unityConnection.isConnected()) {
30
+ return {
31
+ content: [
32
+ {
33
+ type: 'text',
34
+ text: 'Failed to save scene: Unity connection not available'
35
+ }
36
+ ],
37
+ isError: true
38
+ };
39
+ }
40
+
41
+ // Validate saveAs requires scenePath
42
+ if (args.saveAs && !args.scenePath) {
43
+ return {
44
+ content: [
45
+ {
46
+ type: 'text',
47
+ text: 'Failed to save scene: scenePath is required when saveAs is true'
48
+ }
49
+ ],
50
+ isError: true
51
+ };
52
+ }
53
+
54
+ // Send command to Unity
55
+ const result = await unityConnection.sendCommand('save_scene', args);
56
+
57
+ // Handle Unity response
58
+ if (result.status === 'error') {
59
+ return {
60
+ content: [
61
+ {
62
+ type: 'text',
63
+ text: `Failed to save scene: ${result.error}`
64
+ }
65
+ ],
66
+ isError: true
67
+ };
68
+ }
69
+
70
+ // Success response
71
+ return {
72
+ content: [
73
+ {
74
+ type: 'text',
75
+ text: result.result.summary || `Scene saved successfully`
76
+ }
77
+ ],
78
+ isError: false
79
+ };
80
+ } catch (error) {
81
+ return {
82
+ content: [
83
+ {
84
+ type: 'text',
85
+ text: `Failed to save scene: ${error.message}`
86
+ }
87
+ ],
88
+ isError: true
89
+ };
90
+ }
91
+ }
@@ -0,0 +1,72 @@
1
+ import {
2
+ ListToolsRequestSchema,
3
+ CallToolRequestSchema
4
+ } from '@modelcontextprotocol/sdk/types.js';
5
+
6
+ /**
7
+ * Ping tool for testing Unity connection
8
+ */
9
+ export function registerPingTool(server, unityConnection) {
10
+ // Tool definition
11
+ const pingTool = {
12
+ name: 'ping',
13
+ description: 'Test connection to Unity Editor',
14
+ inputSchema: {
15
+ type: 'object',
16
+ properties: {
17
+ message: {
18
+ type: 'string',
19
+ description: 'Optional message to echo back'
20
+ }
21
+ },
22
+ required: []
23
+ }
24
+ };
25
+
26
+ // Register list tools handler
27
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
28
+ return {
29
+ tools: [pingTool]
30
+ };
31
+ });
32
+
33
+ // Register call tool handler
34
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
35
+ const { name, arguments: args } = request.params;
36
+
37
+ if (name === 'ping') {
38
+ try {
39
+ // Ensure connected
40
+ if (!unityConnection.isConnected()) {
41
+ await unityConnection.connect();
42
+ }
43
+
44
+ // Send ping with optional message
45
+ const result = await unityConnection.sendCommand('ping', {
46
+ message: args?.message || 'ping'
47
+ });
48
+
49
+ return {
50
+ content: [
51
+ {
52
+ type: 'text',
53
+ text: `Unity responded: ${result.message} (echo: ${result.echo || args?.message || 'ping'})`
54
+ }
55
+ ]
56
+ };
57
+ } catch (error) {
58
+ return {
59
+ content: [
60
+ {
61
+ type: 'text',
62
+ text: `Failed to ping Unity: ${error.message}`
63
+ }
64
+ ],
65
+ isError: true
66
+ };
67
+ }
68
+ }
69
+
70
+ throw new Error(`Tool not found: ${name}`);
71
+ });
72
+ }
@@ -0,0 +1,31 @@
1
+ import { UnityConnection } from '../../core/unityConnection.js';
2
+ import { CaptureVideoForToolHandler } from '../../handlers/video/CaptureVideoForToolHandler.js';
3
+
4
+ async function main() {
5
+ const unity = new UnityConnection();
6
+ try { await unity.connect(); } catch (e) { console.error('connect failed:', e.message); process.exit(1); }
7
+ try {
8
+ const ts = new Date().toISOString().replace(/[:.]/g, '').slice(0, 15);
9
+ const outputPath = `Assets/Screenshots/recordings/mcp_for_${ts}.mp4`;
10
+ const handler = new CaptureVideoForToolHandler(unity);
11
+ const result = await handler.execute({
12
+ captureMode: 'game',
13
+ width: 1280,
14
+ height: 720,
15
+ fps: 30,
16
+ durationSec: 2,
17
+ play: true
18
+ });
19
+ if (result && result.error) {
20
+ console.error('capture_video_for error:', result.error);
21
+ } else {
22
+ console.log('capture_video_for ok:', result && result.outputPath);
23
+ }
24
+ } catch (e) {
25
+ console.error('recordFor error:', e.message);
26
+ } finally {
27
+ unity.disconnect();
28
+ }
29
+ }
30
+
31
+ main();
@@ -0,0 +1,61 @@
1
+ import { UnityConnection } from '../../core/unityConnection.js';
2
+ import { config } from '../../core/config.js';
3
+
4
+ async function main() {
5
+ const unity = new UnityConnection();
6
+ try {
7
+ await unity.connect();
8
+ } catch (e) {
9
+ console.error('[recordPlayMode] Failed to connect to Unity:', e.message);
10
+ process.exit(1);
11
+ }
12
+
13
+ try {
14
+ // Enter Play Mode
15
+ await unity.sendCommand('play_game', {});
16
+ // Domain reload causes disconnect. Reconnect and wait for play.
17
+ for (let i = 0; i < 60; i++) {
18
+ if (!unity.isConnected()) {
19
+ try { await unity.connect(); } catch {}
20
+ }
21
+ try {
22
+ const s = await unity.sendCommand('get_editor_state', {});
23
+ if (s && s.isPlaying) break;
24
+ } catch {}
25
+ await new Promise(r => setTimeout(r, 500));
26
+ }
27
+
28
+ // Start recording
29
+ const start = await unity.sendCommand('capture_video_start', {
30
+ captureMode: 'game',
31
+ width: 1280,
32
+ height: 720,
33
+ fps: 30,
34
+ maxDurationSec: 3
35
+ });
36
+ if (start && start.error) {
37
+ console.error('[recordPlayMode] start error:', start.error);
38
+ }
39
+
40
+ // Poll status a few times
41
+ for (let i = 0; i < 16; i++) {
42
+ const st = await unity.sendCommand('capture_video_status', {});
43
+ // console.log('[recordPlayMode] status', st);
44
+ await new Promise(r => setTimeout(r, 250));
45
+ }
46
+
47
+ // Ensure stopped
48
+ await unity.sendCommand('capture_video_stop', {});
49
+
50
+ } catch (e) {
51
+ console.error('[recordPlayMode] error:', e.message);
52
+ } finally {
53
+ try {
54
+ if (!unity.isConnected()) { await unity.connect(); }
55
+ await unity.sendCommand('stop_game', {});
56
+ } catch {}
57
+ unity.disconnect();
58
+ }
59
+ }
60
+
61
+ main();
@@ -0,0 +1,88 @@
1
+ // Lightweight C# symbol extractor for Node-side fallback (not Roslyn-accurate)
2
+ export function parseFileSymbols(relPath, text) {
3
+ const lines = text.split('\n');
4
+ const result = { path: relPath, symbols: [] };
5
+ const nsRx = /^\s*namespace\s+([A-Za-z0-9_.]+)/;
6
+ const typeRx = /^\s*(?:public|internal|protected|private|abstract|sealed|static|partial|new|readonly|\s)*\s*(class|struct|interface|enum)\s+([A-Za-z0-9_]+)/;
7
+ const methodRx = /^\s*(?:public|internal|protected|private|static|virtual|override|async|sealed|extern|unsafe|new|readonly|\s)+[A-Za-z0-9_<>,\[\]\?\(\)\.:\s]+\s+([A-Za-z0-9_]+)\s*\(([^)]*)\)\s*(?:\{|=>|;)/;
8
+ const propRx = /^\s*(?:public|internal|protected|private|static|virtual|override|sealed|new|readonly|\s)+[A-Za-z0-9_<>,\[\]\?\.:\s]+\s+([A-Za-z0-9_]+)\s*\{/;
9
+
10
+ const nsStack = [];
11
+ const typeStack = [];
12
+ let braceDepth = 0;
13
+
14
+ for (let i = 0; i < lines.length; i++) {
15
+ const line = lines[i];
16
+ const nsM = line.match(nsRx);
17
+ if (nsM) nsStack.push({ name: nsM[1], line: i + 1 });
18
+
19
+ const tM = line.match(typeRx);
20
+ if (tM) {
21
+ const kind = tM[1];
22
+ const name = tM[2];
23
+ typeStack.push({ kind, name, startLine: i + 1, braceDepthAtStart: braceDepth });
24
+ result.symbols.push({
25
+ name,
26
+ kind,
27
+ namespace: nsStack.map(n => n.name).join('.'),
28
+ container: typeStack.length > 1 ? typeStack[typeStack.length - 2].name : null,
29
+ startLine: i + 1,
30
+ endLine: 0,
31
+ startColumn: 1,
32
+ endColumn: 1,
33
+ });
34
+ }
35
+
36
+ const mM = line.match(methodRx);
37
+ if (mM) {
38
+ const name = mM[1];
39
+ result.symbols.push({
40
+ name,
41
+ kind: 'method',
42
+ namespace: nsStack.map(n => n.name).join('.'),
43
+ container: typeStack.length ? typeStack[typeStack.length - 1].name : null,
44
+ startLine: i + 1,
45
+ endLine: i + 1,
46
+ startColumn: 1,
47
+ endColumn: 1,
48
+ });
49
+ }
50
+
51
+ const pM = line.match(propRx);
52
+ if (pM) {
53
+ const name = pM[1];
54
+ result.symbols.push({
55
+ name,
56
+ kind: 'property',
57
+ namespace: nsStack.map(n => n.name).join('.'),
58
+ container: typeStack.length ? typeStack[typeStack.length - 1].name : null,
59
+ startLine: i + 1,
60
+ endLine: i + 1,
61
+ startColumn: 1,
62
+ endColumn: 1,
63
+ });
64
+ }
65
+
66
+ // brace accounting
67
+ for (const ch of line) {
68
+ if (ch === '{') braceDepth++;
69
+ else if (ch === '}') braceDepth--;
70
+ }
71
+ // close types whose body ended
72
+ while (typeStack.length && braceDepth < typeStack[typeStack.length - 1].braceDepthAtStart) {
73
+ const closed = typeStack.pop();
74
+ // set endLine for the last matching symbol
75
+ for (let j = result.symbols.length - 1; j >= 0; j--) {
76
+ const s = result.symbols[j];
77
+ if (s.kind === closed.kind && s.name === closed.name && s.endLine === 0) { s.endLine = i + 1; break; }
78
+ }
79
+ }
80
+ }
81
+ const last = Math.max(1, lines.length);
82
+ for (const s of result.symbols) {
83
+ if (!s.endLine) s.endLine = last;
84
+ if (!s.endColumn) s.endColumn = 1;
85
+ }
86
+ return result;
87
+ }
88
+