@@ -177,7 +177,7 @@ def __init__(
177177 # If true, this means the environment was successfully loaded
178178 self ._loaded = False
179179 # The process that is started. If None, no process was started
180- self ._proc1 = None
180+ self ._process : Optional [ subprocess . Popen ] = None
181181 self ._timeout_wait : int = timeout_wait
182182 self ._communicator = self ._get_communicator (worker_id , base_port , timeout_wait )
183183 self ._worker_id = worker_id
@@ -194,7 +194,7 @@ def __init__(
194194 )
195195 if file_name is not None :
196196 try :
197- self ._proc1 = env_utils .launch_executable (
197+ self ._process = env_utils .launch_executable (
198198 file_name , self ._executable_args ()
199199 )
200200 except UnityEnvironmentException :
@@ -249,7 +249,11 @@ def _executable_args(self) -> List[str]:
249249 if self ._no_graphics :
250250 args += ["-nographics" , "-batchmode" ]
251251 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 :
253257 log_file_path = os .path .join (
254258 self ._log_folder , f"Player-{ self ._worker_id } .log"
255259 )
@@ -289,7 +293,9 @@ def _update_state(self, output: UnityRLOutputProto) -> None:
289293
290294 def reset (self ) -> None :
291295 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+ )
293299 if outputs is None :
294300 raise UnityCommunicatorStoppedException ("Communicator has exited." )
295301 self ._update_behavior_specs (outputs )
@@ -317,7 +323,7 @@ def step(self) -> None:
317323 ].action_spec .empty_action (n_agents )
318324 step_input = self ._generate_step_input (self ._env_actions )
319325 with hierarchical_timer ("communicator.exchange" ):
320- outputs = self ._communicator .exchange (step_input )
326+ outputs = self ._communicator .exchange (step_input , self . _poll_process )
321327 if outputs is None :
322328 raise UnityCommunicatorStoppedException ("Communicator has exited." )
323329 self ._update_behavior_specs (outputs )
@@ -377,6 +383,18 @@ def get_steps(
377383 self ._assert_behavior_exists (behavior_name )
378384 return self ._env_state [behavior_name ]
379385
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+
380398 def close (self ):
381399 """
382400 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:
397415 timeout = self ._timeout_wait
398416 self ._loaded = False
399417 self ._communicator .close ()
400- if self ._proc1 is not None :
418+ if self ._process is not None :
401419 # Wait a bit for the process to shutdown, but kill it if it takes too long
402420 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 ))
408423 except subprocess .TimeoutExpired :
409424 logger .info ("Environment timed out shutting down. Killing..." )
410- self ._proc1 .kill ()
425+ self ._process .kill ()
411426 # Set to None so we don't try to close multiple times.
412- self ._proc1 = None
427+ self ._process = None
413428
414429 @timed
415430 def _generate_step_input (
@@ -452,7 +467,7 @@ def _send_academy_parameters(
452467 ) -> UnityOutputProto :
453468 inputs = UnityInputProto ()
454469 inputs .rl_initialization_input .CopyFrom (init_parameters )
455- return self ._communicator .initialize (inputs )
470+ return self ._communicator .initialize (inputs , self . _poll_process )
456471
457472 @staticmethod
458473 def _wrap_unity_input (rl_input : UnityRLInputProto ) -> UnityInputProto :
@@ -473,3 +488,9 @@ def _returncode_to_signal_name(returncode: int) -> Optional[str]:
473488 except Exception :
474489 # Should generally be a ValueError, but catch everything just in case.
475490 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