77from 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
2268def 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" )
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" )
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" ):
111+ if script_config := __str ( script_table , "config" ):
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 = __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 = __enum (mutator_table , "boost_amount" , flat .BoostMutator ),
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 )
0 commit comments