@@ -269,29 +269,26 @@ export class UnityEditor {
269269 throw new Error ( `Cannot execute Unity ${ this . version . toString ( ) } on Apple Silicon Macs.` ) ;
270270 }
271271
272+ const linuxEnvOverrides = process . platform === 'linux'
273+ ? await this . prepareLinuxAudioEnvironment ( )
274+ : undefined ;
275+ const baseEditorEnv : NodeJS . ProcessEnv = {
276+ ...process . env ,
277+ UNITY_THISISABUILDMACHINE : '1' ,
278+ ...( linuxEnvOverrides ?? { } )
279+ } ;
280+
272281 if ( process . platform === 'linux' &&
273282 ! command . args . includes ( '-nographics' )
274283 ) {
275- // On Linux, force Unity to run under Xvfb and provide a dummy audio driver
276- // to prevent FMOD from failing to initialize the output device when no
277- // actual audio device is present (common in CI/container environments).
278- const linuxEnv = {
279- ...process . env ,
280- DISPLAY : ':99' ,
281- UNITY_THISISABUILDMACHINE : '1' ,
282- // Tell various audio systems to use a dummy/out-of-process driver
283- SDL_AUDIODRIVER : process . env . SDL_AUDIODRIVER || 'dummy' ,
284- AUDIODRIVER : process . env . AUDIODRIVER || 'dummy' ,
285- AUDIODEV : process . env . AUDIODEV || 'null' ,
286- // For PulseAudio: point to an invalid socket to avoid connecting
287- PULSE_SERVER : process . env . PULSE_SERVER || '/tmp/invalid-pulse-socket'
288- } ;
289-
290284 unityProcess = spawn (
291285 'xvfb-run' ,
292286 [ this . editorPath , ...command . args ] , {
293287 stdio : [ 'ignore' , 'ignore' , 'ignore' ] ,
294- env : linuxEnv
288+ env : {
289+ ...baseEditorEnv ,
290+ DISPLAY : baseEditorEnv . DISPLAY || ':99'
291+ }
295292 } ) ;
296293 } else if ( process . arch === 'arm64' &&
297294 process . platform === 'darwin' &&
@@ -311,10 +308,7 @@ export class UnityEditor {
311308 this . editorPath ,
312309 command . args , {
313310 stdio : [ 'ignore' , 'ignore' , 'ignore' ] ,
314- env : {
315- ...process . env ,
316- UNITY_THISISABUILDMACHINE : '1'
317- }
311+ env : baseEditorEnv
318312 } ) ;
319313 }
320314
@@ -394,6 +388,43 @@ export class UnityEditor {
394388 return path . join ( logsDir , `${ prefix ? prefix + '-' : '' } Unity-${ timestamp } .log` ) ;
395389 }
396390
391+ private async prepareLinuxAudioEnvironment ( ) : Promise < NodeJS . ProcessEnv > {
392+ if ( process . platform !== 'linux' ) {
393+ return { } ;
394+ }
395+
396+ const envOverrides : NodeJS . ProcessEnv = {
397+ SDL_AUDIODRIVER : process . env . SDL_AUDIODRIVER || 'dummy' ,
398+ AUDIODRIVER : process . env . AUDIODRIVER || 'dummy' ,
399+ AUDIODEV : process . env . AUDIODEV || 'null' ,
400+ ALSA_CARD : process . env . ALSA_CARD || 'Loopback' ,
401+ PULSE_SINK : process . env . PULSE_SINK || 'unity_dummy'
402+ } ;
403+
404+ const defaultRuntimeDir = `/run/user/${ typeof process . getuid === 'function' ? process . getuid ( ) : 1000 } ` ;
405+ const runtimeDir = process . env . XDG_RUNTIME_DIR || defaultRuntimeDir ;
406+ envOverrides . XDG_RUNTIME_DIR = runtimeDir ;
407+
408+ try {
409+ await fs . promises . mkdir ( runtimeDir , { recursive : true , mode : 0o700 } ) ;
410+ } catch ( error ) {
411+ this . logger . debug ( `Failed to ensure XDG_RUNTIME_DIR (${ runtimeDir } ): ${ error } ` ) ;
412+ }
413+
414+ await this . tryExec ( 'bash' , [ '-c' , 'pulseaudio --check 2>/dev/null || pulseaudio --start --exit-idle-time=-1 || true' ] ) ;
415+ await this . tryExec ( 'bash' , [ '-c' , 'command -v pactl >/dev/null 2>&1 && { pactl list short sinks 2>/dev/null | grep -q unity_dummy || pactl load-module module-null-sink sink_name=unity_dummy sink_properties=device.description=UnityCI >/tmp/unity-null-sink.id; } || true' ] ) ;
416+
417+ return envOverrides ;
418+ }
419+
420+ private async tryExec ( command : string , args : string [ ] ) : Promise < void > {
421+ try {
422+ await Exec ( command , args , { silent : true , showCommand : false } ) ;
423+ } catch ( error ) {
424+ this . logger . debug ( `Skipped helper command "${ command } ${ args . join ( ' ' ) } ": ${ error } ` ) ;
425+ }
426+ }
427+
397428 /**
398429 * Get the root path of the Unity Editor installation based on the provided editor path.
399430 * @param editorPath The path to the Unity Editor executable.
0 commit comments