mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-27 01:13:13 +08:00
Cleaned up and added noninteractive mode to server script
This commit is contained in:
parent
ca5f45f3a2
commit
1dc05bed21
@ -3932,26 +3932,26 @@
|
||||
"assets/build/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/b5/85/f8b6d0558ddb87267f34254b1450",
|
||||
"assets/build/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/1c/e1/4a1a2eddda2f4aebd5f8b64ab08e",
|
||||
"assets/build/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/50/8d/bc2600ac9491f1b14d659709451f",
|
||||
"build/prefab/full/linux_arm64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/84/a1/420d282610456205a0c7317ef587",
|
||||
"build/prefab/full/linux_arm64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/a9/63/eabb9fe980c4686ff48c62dc3215",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/4c/3f/7bc2491bcb678a964043ba7d25dd",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ee/12/8addc0aa42af13c2e2903b1f2cb1",
|
||||
"build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/00/11/ca95947488543f6e9fcb6ff4a122",
|
||||
"build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d8/d9/e4f7c8454210a1bedef3416d1768",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f7/b9/21faa11f5b965f55e548b6485b2c",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ac/aa/b1641c07d4539f673445467ccef9",
|
||||
"build/prefab/full/mac_arm64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/ac/a3/ff8cbd9b0bae767ad4809360e21a",
|
||||
"build/prefab/full/mac_arm64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/46/ee/4c5599f4b9aef54eff51b7702409",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/db/ce/aec9518eee7bcd7fdc8ca80b1d76",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d8/dc/6b1c63cb28db2ff64554526d5311",
|
||||
"build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/a4/fa/8613c74733350c3cd26945823ed0",
|
||||
"build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/10/65/62b7f0eb542a037f54ea486be204",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/89/6e/569b32f6c9401b17ceed5327257f",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/99/dc/5a55ebdf3e5541c74a59f68f3f65",
|
||||
"build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/9c/58/1be66154a04eab5f00f6aa92d165",
|
||||
"build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/bd/89/b6210c67baf254ab314317973b5e",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/93/41/617d1b3436173551780992842b6e",
|
||||
"build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/3e/c5/d5a2841dc15918206f8c19b1ce02",
|
||||
"build/prefab/full/linux_arm64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/9b/14/177c2689a9f00af6d64347a4b985",
|
||||
"build/prefab/full/linux_arm64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/7a/bc/6c75d6f16c4bd1bd48ae4e31a304",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/63/98/38d14bf5bbdf01d898bf157e9491",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/61/8a/c805d1785f92c242ca038caeebc9",
|
||||
"build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/9d/88/c37bd934f664e0af630e671a70e9",
|
||||
"build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/c4/07/99095bfc02f73b9b2590a86f8f93",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/6c/a9/22b68d2b6e54bd9cd69fa59cc4e6",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/9d/fb/fba081862fc2e42a5a591c095ef3",
|
||||
"build/prefab/full/mac_arm64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/7c/24/71fd9231c09bc0584c92bf10c0d5",
|
||||
"build/prefab/full/mac_arm64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/70/2b/94679b9fe4df5a3f8ea106a8bb26",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/0f/f3/5b7f71d5f98d90444f2c1781776c",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f0/9b/5b1aa55452691590f1e0b00dbbe6",
|
||||
"build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/99/8e/0b2157debae0db9e72a9a8224e24",
|
||||
"build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/8f/18/0b6e28d94eea96e8e2336713515b",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/fa/60/b97a279322a881d860adac050c04",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a0/f0/b98326c4e5f54e5fca9e430822bb",
|
||||
"build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/75/48/8eb219cde7fc49ae97161621178a",
|
||||
"build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/6e/12/904e655eb8e283aee2604e42080d",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/1a/2c/2407f7b2b2f756215d26fd713d51",
|
||||
"build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/db/61/fb74c49d524ec0e9eb356f0b0f0a",
|
||||
"build/prefab/lib/linux_arm64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/4c/bf/393694ea67f3d590dd2706c9955e",
|
||||
"build/prefab/lib/linux_arm64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/78/cb/bb9ae4f896f862074057c8e36e1d",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ae/bd/39d7b885f7f01e81d0e96f0f85ce",
|
||||
@ -3960,12 +3960,12 @@
|
||||
"build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/52/d9/563a6949d2c4db5a915c54460fbc",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d0/6a/42fe8d2e34f95e1b3282e8422344",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/50/cf/bad44b07a4022aee3001002086b5",
|
||||
"build/prefab/lib/mac_arm64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/fa/8a/a6aedb3b2c74c055005792710be8",
|
||||
"build/prefab/lib/mac_arm64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/4b/97/0b6acf3397515a5fb997b3a81e3d",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ee/b0/81fbb6e8996205a16e3d8e912000",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/64/68/e668be0a349bcdd51efe35703b8a",
|
||||
"build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/4b/06/3e80d9b9efa82e28b77d895562b0",
|
||||
"build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/34/b1/942b8c6206051dbaa15f225bc8d9",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e1/a6/2b3a07d7afe0215ab5843a83809f",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/48/5d/e8c8deb5b42593c805fa9c181c0c"
|
||||
"build/prefab/lib/mac_arm64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/0e/3e/d730bb6f8cbd419a6f0bc9ec45b5",
|
||||
"build/prefab/lib/mac_arm64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ce/4d/33d705994f4f715b5ef86af3b10c",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/cc/21/6a8ba5a374899e96de6842b456dc",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e8/5c/d903596fd08f95bd89c76250e54c",
|
||||
"build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f9/25/6cbd4a35e2d75a61cdb379a7148b",
|
||||
"build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/aa/bd/f5747ffd8cfbef6970051b1714e1",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c9/4b/4da5368fe05606c3717a2fadfbaf",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/cb/44/14887341014ac31de1e8938ef309"
|
||||
}
|
||||
7
.idea/dictionaries/ericf.xml
generated
7
.idea/dictionaries/ericf.xml
generated
@ -306,6 +306,7 @@
|
||||
<w>cfgdir</w>
|
||||
<w>cfgkey</w>
|
||||
<w>cfgkeys</w>
|
||||
<w>cfgpath</w>
|
||||
<w>cfgs</w>
|
||||
<w>cfgui</w>
|
||||
<w>cflags</w>
|
||||
@ -483,6 +484,7 @@
|
||||
<w>dbapi</w>
|
||||
<w>dbase</w>
|
||||
<w>dbgsfx</w>
|
||||
<w>dbgstr</w>
|
||||
<w>dbpath</w>
|
||||
<w>dcls</w>
|
||||
<w>dcmake</w>
|
||||
@ -1055,6 +1057,7 @@
|
||||
<w>intex</w>
|
||||
<w>intp</w>
|
||||
<w>introspectable</w>
|
||||
<w>intstr</w>
|
||||
<w>iobj</w>
|
||||
<w>ipaddress</w>
|
||||
<w>ipos</w>
|
||||
@ -1418,6 +1421,8 @@
|
||||
<w>noinspect</w>
|
||||
<w>nondeterministic</w>
|
||||
<w>noninfringement</w>
|
||||
<w>noninteractive</w>
|
||||
<w>noninteractively</w>
|
||||
<w>nonmultipart</w>
|
||||
<w>noone</w>
|
||||
<w>norun</w>
|
||||
@ -1599,6 +1604,7 @@
|
||||
<w>posixsubprocess</w>
|
||||
<w>postinit</w>
|
||||
<w>postinited</w>
|
||||
<w>postrun</w>
|
||||
<w>poststr</w>
|
||||
<w>powerdown</w>
|
||||
<w>powersgiven</w>
|
||||
@ -1631,6 +1637,7 @@
|
||||
<w>preprocessing</w>
|
||||
<w>prereq</w>
|
||||
<w>prereqs</w>
|
||||
<w>prerun</w>
|
||||
<w>prevstate</w>
|
||||
<w>priceraw</w>
|
||||
<w>printcolors</w>
|
||||
|
||||
@ -10,7 +10,6 @@ import signal
|
||||
import subprocess
|
||||
import sys
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from threading import Lock, Thread, current_thread
|
||||
from typing import TYPE_CHECKING
|
||||
@ -33,11 +32,13 @@ if TYPE_CHECKING:
|
||||
from types import FrameType
|
||||
from bacommon.servermanager import ServerCommand
|
||||
|
||||
VERSION_STR = '1.1.2'
|
||||
VERSION_STR = '1.2'
|
||||
|
||||
# Version history:
|
||||
# 1.1.2:
|
||||
# Added args for setting config path and ba_root path
|
||||
# 1.2:
|
||||
# Added optional --help arg
|
||||
# Added --config arg for setting config path and --root for ba_root path
|
||||
# Added noninteractive mode and --interactive/--noninteractive to select
|
||||
# 1.1.1:
|
||||
# Switched config reading to use efro.dataclasses.dataclass_from_dict()
|
||||
# 1.1.0:
|
||||
@ -47,10 +48,6 @@ VERSION_STR = '1.1.2'
|
||||
# 1.0.0:
|
||||
# Initial release
|
||||
|
||||
# How many seconds we wait after asking our subprocess to do an immediate
|
||||
# shutdown before bringing down the hammer.
|
||||
IMMEDIATE_SHUTDOWN_TIME_LIMIT = 5.0
|
||||
|
||||
|
||||
class ServerManagerApp:
|
||||
"""An app which manages BallisticaCore server execution.
|
||||
@ -59,10 +56,18 @@ class ServerManagerApp:
|
||||
managing BallisticaCore operating in server mode.
|
||||
"""
|
||||
|
||||
def __init__(self, args: Args) -> None:
|
||||
self._config_path = args.config_path
|
||||
self._ba_root_path = (args.ba_root_path if args.ba_root_path
|
||||
is not None else os.path.abspath('dist/ba_root'))
|
||||
# How many seconds we wait after asking our subprocess to do an immediate
|
||||
# shutdown before bringing down the hammer.
|
||||
IMMEDIATE_SHUTDOWN_TIME_LIMIT = 5.0
|
||||
|
||||
def __init__(self) -> None:
|
||||
self._config_path = 'config.yaml'
|
||||
self._ba_root_path = os.path.abspath('dist/ba_root')
|
||||
self._interactive = sys.stdin.isatty()
|
||||
|
||||
# This may override the above defaults.
|
||||
self._parse_command_line_args()
|
||||
|
||||
try:
|
||||
self._config = self._load_config()
|
||||
except Exception as exc:
|
||||
@ -73,7 +78,7 @@ class ServerManagerApp:
|
||||
self._subprocess_commands_lock = Lock()
|
||||
self._subprocess_force_kill_time: Optional[float] = None
|
||||
self._restart_minutes: Optional[float] = None
|
||||
self._running_interactive = False
|
||||
self._running = False
|
||||
self._subprocess: Optional[subprocess.Popen[bytes]] = None
|
||||
self._launch_time = time.time()
|
||||
self._subprocess_launch_time: Optional[float] = None
|
||||
@ -109,26 +114,18 @@ class ServerManagerApp:
|
||||
"""
|
||||
return self._restart_minutes
|
||||
|
||||
def run_interactive(self) -> None:
|
||||
"""Run the app loop to completion."""
|
||||
import code
|
||||
def _prerun(self) -> None:
|
||||
"""Common code at the start of any run."""
|
||||
|
||||
if self._running_interactive:
|
||||
raise RuntimeError('Already running interactively.')
|
||||
self._running_interactive = True
|
||||
# Make sure we don't call run multiple times.
|
||||
if self._running:
|
||||
raise RuntimeError('Already running.')
|
||||
self._running = True
|
||||
|
||||
# Print basic usage info in interactive mode.
|
||||
if sys.stdin.isatty():
|
||||
if __debug__:
|
||||
modestr = '(debug mode)'
|
||||
else:
|
||||
modestr = '(opt mode)'
|
||||
print(f'{Clr.CYN}{Clr.BLD}BallisticaCore server'
|
||||
f' manager {VERSION_STR}'
|
||||
f' starting up {modestr}...{Clr.RST}\n'
|
||||
f'{Clr.CYN}Use the "mgr" object to make'
|
||||
f' live server adjustments.\n'
|
||||
f'Type "help(mgr)" for more information.{Clr.RST}')
|
||||
dbgstr = 'debug' if __debug__ else 'opt'
|
||||
intstr = 'interactive' if self._interactive else 'noninteractive'
|
||||
print(f'{Clr.CYN}{Clr.BLD}BallisticaCore server manager {VERSION_STR}'
|
||||
f' starting up ({dbgstr}/{intstr} mode)...{Clr.RST}')
|
||||
|
||||
# Python will handle SIGINT for us (as KeyboardInterrupt) but we
|
||||
# need to register a SIGTERM handler so we have a chance to clean
|
||||
@ -140,6 +137,56 @@ class ServerManagerApp:
|
||||
self._subprocess_thread = Thread(target=self._bg_thread_main)
|
||||
self._subprocess_thread.start()
|
||||
|
||||
# During a run, we make the assumption that cwd is the dir
|
||||
# containing this script, so make that so. Up until now that may
|
||||
# not be the case (we support being called from any location).
|
||||
os.chdir(os.path.abspath(os.path.dirname(__file__)))
|
||||
|
||||
def _postrun(self) -> None:
|
||||
"""Common code at the end of any run."""
|
||||
print(f'{Clr.CYN}Server manager shutting down...{Clr.RST}')
|
||||
|
||||
assert self._subprocess_thread is not None
|
||||
if self._subprocess_thread.is_alive():
|
||||
print(f'{Clr.CYN}Waiting for subprocess exit...{Clr.RST}')
|
||||
|
||||
# Mark ourselves as shutting down and wait for the process to wrap up.
|
||||
self._done = True
|
||||
self._subprocess_thread.join()
|
||||
|
||||
def run(self) -> None:
|
||||
"""Do the thing."""
|
||||
if self._interactive:
|
||||
self._run_interactive()
|
||||
else:
|
||||
self._run_noninteractive()
|
||||
|
||||
def _run_noninteractive(self) -> None:
|
||||
"""Run the app loop to completion noninteractively."""
|
||||
self._prerun()
|
||||
try:
|
||||
while True:
|
||||
time.sleep(1.234)
|
||||
except KeyboardInterrupt:
|
||||
# Gracefully bow out if we kill ourself via keyboard.
|
||||
pass
|
||||
except SystemExit:
|
||||
# We get this from the builtin quit(), our signal handler, etc.
|
||||
# Need to catch this so we can clean up, otherwise we'll be
|
||||
# left in limbo with our process thread still running.
|
||||
pass
|
||||
self._postrun()
|
||||
|
||||
def _run_interactive(self) -> None:
|
||||
"""Run the app loop to completion interactively."""
|
||||
import code
|
||||
|
||||
self._prerun()
|
||||
|
||||
# Print basic usage info for interactive mode.
|
||||
print(f'{Clr.CYN}Use the "mgr" object to interact with the server.\n'
|
||||
f'Type "help(mgr)" for more information.{Clr.RST}')
|
||||
|
||||
context = {'__name__': '__console__', '__doc__': None, 'mgr': self}
|
||||
|
||||
# Enable tab-completion if possible.
|
||||
@ -150,7 +197,7 @@ class ServerManagerApp:
|
||||
try:
|
||||
code.interact(local=context, banner='', exitmsg='')
|
||||
except SystemExit:
|
||||
# We get this from the builtin quit(), etc.
|
||||
# We get this from the builtin quit(), our signal handler, etc.
|
||||
# Need to catch this so we can clean up, otherwise we'll be
|
||||
# left in limbo with our process thread still running.
|
||||
pass
|
||||
@ -158,14 +205,7 @@ class ServerManagerApp:
|
||||
print(f'{Clr.SRED}Unexpected interpreter exception:'
|
||||
f' {exc} ({type(exc)}){Clr.RST}')
|
||||
|
||||
print(f'{Clr.CYN}Server manager shutting down...{Clr.RST}')
|
||||
|
||||
if self._subprocess_thread.is_alive():
|
||||
print(f'{Clr.CYN}Waiting for subprocess exit...{Clr.RST}')
|
||||
|
||||
# Mark ourselves as shutting down and wait for the process to wrap up.
|
||||
self._done = True
|
||||
self._subprocess_thread.join()
|
||||
self._postrun()
|
||||
|
||||
def cmd(self, statement: str) -> None:
|
||||
"""Exec a Python command on the current running server subprocess.
|
||||
@ -255,8 +295,8 @@ class ServerManagerApp:
|
||||
# If we're asking for an immediate restart but don't get one within
|
||||
# the grace period, bring down the hammer.
|
||||
if immediate:
|
||||
self._subprocess_force_kill_time = (time.time() +
|
||||
IMMEDIATE_SHUTDOWN_TIME_LIMIT)
|
||||
self._subprocess_force_kill_time = (
|
||||
time.time() + self.IMMEDIATE_SHUTDOWN_TIME_LIMIT)
|
||||
|
||||
def shutdown(self, immediate: bool = True) -> None:
|
||||
"""Shut down the server subprocess and exit the wrapper
|
||||
@ -276,16 +316,106 @@ class ServerManagerApp:
|
||||
# If we're asking for an immediate shutdown but don't get one within
|
||||
# the grace period, bring down the hammer.
|
||||
if immediate:
|
||||
self._subprocess_force_kill_time = (time.time() +
|
||||
IMMEDIATE_SHUTDOWN_TIME_LIMIT)
|
||||
self._subprocess_force_kill_time = (
|
||||
time.time() + self.IMMEDIATE_SHUTDOWN_TIME_LIMIT)
|
||||
|
||||
def _parse_command_line_args(self) -> None:
|
||||
"""Parse command line args."""
|
||||
|
||||
i = 1
|
||||
argc = len(sys.argv)
|
||||
did_set_interactive = False
|
||||
while i < argc:
|
||||
arg = sys.argv[i]
|
||||
if arg == '--help':
|
||||
self.print_help()
|
||||
sys.exit(0)
|
||||
elif arg == '--config':
|
||||
if i + 1 >= argc:
|
||||
raise CleanError('Expected a config path as next arg.')
|
||||
path = sys.argv[i + 1]
|
||||
if not os.path.exists(path):
|
||||
raise CleanError(
|
||||
f"Supplied path does not exist: '{path}'.")
|
||||
# We need an abs path because we may be in a different
|
||||
# cwd currently than we will be during the run.
|
||||
self._config_path = os.path.abspath(path)
|
||||
i += 2
|
||||
elif arg == '--root':
|
||||
if i + 1 >= argc:
|
||||
raise CleanError('Expected a path as next arg.')
|
||||
path = sys.argv[i + 1]
|
||||
# Unlike config_path, this one doesn't have to exist now.
|
||||
# We do however need an abs path because we may be in a
|
||||
# different cwd currently than we will be during the run.
|
||||
self._ba_root_path = os.path.abspath(path)
|
||||
i += 2
|
||||
elif arg == '--interactive':
|
||||
if did_set_interactive:
|
||||
raise CleanError('interactive/noninteractive can only'
|
||||
' be specified once.')
|
||||
self._interactive = True
|
||||
did_set_interactive = True
|
||||
i += 1
|
||||
elif arg == '--noninteractive':
|
||||
if did_set_interactive:
|
||||
raise CleanError('interactive/noninteractive can only'
|
||||
' be specified once.')
|
||||
self._interactive = False
|
||||
did_set_interactive = True
|
||||
i += 1
|
||||
else:
|
||||
raise CleanError(f"Invalid arg: '{arg}'.")
|
||||
|
||||
@classmethod
|
||||
def _par(cls, txt: str) -> str:
|
||||
import textwrap
|
||||
ind = ' ' * 2
|
||||
out = textwrap.fill(txt, 80, initial_indent=ind, subsequent_indent=ind)
|
||||
return f'{out}\n'
|
||||
|
||||
@classmethod
|
||||
def print_help(cls) -> None:
|
||||
"""Print app help."""
|
||||
filename = os.path.basename(__file__)
|
||||
out = (
|
||||
f'{Clr.BLD}{filename} usage:{Clr.RST}\n' + cls._par(
|
||||
'This script handles configuring, launching, re-launching,'
|
||||
' and otherwise managing BallisticaCore operating'
|
||||
' in server mode. It can be run with no arguments, but'
|
||||
' accepts the following optional ones:') + f'\n'
|
||||
f'{Clr.BLD}--help:{Clr.RST}\n'
|
||||
f' Show this help.\n'
|
||||
f'\n'
|
||||
f'{Clr.BLD}--config [path]{Clr.RST}\n' + cls._par(
|
||||
'Set the config file read by the server script. This should'
|
||||
' be in yaml format. Note that yaml is backwards compatible'
|
||||
' with json so you can just write json if you want to. If'
|
||||
' not specified, the script will look for a file named'
|
||||
' \'config.yaml\' in the same directory as the script.') + '\n'
|
||||
f'{Clr.BLD}--root [path]{Clr.RST}\n' + cls._par(
|
||||
'Set the ballistica root directory. This is where the game'
|
||||
' will read and write its caches, state files, downloaded'
|
||||
' assets, etc. It needs to be a writable directory. If not'
|
||||
' specified, the script will use the \'dist/ba_root\''
|
||||
' directory relative to itself.') + '\n'
|
||||
f'{Clr.BLD}--interactive{Clr.RST}\n'
|
||||
f'{Clr.BLD}--noninteractive{Clr.RST}\n' + cls._par(
|
||||
'Specify whether the script should run interactively.'
|
||||
' In interactive mode, the script creates a Python interpreter'
|
||||
' and reads commands from stdin, allowing for live interaction'
|
||||
' with the server. The server script will then exit when '
|
||||
'end-of-file is reached in stdin. Noninteractive mode creates'
|
||||
' no interpreter and is more suited to being run in automated'
|
||||
' scenarios. By default, interactive mode will be used if'
|
||||
' a terminal is detected and noninteractive mode otherwise.'))
|
||||
print(out)
|
||||
|
||||
def _load_config(self) -> ServerConfig:
|
||||
user_config_path = (self._config_path if self._config_path is not None
|
||||
else 'config.yaml')
|
||||
|
||||
if os.path.exists(user_config_path):
|
||||
if os.path.exists(self._config_path):
|
||||
import yaml
|
||||
with open(user_config_path) as infile:
|
||||
with open(self._config_path) as infile:
|
||||
user_config_raw = yaml.safe_load(infile.read())
|
||||
|
||||
# An empty config file will yield None, and that's ok.
|
||||
@ -293,7 +423,7 @@ class ServerManagerApp:
|
||||
return dataclass_from_dict(ServerConfig, user_config_raw)
|
||||
else:
|
||||
print(
|
||||
f"Warning: config file not found at '{user_config_path}'"
|
||||
f"Warning: config file not found at '{self._config_path}'"
|
||||
f'; will use default config.',
|
||||
file=sys.stderr,
|
||||
flush=True)
|
||||
@ -535,74 +665,10 @@ class ServerManagerApp:
|
||||
print(f'{Clr.CYN}Subprocess stopped.{Clr.RST}')
|
||||
|
||||
|
||||
def _parse_args() -> Optional[str]:
|
||||
"""Parse command line args; return optional config path."""
|
||||
if len(sys.argv) > 2:
|
||||
raise CleanError('Expected no more than 1 arg (config path)')
|
||||
if len(sys.argv) > 1:
|
||||
configpath = sys.argv[1]
|
||||
if not os.path.exists(configpath):
|
||||
raise CleanError(
|
||||
f"Supplied config path does not exist: '{configpath}'.")
|
||||
|
||||
# Note that we chdir before running the app so we need an abs path
|
||||
# to this to be safe.
|
||||
return os.path.abspath(configpath)
|
||||
return None
|
||||
|
||||
|
||||
@dataclass
|
||||
class Args:
|
||||
"""Wraps arguments that can be passed from the command line."""
|
||||
config_path: Optional[str] = None
|
||||
ba_root_path: Optional[str] = None
|
||||
|
||||
@classmethod
|
||||
def from_command_line(cls) -> Args:
|
||||
"""Parse command line args and fill ourself out."""
|
||||
args = Args()
|
||||
|
||||
i = 1
|
||||
argc = len(sys.argv)
|
||||
while i < argc:
|
||||
arg = sys.argv[i]
|
||||
if arg == '--config':
|
||||
if i + 1 >= argc:
|
||||
raise CleanError('Expected a config path as next arg.')
|
||||
path = sys.argv[i + 1]
|
||||
if not os.path.exists(path):
|
||||
raise CleanError(
|
||||
f"Supplied path does not exist: '{path}'.")
|
||||
args.config_path = os.path.abspath(path)
|
||||
i += 2
|
||||
elif arg == '--root':
|
||||
if i + 1 >= argc:
|
||||
raise CleanError('Expected a path as next arg.')
|
||||
path = sys.argv[i + 1]
|
||||
# Note: this one doesn't have to exist.
|
||||
args.ba_root_path = os.path.abspath(path)
|
||||
i += 2
|
||||
else:
|
||||
raise CleanError(f"Invalid arg: '{arg}'.")
|
||||
|
||||
return args
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Run a BallisticaCore server manager in interactive mode."""
|
||||
"""Run the BallisticaCore server manager."""
|
||||
try:
|
||||
# Note: we need to parse args before we chdir since we might be
|
||||
# dealing with relative paths.
|
||||
args = Args.from_command_line()
|
||||
|
||||
# ServerManager expects cwd to be the server dir (containing
|
||||
# dist/, config.yaml, etc.) Let's change our working directory to
|
||||
# the location of this file so we can run this script from anywhere
|
||||
# and it'll work.
|
||||
os.chdir(os.path.abspath(os.path.dirname(__file__)))
|
||||
|
||||
ServerManagerApp(args).run_interactive()
|
||||
|
||||
ServerManagerApp().run()
|
||||
except CleanError as exc:
|
||||
# For clean errors, do a simple print and fail; no tracebacks/etc.
|
||||
# Any others will bubble up and give us the usual mess.
|
||||
|
||||
@ -145,6 +145,7 @@
|
||||
<w>ccylinder</w>
|
||||
<w>centiseconds</w>
|
||||
<w>cfgdir</w>
|
||||
<w>cfgpath</w>
|
||||
<w>changeme</w>
|
||||
<w>charn</w>
|
||||
<w>charnum</w>
|
||||
@ -206,6 +207,7 @@
|
||||
<w>datas</w>
|
||||
<w>datav</w>
|
||||
<w>datavec</w>
|
||||
<w>dbgstr</w>
|
||||
<w>dbias</w>
|
||||
<w>dcol</w>
|
||||
<w>ddcaps</w>
|
||||
@ -452,6 +454,7 @@
|
||||
<w>intercollide</w>
|
||||
<w>internalformat</w>
|
||||
<w>interuptions</w>
|
||||
<w>intstr</w>
|
||||
<w>invote</w>
|
||||
<w>iobj</w>
|
||||
<w>iserverget</w>
|
||||
@ -608,6 +611,8 @@
|
||||
<w>nointhash</w>
|
||||
<w>nominmax</w>
|
||||
<w>noninfringement</w>
|
||||
<w>noninteractive</w>
|
||||
<w>noninteractively</w>
|
||||
<w>nonlint</w>
|
||||
<w>noone</w>
|
||||
<w>nothin</w>
|
||||
@ -686,6 +691,7 @@
|
||||
<w>positivey</w>
|
||||
<w>positivez</w>
|
||||
<w>postinit</w>
|
||||
<w>postrun</w>
|
||||
<w>powerup</w>
|
||||
<w>pptabcom</w>
|
||||
<w>precalc</w>
|
||||
@ -695,6 +701,7 @@
|
||||
<w>preloads</w>
|
||||
<w>premult</w>
|
||||
<w>prereq</w>
|
||||
<w>prerun</w>
|
||||
<w>printf</w>
|
||||
<w>printnodes</w>
|
||||
<w>printobjects</w>
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
|
||||
<h4><em>last updated on 2021-02-22 for Ballistica version 1.6.0 build 20280</em></h4>
|
||||
<h4><em>last updated on 2021-02-25 for Ballistica version 1.6.0 build 20306</em></h4>
|
||||
<p>This page documents the Python classes and functions in the 'ba' module,
|
||||
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
|
||||
<hr>
|
||||
|
||||
@ -21,7 +21,7 @@
|
||||
namespace ballistica {
|
||||
|
||||
// These are set automatically via script; don't change here.
|
||||
const int kAppBuildNumber = 20303;
|
||||
const int kAppBuildNumber = 20307;
|
||||
const char* kAppVersion = "1.6.0";
|
||||
|
||||
// Our standalone globals.
|
||||
|
||||
@ -94,21 +94,21 @@ class ServerConfig:
|
||||
# If present, the server manager will attempt to gracefully exit after
|
||||
# this amount of time. A graceful exit can occur at the end of a series
|
||||
# or other opportune time.
|
||||
# Servers with no exit times set will run indefinitely, though the
|
||||
# Servers with no exit conditions set will run indefinitely, though the
|
||||
# server binary will be restarted periodically to clear any memory
|
||||
# leaks or other bad state.
|
||||
clean_exit_minutes: Optional[float] = None
|
||||
|
||||
# If present, the server manager will shut down immediately after this
|
||||
# amount of time. This can be useful as a fallback for clean_exit_time.
|
||||
# Servers with no exit times set will run indefinitely, though the
|
||||
# Servers with no exit conditions set will run indefinitely, though the
|
||||
# server binary will be restarted periodically to clear any memory
|
||||
# leaks or other bad state.
|
||||
unclean_exit_minutes: Optional[float] = None
|
||||
|
||||
# If present, the server will shut down immediately if this amount of
|
||||
# time passes with no activity from any players.
|
||||
# Servers with no exit times set will run indefinitely, though the
|
||||
# Servers with no exit conditions set will run indefinitely, though the
|
||||
# server binary will be restarted periodically to clear any memory
|
||||
# leaks or other bad state.
|
||||
idle_exit_minutes: Optional[float] = None
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user