1
1
import WebSocket from 'ws' ;
2
2
import { v4 as uuidv4 } from 'uuid' ;
3
3
import { McpUnityError , ErrorType } from '../utils/errors.js' ;
4
- import { execSync } from 'child_process ' ;
5
- import { default as winreg } from 'winreg ' ;
4
+ import { promises as fs } from 'fs ' ;
5
+ import path from 'path ' ;
6
6
export class McpUnity {
7
7
logger ;
8
- port ;
8
+ port = null ;
9
9
ws = null ;
10
10
pendingRequests = new Map ( ) ;
11
- REQUEST_TIMEOUT ;
11
+ requestTimeout = 10000 ;
12
12
retryDelay = 1000 ;
13
13
constructor ( logger ) {
14
14
this . logger = logger ;
15
- // Initialize port from environment variable or use default
16
- const envRegistry = process . platform === 'win32'
17
- ? this . getUnityPortFromWindowsRegistry ( )
18
- : this . getUnityPortFromUnixRegistry ( ) ;
19
- const envPort = process . env . UNITY_PORT || envRegistry ;
20
- this . port = envPort ? parseInt ( envPort , 10 ) : 8090 ;
21
- this . logger . info ( `Using port: ${ this . port } for Unity WebSocket connection` ) ;
22
- // Initialize timeout from environment variable (in seconds; it is the same as Cline) or use default (10 seconds)
23
- const envTimeout = process . env . UNITY_REQUEST_TIMEOUT ;
24
- this . REQUEST_TIMEOUT = envTimeout ? parseInt ( envTimeout , 10 ) * 1000 : 10000 ;
25
- this . logger . info ( `Using request timeout: ${ this . REQUEST_TIMEOUT / 1000 } seconds` ) ;
26
15
}
27
16
/**
28
17
* Start the Unity connection
29
18
* @param clientName Optional name of the MCP client connecting to Unity
30
19
*/
31
20
async start ( clientName ) {
32
21
try {
22
+ this . logger . info ( 'Attempting to read startup parameters...' ) ;
23
+ this . parseAndSetConfig ( ) ;
33
24
this . logger . info ( 'Attempting to connect to Unity WebSocket...' ) ;
34
25
await this . connect ( clientName ) ; // Pass client name to connect
35
26
this . logger . info ( 'Successfully connected to Unity WebSocket' ) ;
@@ -45,6 +36,19 @@ export class McpUnity {
45
36
}
46
37
return Promise . resolve ( ) ;
47
38
}
39
+ /**
40
+ * Reads our configuration file and sets parameters of the server based on them.
41
+ */
42
+ async parseAndSetConfig ( ) {
43
+ const config = await this . readConfigFileAsJson ( ) ;
44
+ const configPort = config . Port ;
45
+ this . port = configPort ? parseInt ( configPort , 10 ) : 8090 ;
46
+ this . logger . info ( `Using port: ${ this . port } for Unity WebSocket connection` ) ;
47
+ // Initialize timeout from environment variable (in seconds; it is the same as Cline) or use default (10 seconds)
48
+ const configTimeout = config . RequestTimeoutSeconds ;
49
+ this . requestTimeout = configTimeout ? parseInt ( configTimeout , 10 ) * 1000 : 10000 ;
50
+ this . logger . info ( `Using request timeout: ${ this . requestTimeout / 1000 } seconds` ) ;
51
+ }
48
52
/**
49
53
* Connect to the Unity WebSocket
50
54
* @param clientName Optional name of the MCP client connecting to Unity
@@ -74,7 +78,7 @@ export class McpUnity {
74
78
this . disconnect ( ) ;
75
79
reject ( new McpUnityError ( ErrorType . CONNECTION , 'Connection timeout' ) ) ;
76
80
}
77
- } , this . REQUEST_TIMEOUT ) ;
81
+ } , this . requestTimeout ) ;
78
82
this . ws . onopen = ( ) => {
79
83
clearTimeout ( connectionTimeout ) ;
80
84
this . logger . debug ( 'WebSocket connected' ) ;
@@ -193,12 +197,12 @@ export class McpUnity {
193
197
// Create timeout for the request
194
198
const timeout = setTimeout ( ( ) => {
195
199
if ( this . pendingRequests . has ( requestId ) ) {
196
- this . logger . error ( `Request ${ requestId } timed out after ${ this . REQUEST_TIMEOUT } ms` ) ;
200
+ this . logger . error ( `Request ${ requestId } timed out after ${ this . requestTimeout } ms` ) ;
197
201
this . pendingRequests . delete ( requestId ) ;
198
202
reject ( new McpUnityError ( ErrorType . TIMEOUT , 'Request timed out' ) ) ;
199
203
}
200
204
this . reconnect ( ) ;
201
- } , this . REQUEST_TIMEOUT ) ;
205
+ } , this . requestTimeout ) ;
202
206
// Store pending request
203
207
this . pendingRequests . set ( requestId , {
204
208
resolve,
@@ -225,27 +229,20 @@ export class McpUnity {
225
229
return this . ws !== null && this . ws . readyState === WebSocket . OPEN ;
226
230
}
227
231
/**
228
- * Retrieves the UNITY_PORT value from the Windows registry (HKCU\Environment)
229
- * @returns The port value as a string if found, otherwise an empty string
230
- */
231
- getUnityPortFromWindowsRegistry ( ) {
232
- const regKey = new winreg ( { hive : winreg . HKCU , key : '\\Environment' } ) ;
233
- let result = '' ;
234
- regKey . get ( 'UNITY_PORT' , ( err , item ) => {
235
- if ( err ) {
236
- this . logger . error ( `Error getting registry value: ${ err . message } ` ) ;
237
- }
238
- else {
239
- result = item . value ;
240
- }
241
- } ) ;
242
- return result ;
243
- }
244
- /**
245
- * Retrieves the UNITY_PORT value from Unix-like system environment variables
246
- * @returns The port value as a string if found, otherwise an empty string
232
+ * Read the McpUnitySettings.json file and return its contents as a JSON object.
233
+ * @returns a JSON object with the contents of the McpUnitySettings.json file.
247
234
*/
248
- getUnityPortFromUnixRegistry ( ) {
249
- return execSync ( 'printenv UNITY_PORT' , { stdio : [ 'pipe' , 'pipe' , 'ignore' ] } ) . toString ( ) . trim ( ) ;
235
+ async readConfigFileAsJson ( ) {
236
+ const configPath = path . resolve ( process . cwd ( ) , 'build/McpUnitySettings.json' ) ;
237
+ this . logger . debug ( `Reading McpUnitySettings.json from ${ configPath } ` ) ;
238
+ try {
239
+ const content = await fs . readFile ( configPath , 'utf-8' ) ;
240
+ const json = JSON . parse ( content ) ;
241
+ return json ;
242
+ }
243
+ catch ( err ) {
244
+ this . logger . debug ( `McpUnitySettings.json not found or unreadable: ${ err instanceof Error ? err . message : String ( err ) } ` ) ;
245
+ return { } ;
246
+ }
250
247
}
251
248
}
0 commit comments