@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.
- package/LICENSE +21 -0
- package/README.md +206 -0
- package/bin/unity-mcp-server +2 -0
- package/package.json +73 -0
- package/src/core/codeIndex.js +163 -0
- package/src/core/codeIndexDb.js +96 -0
- package/src/core/config.js +165 -0
- package/src/core/indexWatcher.js +52 -0
- package/src/core/projectInfo.js +111 -0
- package/src/core/server.js +294 -0
- package/src/core/unityConnection.js +426 -0
- package/src/handlers/analysis/AnalyzeSceneContentsToolHandler.js +35 -0
- package/src/handlers/analysis/FindByComponentToolHandler.js +20 -0
- package/src/handlers/analysis/GetAnimatorStateToolHandler.js +37 -0
- package/src/handlers/analysis/GetComponentValuesToolHandler.js +20 -0
- package/src/handlers/analysis/GetGameObjectDetailsToolHandler.js +35 -0
- package/src/handlers/analysis/GetInputActionsStateToolHandler.js +37 -0
- package/src/handlers/analysis/GetObjectReferencesToolHandler.js +20 -0
- package/src/handlers/asset/AssetDatabaseToolHandler.js +221 -0
- package/src/handlers/asset/AssetDependencyToolHandler.js +201 -0
- package/src/handlers/asset/AssetImportSettingsToolHandler.js +170 -0
- package/src/handlers/asset/CreateMaterialToolHandler.js +96 -0
- package/src/handlers/asset/CreatePrefabToolHandler.js +78 -0
- package/src/handlers/asset/ExitPrefabModeToolHandler.js +83 -0
- package/src/handlers/asset/InstantiatePrefabToolHandler.js +133 -0
- package/src/handlers/asset/ModifyMaterialToolHandler.js +76 -0
- package/src/handlers/asset/ModifyPrefabToolHandler.js +72 -0
- package/src/handlers/asset/OpenPrefabToolHandler.js +121 -0
- package/src/handlers/asset/SavePrefabToolHandler.js +106 -0
- package/src/handlers/base/BaseToolHandler.js +133 -0
- package/src/handlers/compilation/GetCompilationStateToolHandler.js +90 -0
- package/src/handlers/component/AddComponentToolHandler.js +126 -0
- package/src/handlers/component/GetComponentTypesToolHandler.js +100 -0
- package/src/handlers/component/ListComponentsToolHandler.js +85 -0
- package/src/handlers/component/ModifyComponentToolHandler.js +143 -0
- package/src/handlers/component/RemoveComponentToolHandler.js +108 -0
- package/src/handlers/console/ClearConsoleToolHandler.js +160 -0
- package/src/handlers/console/ReadConsoleToolHandler.js +276 -0
- package/src/handlers/editor/LayerManagementToolHandler.js +160 -0
- package/src/handlers/editor/SelectionToolHandler.js +141 -0
- package/src/handlers/editor/TagManagementToolHandler.js +129 -0
- package/src/handlers/editor/ToolManagementToolHandler.js +135 -0
- package/src/handlers/editor/WindowManagementToolHandler.js +125 -0
- package/src/handlers/gameobject/CreateGameObjectToolHandler.js +131 -0
- package/src/handlers/gameobject/DeleteGameObjectToolHandler.js +101 -0
- package/src/handlers/gameobject/FindGameObjectToolHandler.js +119 -0
- package/src/handlers/gameobject/GetHierarchyToolHandler.js +132 -0
- package/src/handlers/gameobject/ModifyGameObjectToolHandler.js +128 -0
- package/src/handlers/index.js +389 -0
- package/src/handlers/input/AddInputActionToolHandler.js +20 -0
- package/src/handlers/input/AddInputBindingToolHandler.js +20 -0
- package/src/handlers/input/CreateActionMapToolHandler.js +20 -0
- package/src/handlers/input/CreateCompositeBindingToolHandler.js +20 -0
- package/src/handlers/input/GamepadSimulationHandler.js +116 -0
- package/src/handlers/input/InputSystemHandler.js +80 -0
- package/src/handlers/input/KeyboardSimulationHandler.js +79 -0
- package/src/handlers/input/ManageControlSchemesToolHandler.js +20 -0
- package/src/handlers/input/MouseSimulationHandler.js +107 -0
- package/src/handlers/input/RemoveActionMapToolHandler.js +20 -0
- package/src/handlers/input/RemoveAllBindingsToolHandler.js +20 -0
- package/src/handlers/input/RemoveInputActionToolHandler.js +20 -0
- package/src/handlers/input/RemoveInputBindingToolHandler.js +20 -0
- package/src/handlers/input/TouchSimulationHandler.js +142 -0
- package/src/handlers/menu/ExecuteMenuItemToolHandler.js +304 -0
- package/src/handlers/package/PackageManagerToolHandler.js +248 -0
- package/src/handlers/package/RegistryConfigToolHandler.js +198 -0
- package/src/handlers/playmode/GetEditorStateToolHandler.js +81 -0
- package/src/handlers/playmode/PauseToolHandler.js +44 -0
- package/src/handlers/playmode/PlayToolHandler.js +91 -0
- package/src/handlers/playmode/StopToolHandler.js +77 -0
- package/src/handlers/playmode/WaitForEditorStateToolHandler.js +45 -0
- package/src/handlers/scene/CreateSceneToolHandler.js +91 -0
- package/src/handlers/scene/GetSceneInfoToolHandler.js +20 -0
- package/src/handlers/scene/ListScenesToolHandler.js +58 -0
- package/src/handlers/scene/LoadSceneToolHandler.js +92 -0
- package/src/handlers/scene/SaveSceneToolHandler.js +76 -0
- package/src/handlers/screenshot/AnalyzeScreenshotToolHandler.js +238 -0
- package/src/handlers/screenshot/CaptureScreenshotToolHandler.js +692 -0
- package/src/handlers/script/BuildCodeIndexToolHandler.js +163 -0
- package/src/handlers/script/ScriptCreateClassFileToolHandler.js +60 -0
- package/src/handlers/script/ScriptEditStructuredToolHandler.js +173 -0
- package/src/handlers/script/ScriptIndexStatusToolHandler.js +61 -0
- package/src/handlers/script/ScriptPackagesListToolHandler.js +103 -0
- package/src/handlers/script/ScriptReadToolHandler.js +106 -0
- package/src/handlers/script/ScriptRefactorRenameToolHandler.js +83 -0
- package/src/handlers/script/ScriptRefsFindToolHandler.js +144 -0
- package/src/handlers/script/ScriptRemoveSymbolToolHandler.js +79 -0
- package/src/handlers/script/ScriptSearchToolHandler.js +320 -0
- package/src/handlers/script/ScriptSymbolFindToolHandler.js +117 -0
- package/src/handlers/script/ScriptSymbolsGetToolHandler.js +96 -0
- package/src/handlers/settings/GetProjectSettingsToolHandler.js +161 -0
- package/src/handlers/settings/UpdateProjectSettingsToolHandler.js +272 -0
- package/src/handlers/system/GetCommandStatsToolHandler.js +25 -0
- package/src/handlers/system/PingToolHandler.js +53 -0
- package/src/handlers/system/RefreshAssetsToolHandler.js +45 -0
- package/src/handlers/ui/ClickUIElementToolHandler.js +110 -0
- package/src/handlers/ui/FindUIElementsToolHandler.js +63 -0
- package/src/handlers/ui/GetUIElementStateToolHandler.js +50 -0
- package/src/handlers/ui/SetUIElementValueToolHandler.js +49 -0
- package/src/handlers/ui/SimulateUIInputToolHandler.js +156 -0
- package/src/handlers/video/CaptureVideoForToolHandler.js +96 -0
- package/src/handlers/video/CaptureVideoStartToolHandler.js +38 -0
- package/src/handlers/video/CaptureVideoStatusToolHandler.js +30 -0
- package/src/handlers/video/CaptureVideoStopToolHandler.js +32 -0
- package/src/lsp/CSharpLspUtils.js +134 -0
- package/src/lsp/LspProcessManager.js +60 -0
- package/src/lsp/LspRpcClient.js +133 -0
- package/src/tools/analysis/analyzeSceneContents.js +100 -0
- package/src/tools/analysis/findByComponent.js +87 -0
- package/src/tools/analysis/getAnimatorState.js +326 -0
- package/src/tools/analysis/getComponentValues.js +182 -0
- package/src/tools/analysis/getGameObjectDetails.js +159 -0
- package/src/tools/analysis/getInputActionsState.js +329 -0
- package/src/tools/analysis/getObjectReferences.js +86 -0
- package/src/tools/input/inputActionsEditor.js +556 -0
- package/src/tools/scene/createScene.js +112 -0
- package/src/tools/scene/getSceneInfo.js +95 -0
- package/src/tools/scene/listScenes.js +82 -0
- package/src/tools/scene/loadScene.js +122 -0
- package/src/tools/scene/saveScene.js +91 -0
- package/src/tools/system/ping.js +72 -0
- package/src/tools/video/recordFor.js +31 -0
- package/src/tools/video/recordPlayMode.js +61 -0
- package/src/utils/csharpParse.js +88 -0
- package/src/utils/validators.js +90 -0
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { BaseToolHandler } from '../base/BaseToolHandler.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Registry Configuration Tool Handler for Unity MCP
|
|
5
|
+
* Handles Unity Package Registry configuration (OpenUPM, Unity NuGet, etc.)
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
export default class RegistryConfigToolHandler extends BaseToolHandler {
|
|
9
|
+
constructor(unityConnection) {
|
|
10
|
+
super(
|
|
11
|
+
'registry_config',
|
|
12
|
+
'Configure package registries (OpenUPM/NuGet): list/add/remove scopes or recommend.',
|
|
13
|
+
{
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
action: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
enum: ['list', 'add_openupm', 'add_nuget', 'remove', 'add_scope', 'recommend'],
|
|
19
|
+
description: 'The registry operation to perform'
|
|
20
|
+
},
|
|
21
|
+
registryName: {
|
|
22
|
+
type: 'string',
|
|
23
|
+
description: 'Name of the registry (for remove and add_scope actions)'
|
|
24
|
+
},
|
|
25
|
+
scope: {
|
|
26
|
+
type: 'string',
|
|
27
|
+
description: 'Package scope to add (e.g., com.unity, com.cysharp)'
|
|
28
|
+
},
|
|
29
|
+
scopes: {
|
|
30
|
+
type: 'array',
|
|
31
|
+
items: { type: 'string' },
|
|
32
|
+
description: 'Array of package scopes to add'
|
|
33
|
+
},
|
|
34
|
+
autoAddPopular: {
|
|
35
|
+
type: 'boolean',
|
|
36
|
+
description: 'Automatically add popular package scopes (default: true)'
|
|
37
|
+
},
|
|
38
|
+
registry: {
|
|
39
|
+
type: 'string',
|
|
40
|
+
enum: ['openupm', 'nuget'],
|
|
41
|
+
description: 'Registry type for recommendations'
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
required: ['action']
|
|
45
|
+
}
|
|
46
|
+
);
|
|
47
|
+
this.unityConnection = unityConnection;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
validate(params) {
|
|
51
|
+
const { action, registryName, scope } = params || {};
|
|
52
|
+
if (!action) throw new Error('action is required');
|
|
53
|
+
|
|
54
|
+
const valid = ['list', 'add_openupm', 'add_nuget', 'remove', 'add_scope', 'recommend'];
|
|
55
|
+
if (!valid.includes(action)) {
|
|
56
|
+
throw new Error(`Invalid action: ${action}. Must be one of: ${valid.join(', ')}`);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
switch (action) {
|
|
60
|
+
case 'remove':
|
|
61
|
+
if (!registryName) throw new Error('Registry name is required for remove action');
|
|
62
|
+
break;
|
|
63
|
+
case 'add_scope':
|
|
64
|
+
if (!registryName || !scope) throw new Error('Registry name and scope are required for add_scope action');
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
async execute(params) {
|
|
70
|
+
const { action, ...parameters } = params;
|
|
71
|
+
|
|
72
|
+
// Ensure connected
|
|
73
|
+
if (!this.unityConnection.isConnected()) {
|
|
74
|
+
await this.unityConnection.connect();
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const result = await this.unityConnection.sendCommand('registry_config', {
|
|
78
|
+
action,
|
|
79
|
+
...parameters
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return this.formatResponse(action, result);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
formatResponse(action, result) {
|
|
86
|
+
if (result && result.error) {
|
|
87
|
+
return { success: false, error: result.error };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
switch (action) {
|
|
91
|
+
case 'list':
|
|
92
|
+
return {
|
|
93
|
+
success: result.success,
|
|
94
|
+
action: 'list',
|
|
95
|
+
totalCount: result.totalCount || 0,
|
|
96
|
+
registries: (result.registries || []).map(reg => ({
|
|
97
|
+
name: reg.name,
|
|
98
|
+
url: reg.url,
|
|
99
|
+
scopeCount: reg.scopes ? reg.scopes.length : 0,
|
|
100
|
+
scopes: reg.scopes || []
|
|
101
|
+
})),
|
|
102
|
+
message: result.message || `Found ${result.totalCount || 0} configured registries`
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
case 'add_openupm':
|
|
106
|
+
return {
|
|
107
|
+
success: result.success,
|
|
108
|
+
action: 'add_openupm',
|
|
109
|
+
registryUrl: result.registryUrl,
|
|
110
|
+
scopes: result.scopes || [],
|
|
111
|
+
message: result.message || 'Successfully configured OpenUPM registry',
|
|
112
|
+
instructions: [
|
|
113
|
+
'OpenUPM registry has been added to your project.',
|
|
114
|
+
'You can now install packages from OpenUPM using their package IDs.',
|
|
115
|
+
'Popular packages include:',
|
|
116
|
+
' - com.cysharp.unitask (UniTask)',
|
|
117
|
+
' - com.neuecc.unirx (UniRx)',
|
|
118
|
+
' - jp.keijiro.noiseshader (Noise Shader)'
|
|
119
|
+
]
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
case 'add_nuget':
|
|
123
|
+
return {
|
|
124
|
+
success: result.success,
|
|
125
|
+
action: 'add_nuget',
|
|
126
|
+
registryUrl: result.registryUrl,
|
|
127
|
+
scopes: result.scopes || [],
|
|
128
|
+
message: result.message || 'Successfully configured Unity NuGet registry',
|
|
129
|
+
instructions: [
|
|
130
|
+
'Unity NuGet registry has been added to your project.',
|
|
131
|
+
'You can now install NuGet packages that are compatible with Unity.',
|
|
132
|
+
'Popular packages include:',
|
|
133
|
+
' - org.nuget.newtonsoft.json (Newtonsoft.Json)',
|
|
134
|
+
' - org.nuget.system.threading.tasks.extensions',
|
|
135
|
+
' - org.nuget.sqlite-net-pcl (SQLite)'
|
|
136
|
+
]
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
case 'remove':
|
|
140
|
+
return {
|
|
141
|
+
success: result.success,
|
|
142
|
+
action: 'remove',
|
|
143
|
+
message: result.message || `Successfully removed registry`
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
case 'add_scope':
|
|
147
|
+
return {
|
|
148
|
+
success: result.success,
|
|
149
|
+
action: 'add_scope',
|
|
150
|
+
scopes: result.scopes || [],
|
|
151
|
+
message: result.message || 'Successfully added scope to registry'
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
case 'recommend':
|
|
155
|
+
if (result.allRecommendations) {
|
|
156
|
+
const formatted = {
|
|
157
|
+
success: result.success,
|
|
158
|
+
action: 'recommend',
|
|
159
|
+
recommendations: {},
|
|
160
|
+
message: result.message || 'Package recommendations for available registries'
|
|
161
|
+
};
|
|
162
|
+
for (const [registry, packages] of Object.entries(result.allRecommendations)) {
|
|
163
|
+
formatted.recommendations[registry] = packages.map(pkg => ({
|
|
164
|
+
packageId: pkg.packageId,
|
|
165
|
+
name: pkg.name,
|
|
166
|
+
description: pkg.description,
|
|
167
|
+
scope: pkg.scope
|
|
168
|
+
}));
|
|
169
|
+
}
|
|
170
|
+
return formatted;
|
|
171
|
+
}
|
|
172
|
+
return {
|
|
173
|
+
success: result.success,
|
|
174
|
+
action: 'recommend',
|
|
175
|
+
registry: result.registry,
|
|
176
|
+
packages: (result.packages || []).map(pkg => ({
|
|
177
|
+
packageId: pkg.packageId,
|
|
178
|
+
name: pkg.name,
|
|
179
|
+
description: pkg.description,
|
|
180
|
+
scope: pkg.scope
|
|
181
|
+
})),
|
|
182
|
+
message: result.message || `Recommendations for ${result.registry}`
|
|
183
|
+
}; }
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
static getExamples() {
|
|
187
|
+
return [
|
|
188
|
+
{ description: 'List configured registries', input: { action: 'list' } },
|
|
189
|
+
{ description: 'Add OpenUPM registry with popular scopes', input: { action: 'add_openupm', autoAddPopular: true } },
|
|
190
|
+
{ description: 'Add OpenUPM with custom scopes', input: { action: 'add_openupm', scopes: ['com.cysharp', 'com.neuecc', 'jp.keijiro'], autoAddPopular: false } },
|
|
191
|
+
{ description: 'Add Unity NuGet registry', input: { action: 'add_nuget', autoAddPopular: true } },
|
|
192
|
+
{ description: 'Add scope to existing registry', input: { action: 'add_scope', registryName: 'OpenUPM', scope: 'com.yasirkula' } },
|
|
193
|
+
{ description: 'Remove a registry', input: { action: 'remove', registryName: 'OpenUPM' } },
|
|
194
|
+
{ description: 'Get recommended packages for OpenUPM', input: { action: 'recommend', registry: 'openupm' } },
|
|
195
|
+
{ description: 'Get all registry recommendations', input: { action: 'recommend' } }
|
|
196
|
+
];
|
|
197
|
+
}
|
|
198
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { BaseToolHandler } from '../base/BaseToolHandler.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handler for getting Unity editor state
|
|
5
|
+
*/
|
|
6
|
+
export class GetEditorStateToolHandler extends BaseToolHandler {
|
|
7
|
+
constructor(unityConnection) {
|
|
8
|
+
super(
|
|
9
|
+
'get_editor_state',
|
|
10
|
+
'Get editor state (play mode, platform, selected objects, etc.). Optionally wait for a target state.',
|
|
11
|
+
{
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
waitFor: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: { isPlaying: { type: 'boolean' } },
|
|
17
|
+
required: ['isPlaying']
|
|
18
|
+
},
|
|
19
|
+
timeoutMs: { type: 'number' },
|
|
20
|
+
pollMs: { type: 'number' }
|
|
21
|
+
},
|
|
22
|
+
required: []
|
|
23
|
+
}
|
|
24
|
+
);
|
|
25
|
+
this.unityConnection = unityConnection;
|
|
26
|
+
// 軽量なソフトキャッシュ(単発取得時のみ使用)
|
|
27
|
+
this._last = { t: 0, state: null };
|
|
28
|
+
this._minIntervalMs = parseInt(process.env.MIN_EDITOR_STATE_INTERVAL_MS || '', 10);
|
|
29
|
+
if (!(this._minIntervalMs >= 0)) this._minIntervalMs = 250; // 既定250ms
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Executes the get editor state command
|
|
34
|
+
* @param {object} params - Empty object for this command
|
|
35
|
+
* @returns {Promise<object>} Editor state information
|
|
36
|
+
*/
|
|
37
|
+
async execute(params) {
|
|
38
|
+
const wait = params?.waitFor;
|
|
39
|
+
const timeoutMs = typeof params?.timeoutMs === 'number' ? params.timeoutMs : null;
|
|
40
|
+
const pollMs = typeof params?.pollMs === 'number' ? params.pollMs : 500;
|
|
41
|
+
|
|
42
|
+
const getOnce = async () => {
|
|
43
|
+
// 非waitモード時はキャッシュを活用
|
|
44
|
+
if (!wait && this._last.state && Date.now() - this._last.t < this._minIntervalMs) {
|
|
45
|
+
return this._last.state;
|
|
46
|
+
}
|
|
47
|
+
if (!this.unityConnection.isConnected()) {
|
|
48
|
+
await this.unityConnection.connect();
|
|
49
|
+
}
|
|
50
|
+
const result = await this.unityConnection.sendCommand('get_editor_state', {});
|
|
51
|
+
if (result.status === 'error') {
|
|
52
|
+
const error = new Error(result.error);
|
|
53
|
+
error.code = 'UNITY_ERROR';
|
|
54
|
+
throw error;
|
|
55
|
+
}
|
|
56
|
+
if (!wait) {
|
|
57
|
+
this._last = { t: Date.now(), state: result };
|
|
58
|
+
}
|
|
59
|
+
return result;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
if (!wait) {
|
|
63
|
+
return await getOnce();
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const want = !!wait.isPlaying;
|
|
67
|
+
const start = Date.now();
|
|
68
|
+
for (;;) {
|
|
69
|
+
try {
|
|
70
|
+
const state = await this.unityConnection.sendCommand('get_editor_state', {});
|
|
71
|
+
if (state && !!state.isPlaying === want) {
|
|
72
|
+
return { status: 'success', state, waitedMs: Date.now() - start };
|
|
73
|
+
}
|
|
74
|
+
} catch {}
|
|
75
|
+
await new Promise(r => setTimeout(r, pollMs));
|
|
76
|
+
if (timeoutMs != null && Date.now() - start > timeoutMs) {
|
|
77
|
+
return await getOnce();
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { BaseToolHandler } from '../base/BaseToolHandler.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handler for pausing/resuming Unity play mode
|
|
5
|
+
*/
|
|
6
|
+
export class PauseToolHandler extends BaseToolHandler {
|
|
7
|
+
constructor(unityConnection) {
|
|
8
|
+
super(
|
|
9
|
+
'pause_game',
|
|
10
|
+
'Toggle Pause/Resume in Play Mode.',
|
|
11
|
+
{
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {},
|
|
14
|
+
required: []
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
this.unityConnection = unityConnection;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Executes the pause/resume command
|
|
22
|
+
* @param {object} params - Empty object for this command
|
|
23
|
+
* @returns {Promise<object>} Play mode state
|
|
24
|
+
*/
|
|
25
|
+
async execute(params) {
|
|
26
|
+
// Ensure connected
|
|
27
|
+
if (!this.unityConnection.isConnected()) {
|
|
28
|
+
throw new Error('Unity connection not available');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Send pause command to Unity
|
|
32
|
+
const result = await this.unityConnection.sendCommand('pause_game', params);
|
|
33
|
+
|
|
34
|
+
// Check for Unity-side errors
|
|
35
|
+
if (result.status === 'error') {
|
|
36
|
+
const error = new Error(result.error);
|
|
37
|
+
error.code = 'UNITY_ERROR';
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Return the result with state information
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { BaseToolHandler } from '../base/BaseToolHandler.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handler for starting Unity play mode
|
|
5
|
+
*/
|
|
6
|
+
export class PlayToolHandler extends BaseToolHandler {
|
|
7
|
+
constructor(unityConnection) {
|
|
8
|
+
super(
|
|
9
|
+
'play_game',
|
|
10
|
+
'Enter Play Mode.',
|
|
11
|
+
{
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {},
|
|
14
|
+
required: []
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
this.unityConnection = unityConnection;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Executes the play command
|
|
22
|
+
* @param {object} params - Empty object for this command
|
|
23
|
+
* @returns {Promise<object>} Play mode state
|
|
24
|
+
*/
|
|
25
|
+
async execute(params) {
|
|
26
|
+
const initialDelayMs = typeof params?.initialDelayMs === 'number' ? params.initialDelayMs : 3000;
|
|
27
|
+
const reconnectIntervalMs = typeof params?.reconnectIntervalMs === 'number' ? params.reconnectIntervalMs : 500;
|
|
28
|
+
const pollIntervalMs = typeof params?.pollIntervalMs === 'number' ? params.pollIntervalMs : 800;
|
|
29
|
+
const maxWaitMs = typeof params?.maxWaitMs === 'number' ? params.maxWaitMs : null; // null = unlimited
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
if (!this.unityConnection.isConnected()) {
|
|
33
|
+
await this.unityConnection.connect();
|
|
34
|
+
}
|
|
35
|
+
const result = await this.unityConnection.sendCommand('play_game', params);
|
|
36
|
+
if (result?.status === 'error') {
|
|
37
|
+
const error = new Error(result.error || 'Unity returned error');
|
|
38
|
+
error.code = 'UNITY_ERROR';
|
|
39
|
+
throw error;
|
|
40
|
+
}
|
|
41
|
+
const startOk = Date.now();
|
|
42
|
+
for (;;) {
|
|
43
|
+
try {
|
|
44
|
+
const state = await this.unityConnection.sendCommand('get_editor_state', {});
|
|
45
|
+
if (state && state.isPlaying) {
|
|
46
|
+
return { status: 'success', message: 'Entered play mode', state };
|
|
47
|
+
}
|
|
48
|
+
} catch {}
|
|
49
|
+
await sleep(pollIntervalMs);
|
|
50
|
+
if (maxWaitMs != null && Date.now() - startOk > maxWaitMs) {
|
|
51
|
+
return result;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
} catch (err) {
|
|
55
|
+
const msg = err?.message || '';
|
|
56
|
+
const transient = /(Connection closed|timeout|ECONNRESET|EPIPE|socket|Not connected)/i.test(msg);
|
|
57
|
+
if (!transient) throw err;
|
|
58
|
+
|
|
59
|
+
const start = Date.now();
|
|
60
|
+
await sleep(initialDelayMs);
|
|
61
|
+
// Reconnect until connected (unlimited unless maxWaitMs specified)
|
|
62
|
+
for (;;) {
|
|
63
|
+
if (this.unityConnection.isConnected()) break;
|
|
64
|
+
try { await this.unityConnection.connect(); } catch {}
|
|
65
|
+
await sleep(reconnectIntervalMs);
|
|
66
|
+
if (maxWaitMs != null && Date.now() - start > maxWaitMs) {
|
|
67
|
+
const e = new Error('Timed out waiting to reconnect after Play start');
|
|
68
|
+
e.code = 'RECONNECT_TIMEOUT';
|
|
69
|
+
throw e;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
// Poll for isPlaying
|
|
73
|
+
for (;;) {
|
|
74
|
+
try {
|
|
75
|
+
const state = await this.unityConnection.sendCommand('get_editor_state', {});
|
|
76
|
+
if (state && state.isPlaying) {
|
|
77
|
+
return { status: 'success', message: 'Entered play mode (reconnected after reload)', state };
|
|
78
|
+
}
|
|
79
|
+
} catch {}
|
|
80
|
+
await sleep(pollIntervalMs);
|
|
81
|
+
if (maxWaitMs != null && Date.now() - start > maxWaitMs) {
|
|
82
|
+
const e = new Error('Timed out waiting for Play mode after reconnect');
|
|
83
|
+
e.code = 'PLAY_WAIT_TIMEOUT';
|
|
84
|
+
throw e;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { BaseToolHandler } from '../base/BaseToolHandler.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handler for stopping Unity play mode
|
|
5
|
+
*/
|
|
6
|
+
export class StopToolHandler extends BaseToolHandler {
|
|
7
|
+
constructor(unityConnection) {
|
|
8
|
+
super(
|
|
9
|
+
'stop_game',
|
|
10
|
+
'Exit Play Mode and return to Edit Mode.',
|
|
11
|
+
{
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {},
|
|
14
|
+
required: []
|
|
15
|
+
}
|
|
16
|
+
);
|
|
17
|
+
this.unityConnection = unityConnection;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Executes the stop command
|
|
22
|
+
* @param {object} params - Empty object for this command
|
|
23
|
+
* @returns {Promise<object>} Play mode state
|
|
24
|
+
*/
|
|
25
|
+
async execute(params) {
|
|
26
|
+
const reconnectIntervalMs = typeof params?.reconnectIntervalMs === 'number' ? params.reconnectIntervalMs : 500;
|
|
27
|
+
const pollIntervalMs = typeof params?.pollIntervalMs === 'number' ? params.pollIntervalMs : 500;
|
|
28
|
+
const maxWaitMs = typeof params?.maxWaitMs === 'number' ? params.maxWaitMs : null; // null = unlimited
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
if (!this.unityConnection.isConnected()) {
|
|
32
|
+
await this.unityConnection.connect();
|
|
33
|
+
}
|
|
34
|
+
const result = await this.unityConnection.sendCommand('stop_game', params);
|
|
35
|
+
if (result?.status === 'error') {
|
|
36
|
+
const error = new Error(result.error || 'Unity returned error');
|
|
37
|
+
error.code = 'UNITY_ERROR';
|
|
38
|
+
throw error;
|
|
39
|
+
}
|
|
40
|
+
const startOk = Date.now();
|
|
41
|
+
for (;;) {
|
|
42
|
+
try {
|
|
43
|
+
const state = await this.unityConnection.sendCommand('get_editor_state', {});
|
|
44
|
+
if (state && !state.isPlaying) {
|
|
45
|
+
return { status: 'success', message: 'Exited play mode', state };
|
|
46
|
+
}
|
|
47
|
+
} catch {}
|
|
48
|
+
await sleep(pollIntervalMs);
|
|
49
|
+
if (maxWaitMs != null && Date.now() - startOk > maxWaitMs) return result;
|
|
50
|
+
}
|
|
51
|
+
} catch (err) {
|
|
52
|
+
const transient = /(Connection closed|timeout|ECONNRESET|EPIPE|socket|Not connected)/i.test(err?.message || '');
|
|
53
|
+
if (!transient) throw err;
|
|
54
|
+
const start = Date.now();
|
|
55
|
+
for (;;) {
|
|
56
|
+
if (!this.unityConnection.isConnected()) {
|
|
57
|
+
try { await this.unityConnection.connect(); } catch {}
|
|
58
|
+
await sleep(reconnectIntervalMs);
|
|
59
|
+
}
|
|
60
|
+
try {
|
|
61
|
+
const state = await this.unityConnection.sendCommand('get_editor_state', {});
|
|
62
|
+
if (state && !state.isPlaying) {
|
|
63
|
+
return { status: 'success', message: 'Exited play mode (reconnected after reload)', state };
|
|
64
|
+
}
|
|
65
|
+
} catch {}
|
|
66
|
+
await sleep(pollIntervalMs);
|
|
67
|
+
if (maxWaitMs != null && Date.now() - start > maxWaitMs) {
|
|
68
|
+
const e = new Error('Timed out waiting for stop');
|
|
69
|
+
e.code = 'PLAY_WAIT_TIMEOUT';
|
|
70
|
+
throw e;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { BaseToolHandler } from '../base/BaseToolHandler.js';
|
|
2
|
+
|
|
3
|
+
export class WaitForEditorStateToolHandler extends BaseToolHandler {
|
|
4
|
+
constructor(unityConnection) {
|
|
5
|
+
super(
|
|
6
|
+
'wait_for_editor_state',
|
|
7
|
+
'Wait until the Unity editor reaches the requested state (e.g., isPlaying).',
|
|
8
|
+
{
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
isPlaying: { type: 'boolean', description: 'Target play state (true=Play, false=Edit)' },
|
|
12
|
+
timeoutMs: { type: 'number', description: 'Max wait time (null/omitted = unlimited)' },
|
|
13
|
+
pollMs: { type: 'number', description: 'Polling interval in ms (default 500)' }
|
|
14
|
+
},
|
|
15
|
+
required: ['isPlaying']
|
|
16
|
+
}
|
|
17
|
+
);
|
|
18
|
+
this.unityConnection = unityConnection;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async execute(params) {
|
|
22
|
+
const want = !!params.isPlaying;
|
|
23
|
+
const timeoutMs = typeof params?.timeoutMs === 'number' ? params.timeoutMs : null;
|
|
24
|
+
const pollMs = typeof params?.pollMs === 'number' ? params.pollMs : 500;
|
|
25
|
+
|
|
26
|
+
const start = Date.now();
|
|
27
|
+
for (;;) {
|
|
28
|
+
if (!this.unityConnection.isConnected()) {
|
|
29
|
+
try { await this.unityConnection.connect(); } catch {}
|
|
30
|
+
}
|
|
31
|
+
try {
|
|
32
|
+
const state = await this.unityConnection.sendCommand('get_editor_state', {});
|
|
33
|
+
if (!!state?.isPlaying === want) {
|
|
34
|
+
return { status: 'success', state, waitedMs: Date.now() - start };
|
|
35
|
+
}
|
|
36
|
+
} catch {}
|
|
37
|
+
await new Promise(r => setTimeout(r, pollMs));
|
|
38
|
+
if (timeoutMs != null && Date.now() - start > timeoutMs) {
|
|
39
|
+
const state = this.unityConnection.isConnected() ? await this.unityConnection.sendCommand('get_editor_state', {}).catch(() => ({})) : {};
|
|
40
|
+
return { status: 'timeout', state, waitedMs: Date.now() - start };
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { BaseToolHandler } from '../base/BaseToolHandler.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Handler for creating new scenes in Unity
|
|
5
|
+
*/
|
|
6
|
+
export class CreateSceneToolHandler extends BaseToolHandler {
|
|
7
|
+
constructor(unityConnection) {
|
|
8
|
+
super(
|
|
9
|
+
'create_scene',
|
|
10
|
+
'Create a new scene (optionally load it and add to build settings).',
|
|
11
|
+
{
|
|
12
|
+
type: 'object',
|
|
13
|
+
properties: {
|
|
14
|
+
sceneName: {
|
|
15
|
+
type: 'string',
|
|
16
|
+
description: 'Name of the scene to create'
|
|
17
|
+
},
|
|
18
|
+
path: {
|
|
19
|
+
type: 'string',
|
|
20
|
+
description: 'Path where the scene should be saved (e.g., "Assets/Scenes/"). If not specified, defaults to "Assets/Scenes/"'
|
|
21
|
+
},
|
|
22
|
+
loadScene: {
|
|
23
|
+
type: 'boolean',
|
|
24
|
+
description: 'Whether to load the scene after creation (default: true)'
|
|
25
|
+
},
|
|
26
|
+
addToBuildSettings: {
|
|
27
|
+
type: 'boolean',
|
|
28
|
+
description: 'Whether to add the scene to build settings (default: false)'
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
required: ['sceneName']
|
|
32
|
+
}
|
|
33
|
+
);
|
|
34
|
+
this.unityConnection = unityConnection;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Validates the input parameters
|
|
39
|
+
* @param {object} params - Input parameters
|
|
40
|
+
* @throws {Error} If validation fails
|
|
41
|
+
*/
|
|
42
|
+
validate(params) {
|
|
43
|
+
super.validate(params);
|
|
44
|
+
|
|
45
|
+
// Additional validation for scene name
|
|
46
|
+
if (!params.sceneName || params.sceneName.trim() === '') {
|
|
47
|
+
throw new Error('Scene name cannot be empty');
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Check for invalid characters in scene name
|
|
51
|
+
if (params.sceneName.includes('/') || params.sceneName.includes('\\')) {
|
|
52
|
+
throw new Error('Scene name contains invalid characters');
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Executes the create scene command
|
|
58
|
+
* @param {object} params - Validated input parameters
|
|
59
|
+
* @returns {Promise<object>} Creation result
|
|
60
|
+
*/
|
|
61
|
+
async execute(params) {
|
|
62
|
+
// Ensure connected
|
|
63
|
+
if (!this.unityConnection.isConnected()) {
|
|
64
|
+
throw new Error('Unity connection not available');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Send command to Unity
|
|
68
|
+
const result = await this.unityConnection.sendCommand('create_scene', params);
|
|
69
|
+
|
|
70
|
+
// Check for Unity-side errors
|
|
71
|
+
if (result.status === 'error') {
|
|
72
|
+
const error = new Error(result.error);
|
|
73
|
+
error.code = 'UNITY_ERROR';
|
|
74
|
+
throw error;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Handle undefined or null results from Unity
|
|
78
|
+
if (result.result === undefined || result.result === null) {
|
|
79
|
+
return {
|
|
80
|
+
status: 'success',
|
|
81
|
+
sceneName: params.sceneName,
|
|
82
|
+
path: params.path || 'Assets/Scenes/',
|
|
83
|
+
loadScene: params.loadScene !== false,
|
|
84
|
+
addToBuildSettings: params.addToBuildSettings === true,
|
|
85
|
+
message: 'Scene creation completed but Unity returned no details'
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return result.result;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { BaseToolHandler } from '../base/BaseToolHandler.js';
|
|
2
|
+
import { getSceneInfoToolDefinition, getSceneInfoHandler } from '../../tools/scene/getSceneInfo.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Handler for get_scene_info tool
|
|
6
|
+
*/
|
|
7
|
+
export class GetSceneInfoToolHandler extends BaseToolHandler {
|
|
8
|
+
constructor(unityConnection) {
|
|
9
|
+
super(
|
|
10
|
+
getSceneInfoToolDefinition.name,
|
|
11
|
+
getSceneInfoToolDefinition.description,
|
|
12
|
+
getSceneInfoToolDefinition.inputSchema
|
|
13
|
+
);
|
|
14
|
+
this.unityConnection = unityConnection;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async execute(args) {
|
|
18
|
+
return getSceneInfoHandler(this.unityConnection, args);
|
|
19
|
+
}
|
|
20
|
+
}
|