Skip to content

Commit 3c42d1c

Browse files
Merge pull request #39 from RLBot/launch_server
Look for RLBotServer in cwd or %localappdata% if not specified
2 parents 8537bfa + 561eb83 commit 3c42d1c

File tree

7 files changed

+111
-70
lines changed

7 files changed

+111
-70
lines changed

rlbot/managers/match.py

Lines changed: 70 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import os
2+
import stat
13
from pathlib import Path
24
from time import sleep
35

@@ -7,7 +9,7 @@
79
from rlbot.interface import RLBOT_SERVER_IP, RLBOT_SERVER_PORT, SocketRelay
810
from rlbot.utils import fill_desired_game_state, gateway
911
from rlbot.utils.logging import DEFAULT_LOGGER
10-
from rlbot.utils.os_detector import MAIN_EXECUTABLE_NAME
12+
from rlbot.utils.os_detector import CURRENT_OS, OS, RLBOT_SERVER_NAME
1113

1214

1315
class MatchManager:
@@ -21,13 +23,16 @@ class MatchManager:
2123
rlbot_server_port = RLBOT_SERVER_PORT
2224
initialized = False
2325

24-
def __init__(
25-
self,
26-
main_executable_path: Path | None = None,
27-
main_executable_name: str = MAIN_EXECUTABLE_NAME,
28-
):
29-
self.main_executable_path = main_executable_path
30-
self.main_executable_name = main_executable_name
26+
def __init__(self, rlbot_server_path: Path | None = None):
27+
"""
28+
Initialize a MatchManager.
29+
Args:
30+
rlbot_server_path: The path to the RLBotServer executable. The path is used to launch the server
31+
if it is not already running. If set to None, the RLBotServer in the current working directory will
32+
be used, and otherwise the globally installed RLBotServer will be used.
33+
"""
34+
35+
self.rlbot_server_path = rlbot_server_path
3136

3237
self.rlbot_interface: SocketRelay = SocketRelay("")
3338
self.rlbot_interface.packet_handlers.append(self._packet_reporter)
@@ -41,27 +46,75 @@ def __exit__(self, exc_type, exc_val, exc_tb) -> None:
4146
def ensure_server_started(self):
4247
"""
4348
Ensures that RLBotServer is running, starting it if it is not.
49+
If no path to an RLBotServer executable was passed during initialization of the MatchManager,
50+
the function will use the RLBotServer executable in the current working directory, if any, or
51+
otherwise the global installed RLBotServer will be used, if any.
4452
"""
4553

54+
exe_name = (
55+
self.rlbot_server_path.stem
56+
if self.rlbot_server_path is not None and self.rlbot_server_path.is_file()
57+
else RLBOT_SERVER_NAME
58+
)
4659
self.rlbot_server_process, self.rlbot_server_port = gateway.find_server_process(
47-
self.main_executable_name
60+
exe_name
4861
)
4962
if self.rlbot_server_process is not None:
50-
self.logger.info("Already have %s running!", self.main_executable_name)
63+
self.logger.info("%s is already running!", exe_name)
5164
return
5265

53-
if self.main_executable_path is None:
54-
self.main_executable_path = Path.cwd()
66+
if self.rlbot_server_path is None:
67+
# Look in cwd or localappdata
68+
path = Path.cwd() / RLBOT_SERVER_NAME
69+
if not path.exists() and CURRENT_OS == OS.WINDOWS:
70+
self.logger.debug(
71+
f"Could not find RLBotServer in cwd ('{path.parent}'), trying %localappdata% instead."
72+
)
73+
path = (
74+
Path(os.environ.get("LOCALAPPDATA"))
75+
/ "RLBot5"
76+
/ "bin"
77+
/ RLBOT_SERVER_NAME
78+
)
79+
if not path.exists():
80+
raise FileNotFoundError(
81+
"Unable to find RLBotServer in the current working directory "
82+
"or in the default installation location. "
83+
"Is your antivirus messing you up? Check "
84+
"https://github.com/RLBot/RLBot/wiki/Antivirus-Notes."
85+
)
86+
else:
87+
# User specified path
88+
path = self.rlbot_server_path
89+
if path.exists() and path.is_dir():
90+
path = path / RLBOT_SERVER_NAME
91+
if not path.exists():
92+
raise FileNotFoundError(
93+
f"Unable to find RLBotServer at the specified path '{path}'."
94+
)
95+
96+
if path is None or not os.access(path, os.F_OK):
97+
raise FileNotFoundError(
98+
f"Unable to find RLBotServer at '{path}'. "
99+
"Is your antivirus messing you up? Check "
100+
"https://github.com/RLBot/RLBot/wiki/Antivirus-Notes."
101+
)
102+
103+
if not os.access(path, os.X_OK):
104+
os.chmod(path, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
55105

56-
rlbot_server_process, self.rlbot_server_port = gateway.launch(
57-
self.main_executable_path,
58-
self.main_executable_name,
59-
)
106+
if not os.access(path, os.X_OK):
107+
raise PermissionError(
108+
"Unable to execute RLBotServer due to file permissions! Is your antivirus messing you up? "
109+
f"Check https://github.com/RLBot/RLBot/wiki/Antivirus-Notes. The exact path is '{path}'"
110+
)
111+
112+
rlbot_server_process, self.rlbot_server_port = gateway.launch(path)
60113
self.rlbot_server_process = psutil.Process(rlbot_server_process.pid)
61114

62115
self.logger.info(
63116
"Started %s with process id %s",
64-
self.main_executable_name,
117+
path,
65118
self.rlbot_server_process.pid,
66119
)
67120

rlbot/utils/gateway.py

Lines changed: 19 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
import os
21
import socket
3-
import stat
42
import subprocess
53
from pathlib import Path
64

@@ -14,21 +12,21 @@
1412
import shlex
1513

1614

17-
def find_main_executable_path(
18-
main_executable_path: Path, main_executable_name: str
19-
) -> tuple[Path, Path | None]:
20-
main_executable_path = main_executable_path.absolute().resolve()
15+
def find_file(base_dir: Path, file_name: str) -> Path | None:
16+
"""
17+
Looks for a file called `file_name` in the given `base_dir` directory and its subdirectories.
18+
Returns the path to the file, or None if it was not found.
19+
"""
2120

22-
# check if the path is directly to the main executable
23-
if main_executable_path.is_file():
24-
return main_executable_path.parent, main_executable_path
21+
base_dir = base_dir.absolute().resolve()
22+
assert base_dir.exists() and base_dir.is_dir(), f"'{base_dir}' is not a directory!"
2523

26-
# search subdirectories for the main executable
27-
for path in main_executable_path.glob(f"**/{main_executable_name}"):
24+
# Search subdirectories for the file
25+
for path in base_dir.glob(f"**/{file_name}"):
2826
if path.is_file():
29-
return path.parent, path
27+
return path
3028

31-
return main_executable_path, None
29+
return None
3230

3331

3432
def is_port_accessible(port: int):
@@ -51,47 +49,25 @@ def find_open_server_port() -> int:
5149
)
5250

5351

54-
def launch(
55-
main_executable_path: Path, main_executable_name: str
56-
) -> tuple[subprocess.Popen[bytes], int]:
57-
directory, path = find_main_executable_path(
58-
main_executable_path, main_executable_name
59-
)
60-
61-
if path is None or not os.access(path, os.F_OK):
62-
raise FileNotFoundError(
63-
f"Unable to find RLBotServer at '{main_executable_path}'. "
64-
"Is your antivirus messing you up? Check "
65-
"https://github.com/RLBot/RLBot/wiki/Antivirus-Notes."
66-
)
67-
68-
if not os.access(path, os.X_OK):
69-
os.chmod(path, stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
70-
71-
if not os.access(path, os.X_OK):
72-
raise PermissionError(
73-
"Unable to execute RLBotServer due to file permissions! Is your antivirus messing you up? "
74-
f"Check https://github.com/RLBot/RLBot/wiki/Antivirus-Notes. The exact path is {path}"
75-
)
76-
52+
def launch(exe_path: Path) -> tuple[subprocess.Popen[bytes], int]:
7753
port = find_open_server_port()
7854

7955
if CURRENT_OS == "Windows":
80-
args = [str(path), str(port)]
56+
args = [str(exe_path), str(port)]
8157
else:
82-
args = f"{shlex.quote(path.as_posix())} {port}" # on Unix, when shell=True, args must be a string for flags to reach the executable
83-
DEFAULT_LOGGER.info("Launching RLBotServer with via %s", args)
58+
args = f"{shlex.quote(exe_path.as_posix())} {port}" # on Unix, when shell=True, args must be a string for flags to reach the executable
59+
DEFAULT_LOGGER.info("Launching RLBotServer via %s", args)
8460

85-
return subprocess.Popen(args, shell=True, cwd=directory), port
61+
return subprocess.Popen(args, shell=True, cwd=exe_path.parent), port
8662

8763

8864
def find_server_process(
89-
main_executable_name: str,
65+
exe_name: str,
9066
) -> tuple[psutil.Process | None, int]:
9167
logger = DEFAULT_LOGGER
9268
for proc in psutil.process_iter():
9369
try:
94-
if proc.name() != main_executable_name:
70+
if proc.name() != exe_name:
9571
continue
9672

9773
args = proc.cmdline()
@@ -106,7 +82,7 @@ def find_server_process(
10682
except Exception as e:
10783
logger.error(
10884
"Failed to read the name of a process while hunting for %s: %s",
109-
main_executable_name,
85+
exe_name,
11086
e,
11187
)
11288

rlbot/utils/os_detector.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ class OS(IntEnum):
1010

1111
match platform.system():
1212
case "Windows":
13-
MAIN_EXECUTABLE_NAME = "RLBotServer.exe"
13+
RLBOT_SERVER_NAME = "RLBotServer.exe"
1414
CURRENT_OS = OS.WINDOWS
1515
case "Linux":
16-
MAIN_EXECUTABLE_NAME = "RLBotServer"
16+
RLBOT_SERVER_NAME = "RLBotServer"
1717
CURRENT_OS = OS.LINUX
1818
case _ as unknown_os:
1919
from rlbot.utils.logging import get_logger
2020

21-
MAIN_EXECUTABLE_NAME = ""
21+
RLBOT_SERVER_NAME = ""
2222
CURRENT_OS = OS.UNKNOWN
2323

2424
get_logger("os_detector").warning("Unknown OS: %s", unknown_os)

rlbot/version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "2.0.0-beta.51"
1+
__version__ = "2.0.0-beta.52"

tests/many_match.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,15 @@
44
from rlbot import flat
55
from rlbot.config import load_player_config
66
from rlbot.managers import MatchManager
7+
from rlbot.utils.gateway import find_file
8+
from rlbot.utils.os_detector import RLBOT_SERVER_NAME
79

810
DIR = Path(__file__).parent
911

1012
BOT_PATH = DIR / "atba/atba.bot.toml"
11-
RLBOT_SERVER_FOLDER = DIR / "../../core/RLBotCS/bin/Release/"
13+
RLBOT_SERVER_PATH = find_file(
14+
DIR / "../../core/RLBotCS/bin/Release/", RLBOT_SERVER_NAME
15+
)
1216

1317
num_comms = set()
1418

@@ -20,7 +24,7 @@ def handle_match_comm(comm: flat.MatchComm):
2024

2125

2226
if __name__ == "__main__":
23-
match_manager = MatchManager(RLBOT_SERVER_FOLDER)
27+
match_manager = MatchManager(RLBOT_SERVER_PATH)
2428
match_manager.rlbot_interface.match_comm_handlers.append(handle_match_comm)
2529
match_manager.ensure_server_started()
2630
match_manager.connect_and_run(

tests/run_forever.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,19 @@
44
from rlbot import flat
55
from rlbot.config import load_player_config
66
from rlbot.managers import MatchManager
7+
from rlbot.utils.gateway import find_file
78
from rlbot.utils.maps import GAME_MAP_TO_UPK, STANDARD_MAPS
9+
from rlbot.utils.os_detector import RLBOT_SERVER_NAME
810

911
DIR = Path(__file__).parent
1012

1113
BOT_PATH = DIR / "atba/atba.bot.toml"
12-
RLBOT_SERVER_FOLDER = DIR / "../../core/RLBotCS/bin/Release/"
14+
RLBOT_SERVER_PATH = find_file(
15+
DIR / "../../core/RLBotCS/bin/Release/", RLBOT_SERVER_NAME
16+
)
1317

1418
if __name__ == "__main__":
15-
match_manager = MatchManager(RLBOT_SERVER_FOLDER)
19+
match_manager = MatchManager(RLBOT_SERVER_PATH)
1620

1721
current_map = -1
1822

tests/run_match.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,18 @@
33

44
from rlbot import flat
55
from rlbot.managers import MatchManager
6+
from rlbot.utils.gateway import find_file
7+
from rlbot.utils.os_detector import RLBOT_SERVER_NAME
68

79
DIR = Path(__file__).parent
810

911
MATCH_CONFIG_PATH = DIR / "human_vs_atba.toml"
10-
RLBOT_SERVER_FOLDER = DIR / "../../core/RLBotCS/bin/Release/"
12+
RLBOT_SERVER_PATH = find_file(
13+
DIR / "../../core/RLBotCS/bin/Release/", RLBOT_SERVER_NAME
14+
)
1115

1216
if __name__ == "__main__":
13-
with MatchManager(RLBOT_SERVER_FOLDER) as man:
17+
with MatchManager(RLBOT_SERVER_PATH) as man:
1418
man.start_match(MATCH_CONFIG_PATH)
1519
assert man.packet is not None
1620

0 commit comments

Comments
 (0)