diff --git a/.efrocachemap b/.efrocachemap index d9d547a3..c2e9eaa5 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -420,7 +420,7 @@ "assets/build/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/a9/71/9286d55c45c37877f3267850f90b", "assets/build/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/2f/09/36e691de67eb8f155449a7170861", "assets/build/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/fd/a8/ad50785ce206e8dc3dcc7358b173", - "assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/29/af/04afc95d95a712c79207291083ba", + "assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/91/b8/a48083094a02608f5dff387d3469", "assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/ac/3e/c50dc4e98df47f858c3a73ac4272", "assets/build/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/44/ed/5b972fa848cffb73723533c2ccb7", "assets/build/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/9d/63/d360eeff63bc64e098427498880d", @@ -444,7 +444,7 @@ "assets/build/ba_data/data/languages/polish.json": "https://files.ballistica.net/cache/ba1/cd/c1/82bf70c3ee4894791506f4da1a15", "assets/build/ba_data/data/languages/portuguese.json": "https://files.ballistica.net/cache/ba1/98/45/ddeb7e797c02fb967e0c8b0dff7d", "assets/build/ba_data/data/languages/romanian.json": "https://files.ballistica.net/cache/ba1/44/3c/7cc06ca8d5475e1687d0ed05bdbf", - "assets/build/ba_data/data/languages/russian.json": "https://files.ballistica.net/cache/ba1/ab/44/3d8b939f0c177cc46e8b0e4cae74", + "assets/build/ba_data/data/languages/russian.json": "https://files.ballistica.net/cache/ba1/15/e2/08ce8831b3a6ee2a5324a4112b04", "assets/build/ba_data/data/languages/serbian.json": "https://files.ballistica.net/cache/ba1/e1/22/5471375791f8825a63e06371df29", "assets/build/ba_data/data/languages/slovak.json": "https://files.ballistica.net/cache/ba1/b7/0a/fab820b96e7aa587ee56427ecdc2", "assets/build/ba_data/data/languages/spanish.json": "https://files.ballistica.net/cache/ba1/72/6a/55e4f8ce819d0c39ee6d86e8c6a0", diff --git a/assets/src/server/ballisticacore_server.py b/assets/src/server/ballisticacore_server.py index bbc234af..db758886 100755 --- a/assets/src/server/ballisticacore_server.py +++ b/assets/src/server/ballisticacore_server.py @@ -89,6 +89,7 @@ class ServerManagerApp: self._subprocess_sent_clean_exit = False self._subprocess_sent_unclean_exit = False self._subprocess_thread: Optional[Thread] = None + self._subprocess_exited_cleanly: bool = False # This may override the above defaults. self._parse_command_line_args() @@ -584,23 +585,42 @@ class ServerManagerApp: if os.name == 'nt' else './ballisticacore_headless') assert self._ba_root_path is not None self._subprocess = None + + # Launch! try: self._subprocess = subprocess.Popen( [binary_name, '-cfgdir', self._ba_root_path], stdin=subprocess.PIPE, cwd='dist') except Exception as exc: - print(f'Error launching server subprocess: {exc}', + print( + f'{Clr.RED}Error launching server subprocess: {exc}{Clr.RST}', + file=sys.stderr, + flush=True) + + # Do the thing. + try: + self._run_subprocess_until_exit() + except Exception as exc: + print(f'{Clr.RED}Error running server subprocess: {exc}{Clr.RST}', file=sys.stderr, flush=True) - # Do the thing. - # No matter how this ends up, make sure the process is dead after. - if self._subprocess is not None: - try: - self._run_subprocess_until_exit() - finally: - self._kill_subprocess() + self._kill_subprocess() + + # Avoid super fast death loops. + if (not self._subprocess_exited_cleanly and self._auto_restart + and not self._done): + time.sleep(5.0) + + # If they don't want auto-restart, we'll exit the whole wrapper. + # (and with an error code if things ended badly). + if not self._auto_restart: + self._wrapper_shutdown_desired = True + if not self._subprocess_exited_cleanly: + self._should_report_subprocess_error = True + + self._reset_subprocess_vars() # If we want to die completely after this subprocess has ended, # tell the main thread to die. @@ -662,8 +682,10 @@ class ServerManagerApp: self._subprocess.stdin.flush() def _run_subprocess_until_exit(self) -> None: + if self._subprocess is None: + return + assert current_thread() is self._subprocess_thread - assert self._subprocess is not None assert self._subprocess.stdin is not None # Send the initial server config which should kick things off. @@ -706,25 +728,13 @@ class ServerManagerApp: code: Optional[int] = self._subprocess.poll() if code is not None: - # If they don't want auto-restart, exit the whole wrapper. - # (and make sure to exit with an error code if things ended - # badly here). - if not self._auto_restart: - self._wrapper_shutdown_desired = True - if code != 0: - self._should_report_subprocess_error = True - clr = Clr.CYN if code == 0 else Clr.RED print( f'{clr}Server subprocess exited' f' with code {code}.{Clr.RST}', file=sys.stderr, flush=True) - self._reset_subprocess_vars() - - # Avoid super fast death loops. - if code != 0 and self._auto_restart: - time.sleep(5.0) + self._subprocess_exited_cleanly = (code == 0) break time.sleep(0.25) @@ -809,6 +819,7 @@ class ServerManagerApp: self._subprocess_sent_clean_exit = False self._subprocess_sent_unclean_exit = False self._subprocess_force_kill_time = None + self._subprocess_exited_cleanly = False def _kill_subprocess(self) -> None: """End the server subprocess if it still exists.""" @@ -827,7 +838,6 @@ class ServerManagerApp: self._subprocess.wait(timeout=10) except subprocess.TimeoutExpired: self._subprocess.kill() - self._reset_subprocess_vars() print(f'{Clr.CYN}Subprocess stopped.{Clr.RST}', file=sys.stderr, flush=True)