Skip to content

Commit 9cc0407

Browse files
committedFeb 21, 2025
Merge branch 'master' into config_inconsistencies
2 parents 154904d + afd114b commit 9cc0407

File tree

7 files changed

+174
-124
lines changed

7 files changed

+174
-124
lines changed
 

‎rlbot/config.py

+146-103
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,62 @@
77
from rlbot.utils.os_detector import CURRENT_OS, OS
88

99

10-
def __parse_enum(table: dict, key: str, enum: Any, default: int = 0) -> Any:
10+
class ConfigParsingException(Exception):
11+
pass
12+
13+
14+
def __enum(table: dict, key: str, enum: Any, default: int = 0) -> Any:
1115
if key not in table:
12-
return enum(0)
16+
return enum(default)
1317
try:
1418
for i in range(100000):
1519
if str(enum(i)).split('.')[-1].lower() == table[key].lower():
1620
return enum(i)
1721
except ValueError:
18-
logger.warning(f"Unknown value '{table[key]}' for key '{key}' using default ({enum(default)})")
19-
return enum(default)
22+
raise ConfigParsingException(f"Invalid value {repr(table[key])} for key '{key}'.")
23+
24+
25+
def __str(table: dict, key: str, default: str = "") -> str:
26+
v = table.get(key, default)
27+
if isinstance(v, str):
28+
return v
29+
raise ConfigParsingException(f"'{key}' has value {repr(v)}. Expected a string.")
30+
31+
32+
def __bool(table: dict, key: str, default: bool = False) -> bool:
33+
v = table.get(key, default)
34+
if isinstance(v, bool):
35+
return v
36+
raise ConfigParsingException(f"'{key}' has value {repr(v)}. Expected a bool.")
37+
38+
39+
def __int(table: dict, key: str, default: int = 0) -> int:
40+
v = table.get(key, default)
41+
if isinstance(v, int):
42+
return v
43+
raise ConfigParsingException(f"'{key}' has value {repr(v)}. Expected an int.")
44+
45+
46+
def __table(table: dict, key: str) -> dict:
47+
v = table.get(key, dict())
48+
if isinstance(v, dict):
49+
return v
50+
raise ConfigParsingException(f"'{key}' has value {repr(v)}. Expected a table.")
51+
52+
53+
def __team(table: dict) -> int:
54+
if 'team' not in table:
55+
return 0
56+
v = table['team']
57+
if isinstance(v, str):
58+
if v.lower() == "blue":
59+
return 0
60+
if v.lower() == "orange":
61+
return 1
62+
if isinstance(v, int):
63+
if 0 <= v <= 1:
64+
return v
65+
raise ConfigParsingException(f"'team' has value {repr(v)}. Expected a 0, 1, \"blue\", or \"orange\".")
2066

2167

2268
def load_match_config(config_path: Path | str) -> flat.MatchConfiguration:
@@ -27,90 +73,85 @@ def load_match_config(config_path: Path | str) -> flat.MatchConfiguration:
2773
with open(config_path, "rb") as f:
2874
config = tomllib.load(f)
2975

30-
rlbot_table = config.get("rlbot", dict())
31-
match_table = config.get("match", dict())
32-
mutator_table = config.get("mutators", dict())
76+
rlbot_table = __table(config, "rlbot")
77+
match_table = __table(config, "match")
78+
mutator_table = __table(config, "mutators")
3379

3480
players = []
3581
for car_table in config.get("cars", []):
36-
car_config = car_table.get("config_file")
37-
name = car_table.get("name", "")
38-
team = car_table.get("team", 0)
39-
try:
40-
team = int(team)
41-
except ValueError:
42-
team = {"blue": 0, "orange": 1}.get(team.lower())
43-
if team is None or team not in [0, 1]:
44-
logger.warning(f"Unknown team '{car_table.get("team")}' for player {len(players)}, using default team 0")
45-
46-
loadout_file = car_table.get("loadout_file")
47-
variant = car_table.get("type", "rlbot")
48-
skill = __parse_enum(car_table, "skill", flat.PsyonixSkill, int(flat.PsyonixSkill.AllStar))
82+
car_config = __str(car_table, "config_file")
83+
name = __str(car_table, "name")
84+
team = __team(car_table)
85+
loadout_file = __str(car_table, "loadout_file") or None
86+
skill = __enum(car_table, "skill", flat.PsyonixSkill, int(flat.PsyonixSkill.AllStar))
87+
variant = __str(car_table, "type", "rlbot").lower()
88+
4989
match variant:
5090
case "rlbot":
51-
if car_config is None:
52-
loadout = load_player_loadout(loadout_file, team) if loadout_file else None
53-
players.append(flat.PlayerConfiguration(flat.CustomBot(), name, team, loadout=loadout))
54-
else:
55-
abs_config_path = (config_path.parent / car_config).resolve()
56-
players.append(load_player_config(abs_config_path, flat.CustomBot(), team, name, loadout_file))
91+
variety, use_config = flat.CustomBot(), True
5792
case "psyonix":
58-
if car_config is None:
59-
loadout = load_player_loadout(loadout_file, team) if loadout_file else None
60-
players.append(flat.PlayerConfiguration(flat.Psyonix(skill), name, team, loadout=loadout))
61-
else:
62-
abs_config_path = (config_path.parent / car_config).resolve()
63-
players.append(load_player_config(abs_config_path, flat.Psyonix(skill), team, name, loadout_file))
93+
variety, use_config = flat.Psyonix(skill), True
6494
case "human":
65-
loadout = load_player_loadout(loadout_file, team) if loadout_file else None
66-
players.append(flat.PlayerConfiguration(flat.Human(), name, team, loadout=loadout))
95+
variety, use_config = flat.Human(), False
96+
case "partymember":
97+
logger.warning("PartyMember player type is not supported yet.")
98+
variety, use_config = flat.PartyMember, False
99+
case t:
100+
raise ConfigParsingException(f"Invalid player type {repr(t)} for player {len(players)}.")
101+
102+
if use_config and car_config:
103+
abs_config_path = (config_path.parent / car_config).resolve()
104+
players.append(load_player_config(abs_config_path, variety, team, name, loadout_file))
105+
else:
106+
loadout = load_player_loadout(loadout_file, team) if loadout_file else None
107+
players.append(flat.PlayerConfiguration(variety, name, team, loadout=loadout))
67108

68109
scripts = []
69110
for script_table in config.get("scripts", []):
70-
if script_config := script_table.get("config_file"):
111+
if script_config := __str(script_table, "config_file"):
71112
abs_config_path = (config_path.parent / script_config).resolve()
72113
scripts.append(load_script_config(abs_config_path))
73114
else:
74115
scripts.append(flat.ScriptConfiguration())
75116

76117
mutators = flat.MutatorSettings(
77-
match_length=__parse_enum(mutator_table, "match_length", flat.MatchLengthMutator),
78-
max_score=__parse_enum(mutator_table, "max_score", flat.MaxScoreMutator),
79-
multi_ball=__parse_enum(mutator_table, "multi_ball", flat.MultiBallMutator),
80-
overtime=__parse_enum(mutator_table, "overtime", flat.OvertimeMutator),
81-
series_length=__parse_enum(mutator_table, "series_length", flat.SeriesLengthMutator),
82-
game_speed=__parse_enum(mutator_table, "game_speed", flat.GameSpeedMutator),
83-
ball_max_speed=__parse_enum(mutator_table, "ball_max_speed", flat.BallMaxSpeedMutator),
84-
ball_type=__parse_enum(mutator_table, "ball_type", flat.BallTypeMutator),
85-
ball_weight=__parse_enum(mutator_table, "ball_weight", flat.BallWeightMutator),
86-
ball_size=__parse_enum(mutator_table, "ball_size", flat.BallSizeMutator),
87-
ball_bounciness=__parse_enum(mutator_table, "ball_bounciness", flat.BallBouncinessMutator),
88-
boost_amount=__parse_enum(mutator_table, "boost_amount", flat.BoostMutator),
89-
rumble=__parse_enum(mutator_table, "rumble", flat.RumbleMutator),
90-
boost_strength=__parse_enum(mutator_table, "boost_strength", flat.BoostStrengthMutator),
91-
gravity=__parse_enum(mutator_table, "gravity", flat.GravityMutator),
92-
demolish=__parse_enum(mutator_table, "demolish", flat.DemolishMutator),
93-
respawn_time=__parse_enum(mutator_table, "respawn_time", flat.RespawnTimeMutator),
94-
max_time=__parse_enum(mutator_table, "max_time", flat.MaxTimeMutator),
95-
game_event=__parse_enum(mutator_table, "game_event", flat.GameEventMutator),
96-
audio=__parse_enum(mutator_table, "audio", flat.AudioMutator),
118+
match_length=__enum(mutator_table, "match_length", flat.MatchLengthMutator),
119+
max_score=__enum(mutator_table, "max_score", flat.MaxScoreMutator),
120+
multi_ball=__enum(mutator_table, "multi_ball", flat.MultiBallMutator),
121+
overtime=__enum(mutator_table, "overtime", flat.OvertimeMutator),
122+
series_length=__enum(mutator_table, "series_length", flat.SeriesLengthMutator),
123+
game_speed=__enum(mutator_table, "game_speed", flat.GameSpeedMutator),
124+
ball_max_speed=__enum(mutator_table, "ball_max_speed", flat.BallMaxSpeedMutator),
125+
ball_type=__enum(mutator_table, "ball_type", flat.BallTypeMutator),
126+
ball_weight=__enum(mutator_table, "ball_weight", flat.BallWeightMutator),
127+
ball_size=__enum(mutator_table, "ball_size", flat.BallSizeMutator),
128+
ball_bounciness=__enum(mutator_table, "ball_bounciness", flat.BallBouncinessMutator),
129+
boost_amount=__enum(mutator_table, "boost_amount", flat.BoostAmountMutator),
130+
rumble=__enum(mutator_table, "rumble", flat.RumbleMutator),
131+
boost_strength=__enum(mutator_table, "boost_strength", flat.BoostStrengthMutator),
132+
gravity=__enum(mutator_table, "gravity", flat.GravityMutator),
133+
demolish=__enum(mutator_table, "demolish", flat.DemolishMutator),
134+
respawn_time=__enum(mutator_table, "respawn_time", flat.RespawnTimeMutator),
135+
max_time=__enum(mutator_table, "max_time", flat.MaxTimeMutator),
136+
game_event=__enum(mutator_table, "game_event", flat.GameEventMutator),
137+
audio=__enum(mutator_table, "audio", flat.AudioMutator),
97138
)
98139

99140
return flat.MatchConfiguration(
100-
launcher=__parse_enum(rlbot_table, "launcher", flat.Launcher),
101-
launcher_arg=rlbot_table.get("launcher_arg", ""),
102-
auto_start_bots=rlbot_table.get("auto_start_bots", True),
103-
game_map_upk=match_table.get("game_map_upk", ""),
141+
launcher=__enum(rlbot_table, "launcher", flat.Launcher),
142+
launcher_arg=__str(rlbot_table, "launcher_arg"),
143+
auto_start_bots=__bool(rlbot_table, "auto_start_bots", True),
144+
game_map_upk=__str(match_table, "game_map_upk"),
104145
player_configurations=players,
105146
script_configurations=scripts,
106-
game_mode=__parse_enum(match_table, "game_mode", flat.GameMode),
107-
skip_replays=match_table.get("skip_replays", False),
108-
instant_start=match_table.get("instant_start", False),
147+
game_mode=__enum(match_table, "game_mode", flat.GameMode),
148+
skip_replays=__bool(match_table, "skip_replays"),
149+
instant_start=__bool(match_table, "instant_start"),
109150
mutators=mutators,
110-
existing_match_behavior=__parse_enum(match_table, "existing_match_behavior", flat.ExistingMatchBehavior),
111-
enable_rendering=match_table.get("enable_rendering", False),
112-
enable_state_setting=match_table.get("enable_state_setting", False),
113-
freeplay=match_table.get("freeplay", False),
151+
existing_match_behavior=__enum(match_table, "existing_match_behavior", flat.ExistingMatchBehavior),
152+
enable_rendering=__bool(match_table, "enable_rendering"),
153+
enable_state_setting=__bool(match_table, "enable_state_setting"),
154+
freeplay=__bool(match_table, "freeplay"),
114155
)
115156

116157

@@ -121,34 +162,35 @@ def load_player_loadout(path: Path | str, team: int) -> flat.PlayerLoadout:
121162
with open(path, "rb") as f:
122163
config = tomllib.load(f)
123164

124-
loadout = config["blue_loadout"] if team == 0 else config["orange_loadout"]
165+
table_name = "blue_loadout" if team == 0 else "orange_loadout"
166+
loadout = __table(config, table_name)
125167
paint = None
126-
if paint_table := loadout.get("paint", None):
168+
if paint_table := __table(loadout, "paint"):
127169
paint = flat.LoadoutPaint(
128-
car_paint_id=paint_table.get("car_paint_id", 0),
129-
decal_paint_id=paint_table.get("decal_paint_id", 0),
130-
wheels_paint_id=paint_table.get("wheels_paint_id", 0),
131-
boost_paint_id=paint_table.get("boost_paint_id", 0),
132-
antenna_paint_id=paint_table.get("antenna_paint_id", 0),
133-
hat_paint_id=paint_table.get("hat_paint_id", 0),
134-
trails_paint_id=paint_table.get("trails_paint_id", 0),
135-
goal_explosion_paint_id=paint_table.get("goal_explosion_paint_id", 0),
170+
car_paint_id=__int(paint_table, "car_paint_id"),
171+
decal_paint_id=__int(paint_table, "decal_paint_id"),
172+
wheels_paint_id=__int(paint_table, "wheels_paint_id"),
173+
boost_paint_id=__int(paint_table, "boost_paint_id"),
174+
antenna_paint_id=__int(paint_table, "antenna_paint_id"),
175+
hat_paint_id=__int(paint_table, "hat_paint_id"),
176+
trails_paint_id=__int(paint_table, "trails_paint_id"),
177+
goal_explosion_paint_id=__int(paint_table, "goal_explosion_paint_id"),
136178
)
137179

138180
return flat.PlayerLoadout(
139-
team_color_id=loadout.get("team_color_id", 0),
140-
custom_color_id=loadout.get("custom_color_id", 0),
141-
car_id=loadout.get("car_id", 0),
142-
decal_id=loadout.get("decal_id", 0),
143-
wheels_id=loadout.get("wheels_id", 0),
144-
boost_id=loadout.get("boost_id", 0),
145-
antenna_id=loadout.get("antenna_id", 0),
146-
hat_id=loadout.get("hat_id", 0),
147-
paint_finish_id=loadout.get("paint_finish_id", 0),
148-
custom_finish_id=loadout.get("custom_finish_id", 0),
149-
engine_audio_id=loadout.get("engine_audio_id", 0),
150-
trails_id=loadout.get("trails_id", 0),
151-
goal_explosion_id=loadout.get("goal_explosion_id", 0),
181+
team_color_id=__int(loadout, "team_color_id"),
182+
custom_color_id=__int(loadout, "custom_color_id"),
183+
car_id=__int(loadout, "car_id"),
184+
decal_id=__int(loadout, "decal_id"),
185+
wheels_id=__int(loadout, "wheels_id"),
186+
boost_id=__int(loadout, "boost_id"),
187+
antenna_id=__int(loadout, "antenna_id"),
188+
hat_id=__int(loadout, "hat_id"),
189+
paint_finish_id=__int(loadout, "paint_finish_id"),
190+
custom_finish_id=__int(loadout, "custom_finish_id"),
191+
engine_audio_id=__int(loadout, "engine_audio_id"),
192+
trails_id=__int(loadout, "trails_id"),
193+
goal_explosion_id=__int(loadout, "goal_explosion_id"),
152194
loadout_paint=paint,
153195
)
154196

@@ -165,29 +207,30 @@ def load_player_config(
165207
with open(path, "rb") as f:
166208
config = tomllib.load(f)
167209

168-
settings: dict[str, Any] = config["settings"]
210+
settings = __table(config, "settings")
169211

170212
root_dir = path.parent.absolute()
171213
if "root_dir" in settings:
172-
root_dir /= Path(settings["root_dir"])
214+
root_dir /= Path(__str(settings, "root_dir"))
173215

174-
run_command = settings.get("run_command", "")
216+
run_command = __str(settings, "run_command")
175217
if CURRENT_OS == OS.LINUX and "run_command_linux" in settings:
176-
run_command = settings["run_command_linux"]
218+
run_command = __str(settings, "run_command_linux")
177219

178-
loadout_path = path.parent / Path(settings["loadout_file"]) if "loadout_file" in settings else loadout_override
220+
loadout_path = path.parent / Path(__str(settings, "loadout_file")) if "loadout_file" in settings else None
221+
loadout_path = loadout_override or loadout_path
179222
loadout = load_player_loadout(loadout_path, team) if loadout_path is not None else None
180223

181224
return flat.PlayerConfiguration(
182225
type,
183-
settings.get("name", name_override or "Unnamed"),
226+
name_override or __str(settings, "name"),
184227
team,
185228
str(root_dir),
186-
str(run_command),
229+
run_command,
187230
loadout,
188231
0,
189-
settings.get("agent_id", ""),
190-
settings.get("hivemind", False),
232+
__str(settings, "agent_id"),
233+
__bool(settings, "hivemind"),
191234
)
192235

193236

@@ -203,16 +246,16 @@ def load_script_config(path: Path | str) -> flat.ScriptConfiguration:
203246

204247
root_dir = path.parent
205248
if "root_dir" in settings:
206-
root_dir /= Path(settings["root_dir"])
249+
root_dir /= Path(__str(settings, "root_dir"))
207250

208-
run_command = settings.get("run_command", "")
251+
run_command = __str(settings, "run_command")
209252
if CURRENT_OS == OS.LINUX and "run_command_linux" in settings:
210-
run_command = settings["run_command_linux"]
253+
run_command = __str(settings, "run_command_linux")
211254

212255
return flat.ScriptConfiguration(
213-
settings.get("name", "Unnamed"),
256+
__str(settings, "name"),
214257
str(root_dir),
215258
run_command,
216259
0,
217-
settings.get("agent_id", ""),
260+
__str(settings, "agent_id"),
218261
)

‎rlbot/managers/match.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ def start_match(
122122

123123
if wait_for_start:
124124
self.wait_for_first_packet()
125-
self.logger.info("Match has started.")
125+
self.logger.debug("First packet received")
126126

127127
def disconnect(self):
128128
"""

‎rlbot/version.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
__version__ = "2.0.0-beta.24"
1+
__version__ = "2.0.0-beta.25"

‎tests/atba/atba.py

+23-17
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class Atba(Bot):
5757

5858
last_demoed = False
5959
needs_render = True
60+
next_teleport = 10
6061

6162
last_send = 0
6263
controller = flat.ControllerState()
@@ -125,30 +126,30 @@ def get_output(self, packet: flat.GamePacket) -> flat.ControllerState:
125126
steer_correction_radians = car_direction.correction_to(car_to_ball)
126127

127128
self.controller.steer = -steer_correction_radians
128-
self.controller.throttle = 1
129+
self.controller.throttle = 1.0
129130

130131
return self.controller
131132

132133
def test_state_setting(self, packet: flat.GamePacket):
133-
self.set_game_state(
134-
{
135-
0: flat.DesiredBallState(
136-
flat.DesiredPhysics(
137-
velocity=flat.Vector3Partial(
138-
z=packet.balls[0].physics.velocity.z + 10
134+
if packet.match_info.seconds_elapsed > self.next_teleport:
135+
self.next_teleport = packet.match_info.seconds_elapsed + 10
136+
self.set_game_state(
137+
{
138+
0: flat.DesiredBallState(
139+
flat.DesiredPhysics(
140+
location=flat.Vector3Partial(0, 0, 100),
139141
)
140142
)
141-
)
142-
},
143-
{
144-
i: flat.DesiredCarState(
145-
flat.DesiredPhysics(
146-
velocity=flat.Vector3Partial(z=car.physics.velocity.z + 1)
143+
},
144+
{
145+
i: flat.DesiredCarState(
146+
flat.DesiredPhysics(
147+
rotation=flat.RotatorPartial(yaw=0)
148+
)
147149
)
148-
)
149-
for i, car in enumerate(packet.players)
150-
},
151-
)
150+
for i, car in enumerate(packet.players)
151+
},
152+
)
152153

153154
def test_rendering(self, packet: flat.GamePacket):
154155
if not self.needs_render:
@@ -163,6 +164,11 @@ def test_rendering(self, packet: flat.GamePacket):
163164
self.renderer.begin_rendering()
164165

165166
text = ["Hello world!", "I hope I'm centered!"]
167+
self.renderer.draw_line_3d(
168+
flat.CarAnchor(self.index),
169+
flat.RenderAnchor(flat.Vector3(0, 0, 150), flat.CarAnchor(self.index)),
170+
self.renderer.lime,
171+
)
166172
self.renderer.draw_string_3d(
167173
"\n".join(text),
168174
flat.CarAnchor(self.index),

‎tests/human_vs_atba.toml

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ auto_start_bots = true
55
[match]
66
game_mode = "Soccer"
77
game_map_upk = "Stadium_P"
8+
enable_rendering = true
89

910
[mutators]
1011
match_length = "Unlimited"

‎tests/render_test/render.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,4 @@ def do_render(self, radius: float):
9595

9696

9797
if __name__ == "__main__":
98-
RenderFun().run(wants_match_communications=False, wants_ball_predictions=False)
98+
RenderFun("testing/render_test").run(wants_match_communications=False, wants_ball_predictions=False)

‎tests/run_match.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
DIR = Path(__file__).parent
88

9-
MATCH_CONFIG_PATH = DIR / "render_test.toml"
9+
MATCH_CONFIG_PATH = DIR / "human_vs_atba.toml"
1010
RLBOT_SERVER_FOLDER = DIR / "../../core/RLBotCS/bin/Release/"
1111

1212
if __name__ == "__main__":

0 commit comments

Comments
 (0)
Please sign in to comment.