@@ -177,7 +177,7 @@ def __init__(
177
177
# If true, this means the environment was successfully loaded
178
178
self ._loaded = False
179
179
# The process that is started. If None, no process was started
180
- self ._proc1 = None
180
+ self ._process : Optional [ subprocess . Popen ] = None
181
181
self ._timeout_wait : int = timeout_wait
182
182
self ._communicator = self ._get_communicator (worker_id , base_port , timeout_wait )
183
183
self ._worker_id = worker_id
@@ -194,7 +194,7 @@ def __init__(
194
194
)
195
195
if file_name is not None :
196
196
try :
197
- self ._proc1 = env_utils .launch_executable (
197
+ self ._process = env_utils .launch_executable (
198
198
file_name , self ._executable_args ()
199
199
)
200
200
except UnityEnvironmentException :
@@ -249,7 +249,11 @@ def _executable_args(self) -> List[str]:
249
249
if self ._no_graphics :
250
250
args += ["-nographics" , "-batchmode" ]
251
251
args += [UnityEnvironment ._PORT_COMMAND_LINE_ARG , str (self ._port )]
252
- if self ._log_folder :
252
+
253
+ # If the logfile arg isn't already set in the env args,
254
+ # try to set it to an output directory
255
+ logfile_set = "-logfile" in (arg .lower () for arg in self ._additional_args )
256
+ if self ._log_folder and not logfile_set :
253
257
log_file_path = os .path .join (
254
258
self ._log_folder , f"Player-{ self ._worker_id } .log"
255
259
)
@@ -289,7 +293,9 @@ def _update_state(self, output: UnityRLOutputProto) -> None:
289
293
290
294
def reset (self ) -> None :
291
295
if self ._loaded :
292
- outputs = self ._communicator .exchange (self ._generate_reset_input ())
296
+ outputs = self ._communicator .exchange (
297
+ self ._generate_reset_input (), self ._poll_process
298
+ )
293
299
if outputs is None :
294
300
raise UnityCommunicatorStoppedException ("Communicator has exited." )
295
301
self ._update_behavior_specs (outputs )
@@ -317,7 +323,7 @@ def step(self) -> None:
317
323
].action_spec .empty_action (n_agents )
318
324
step_input = self ._generate_step_input (self ._env_actions )
319
325
with hierarchical_timer ("communicator.exchange" ):
320
- outputs = self ._communicator .exchange (step_input )
326
+ outputs = self ._communicator .exchange (step_input , self . _poll_process )
321
327
if outputs is None :
322
328
raise UnityCommunicatorStoppedException ("Communicator has exited." )
323
329
self ._update_behavior_specs (outputs )
@@ -377,6 +383,18 @@ def get_steps(
377
383
self ._assert_behavior_exists (behavior_name )
378
384
return self ._env_state [behavior_name ]
379
385
386
+ def _poll_process (self ) -> None :
387
+ """
388
+ Check the status of the subprocess. If it has exited, raise a UnityEnvironmentException
389
+ :return: None
390
+ """
391
+ if not self ._process :
392
+ return
393
+ poll_res = self ._process .poll ()
394
+ if poll_res is not None :
395
+ exc_msg = self ._returncode_to_env_message (self ._process .returncode )
396
+ raise UnityEnvironmentException (exc_msg )
397
+
380
398
def close (self ):
381
399
"""
382
400
Sends a shutdown signal to the unity environment, and closes the socket connection.
@@ -397,19 +415,16 @@ def _close(self, timeout: Optional[int] = None) -> None:
397
415
timeout = self ._timeout_wait
398
416
self ._loaded = False
399
417
self ._communicator .close ()
400
- if self ._proc1 is not None :
418
+ if self ._process is not None :
401
419
# Wait a bit for the process to shutdown, but kill it if it takes too long
402
420
try :
403
- self ._proc1 .wait (timeout = timeout )
404
- signal_name = self ._returncode_to_signal_name (self ._proc1 .returncode )
405
- signal_name = f" ({ signal_name } )" if signal_name else ""
406
- return_info = f"Environment shut down with return code { self ._proc1 .returncode } { signal_name } ."
407
- logger .info (return_info )
421
+ self ._process .wait (timeout = timeout )
422
+ logger .info (self ._returncode_to_env_message (self ._process .returncode ))
408
423
except subprocess .TimeoutExpired :
409
424
logger .info ("Environment timed out shutting down. Killing..." )
410
- self ._proc1 .kill ()
425
+ self ._process .kill ()
411
426
# Set to None so we don't try to close multiple times.
412
- self ._proc1 = None
427
+ self ._process = None
413
428
414
429
@timed
415
430
def _generate_step_input (
@@ -452,7 +467,7 @@ def _send_academy_parameters(
452
467
) -> UnityOutputProto :
453
468
inputs = UnityInputProto ()
454
469
inputs .rl_initialization_input .CopyFrom (init_parameters )
455
- return self ._communicator .initialize (inputs )
470
+ return self ._communicator .initialize (inputs , self . _poll_process )
456
471
457
472
@staticmethod
458
473
def _wrap_unity_input (rl_input : UnityRLInputProto ) -> UnityInputProto :
@@ -473,3 +488,9 @@ def _returncode_to_signal_name(returncode: int) -> Optional[str]:
473
488
except Exception :
474
489
# Should generally be a ValueError, but catch everything just in case.
475
490
return None
491
+
492
+ @staticmethod
493
+ def _returncode_to_env_message (returncode : int ) -> str :
494
+ signal_name = UnityEnvironment ._returncode_to_signal_name (returncode )
495
+ signal_name = f" ({ signal_name } )" if signal_name else ""
496
+ return f"Environment shut down with return code { returncode } { signal_name } ."
0 commit comments