-
Notifications
You must be signed in to change notification settings - Fork 3
Migration
Legend: β¨=new, π =reworked/changed, β=removed
- π
GameMode- π Renamed
GameMode.SoccertoGameMode.Soccar - π Renamed
GameMode.HockeytoGameMode.Snowday
- π Renamed
- π
Bot- π Renamed
spawn_idtoplayer_id
- π Renamed
- π
Hivemind- π Renamed
spawn_idstoplayer_ids
- π Renamed
- π
Script- π Renamed
spawn_idtoscript_id
- π Renamed
- π
Bot/Hivemind/Script- β¨ Added
self.renderer.can_render-Trueif this agent can render,Falseif rendering has been disabled - β¨ Added
update_rendering_status, which lets you request for rendering to be enabled. Does nothing if rendering is set toDebugRendering.AlwaysOff. If the status of this agent is successfully updated,self.renderer.can_renderwill change.def update_rendering_status( self, status: bool, index: Optional[int] = None, is_bot: bool = True, ):
- β¨ Added
- π
load_player_configis now only for loadingCustomBottype players - β¨
load_psyonix_confighas been added for loadingPsyonixBottype players - π Reworked
PlayerConfiguration- Most values are now contained invariety: PlayerClass. See the definition ofPlayerConfiguration
Instead of using .cfg files with a format that's only parsed properly by Python,
RLBot v5 now uses the standard .toml format. This changes makes it easy to grab
your language's off-the-shelf TOML parser to read the configuration file and
get the same data that RLBot would.
To see how TOML works, check out the TOML spec.
Alternatively, to automate the conversion between the two formats, you can use this Python script: VirxEC/python-interface/tests/cfg_to_toml.py
This script can then be used like this: python cfg_to_toml.py necto.cfg necto.bot.toml
There's a new naming convention for the toml files:
- Config files that define bots MUST either be named
bot.tomlor end in.bot.toml. Here are examples of valid names:bot.tomlNecto.bot.tomlatba.bot.toml
- Config files that define scripts MUST either be named
script.tomlor end in.script.toml. Here are examples of valid names:script.tomltron.script.tomlSCB.script.toml
It should also be noted that whatever prefixes the .bot.toml/.script.toml file name will not be used by anything in RLBot.
Example of a bot.toml file that runs in a virtual environment:
[settings]
agent_id = "rolv-soren/necto"
name = "Necto"
loadout_file = "loadout.toml"
run_command = ".\\venv\\Scripts\\python bot.py"
run_command_linux = "./venv/bin/python bot.py"
logo_file = "necto_logo.png"
[details]
description = "Necto is the official RLGym community bot, trained using PPO with workers run by people all around the world."
fun_fact = "Necto uses an attention mechanism, commonly used for text understanding, to support any number of players"
source_link = "https://github.com/Rolv-Arild/Necto"
developer = "Rolv, Soren, and several contributors"
language = "rlgym"
tags = ["1v1", "teamplay"]Example of a script.toml file that runs in the global Python environment:
[settings]
agent_id = "rlbot-community/script-example"
name = "Script Example"
run_command = "python render.py"
run_command_linux = "python3 render.py"
logo_file = "logo.png"
[details]
description = "Script example"
fun_fact = "This is just an example TOML!"
source_link = ""
developer = "The RLBot community"
language = "python"
tags = []-
[settings]- Used by both RLBot & the GUI-
agent_id- The static, unique id that is associated with this bot.- Preferred format is
"author/bot-name"
- Preferred format is
-
name- The name of the bot/script. -
loadout_file- The path to the loadout file for the bot. -
run_command- The command to run the bot/script on Windows. -
run_command_linux- The command to run the bot/script on Linux. -
logo_file- The path to the logo file for the bot/script.
-
-
[details]- Used only by the GUI-
description- A description of the bot/script. -
fun_fact- A fun fact about the bot/script. -
source_link- A link to the source code of the bot/script. -
developer- The developer(s) of the bot/script. -
language- The language the bot/script is written in. -
tags- A list of tags that describe the bot/script. Possible tags:1v1teamplay-
goalie- Only add this tag if your bot only plays as a goalie; this directly contrasts with the teamplay tag! hoopsdropshotsnow-dayrumblespike-rushheatseekermemebot
-
There's fewer differences between the .toml and .cfg files for loadouts - all of they keys have stayed the same, only the headers are differently named.
-
[Bot Loadout]->[blue_loadout] -
[Bot Loadout Orange]->[orange_loadout] -
[Bot Paint Blue]->[blue_loadout.paint] -
[Bot Paint Orange]->[orange_loadout.paint]
Example of a loadout.toml file:
[blue_loadout]
# Primary Color selection
team_color_id = 29
# Secondary Color selection
custom_color_id = 0
# Car type (Octane, Merc, etc)
car_id = 23
# Type of decal
decal_id = 6083
# Wheel selection
wheels_id = 1580
# Boost selection
boost_id = 35
# Antenna Selection
antenna_id = 0
# Hat Selection
hat_id = 0
# Paint Type (for first color)
paint_finish_id = 0
# Paint Type (for secondary color)
custom_finish_id = 0
# Engine Audio Selection
engine_audio_id = 6919
# Car trail Selection
trails_id = 3220
# Goal Explosion Selection
goal_explosion_id = 4118
[orange_loadout]
team_color_id = 69
custom_color_id = 0
car_id = 23
decal_id = 6083
wheels_id = 1580
boost_id = 35
antenna_id = 0
hat_id = 0
paint_finish_id = 1681
custom_finish_id = 1681
engine_audio_id = 5635
trails_id = 3220
goal_explosion_id = 4118
[blue_loadout.paint]
car_paint_id = 12
decal_paint_id = 12
wheels_paint_id = 12
boost_paint_id = 12
antenna_paint_id = 0
hat_paint_id = 0
trails_paint_id = 12
goal_explosion_paint_id = 12
[orange_loadout.paint]
car_paint_id = 12
decal_paint_id = 12
wheels_paint_id = 12
boost_paint_id = 12
antenna_paint_id = 0
hat_paint_id = 0
trails_paint_id = 12
goal_explosion_paint_id = 12In RLBot v5, every bot is now more similar to v4's StandaloneBot.
This means that your bot MUST run it's run method at the bottom of the file,
or else IT WILL NOT RUN.
Here is a minimal example of a bot in RLBot v5:
from rlbot.flat import *
from rlbot.managers import Bot
class MyBot(Bot):
def initialize(self):
"""
Called for all heaver initialization that needs to happen.
Field info (`self.field_info`) and match settings (`self.match_config`) are fully loaded at this point, and won't return garbage data.
`self.index`, `self.team`, and `self.name` are also available.
"""
# A little known feature of even v4,
# bots have a `logger` attribute that can be used to log messages.
self.logger.info("Setting default values that require more match info!")
def get_output(self, packet: GamePacket) -> ControllerState:
"""
Where all the logic of your bot gets its input and returns its output.
"""
return ControllerState()
if __name__ == "__main__":
# This is the entry point for the bot.
MyBot().run()There have been several changes to how you access data like field info, ball prediction, etc.
-
π
def __init__(self, name: str, team: int, index: int)->def __init__(self, default_agent_id: Optional[str] = None)-
These variables are no longer known at this time.
-
default_agent_id- Use this hardcoded value if the environment variableRLBOT_AGENT_IDis not set. This is useful when manually starting the bot, as only runningpython bot.pyis required. IfNone, your bot will still require the environment variableRLBOT_AGENT_ID. An example of setting the default agent id:if __name__ == "__main__": # This is the entry point for the bot. MyBot("author/bot-name").run()
-
NOTE: This method is similar-ish to the "early start" of v4, but you probably shouldn't use it. If you want to define default variables, the following is more Pythonic:
class MyBot(Bot): my_variable = None counter = 1
These variables can then be accessed like normal via
self.my_variableandself.counter.If you need to perform more complex initialization, it's recommended you use
initializeinstead.
-
-
π
run(self, *, wants_match_communications: bool=True, wants_ball_predictions: bool=True)-
Consider setting these to
Falsefor machine learning bots that don't use these as input. Python doesn't just skip deserializing these packets, RLBotServer never sends them in the first place. -
Match comms may arrive with high frequency, and ball prediction packets tend to be on the larger side so this reduces latency and gives your bot more processing time.
-
Example usage:
if __name__ == "__main__": # This is the entry point for the bot. MyBot().run(wants_match_communications=False, wants_ball_predictions=False)
-
-
π
initialize- This method blocks the match from starting until it's finished running. All heavy initialization should be done here, as the purpose of the match not starting is to ensure that all bots had time to boot up and are ready to go. -
π
self.get_field_info()->self.field_info- Example:from rlbot.flat import * from rlbot.managers import Bot class MyBot(Bot): def initialize(self): # Log a yellow warning if the number of boost pads is not 34 pads in a standard map! if len(self.field_info.boost_pads) != 34: self.logger.warning( "The standard number of boost pads is 34, but this map has %d:%s", len(self.field_info.boost_pads), "\n".join(map(str, self.field_info.boost_pads)), ) def get_output(self, packet: GamePacket) -> ControllerState: return ControllerState() if __name__ == "__main__": MyBot().run()
-
π
self.get_match_settings()->self.match_config- Example:from rlbot.flat import * from rlbot.managers import Bot class MyBot(Bot): gravity = -650 def initialize(self): match self.match_config.mutators.gravity: case GravityMutator.Low: self.gravity /= 2 case GravityMutator.High: self.gravity *= 1.75 case GravityMutator.SuperHigh: self.gravity *= 5 case GravityMutator.Reverse: self.gravity *= -1 def get_output(self, packet: GamePacket) -> ControllerState: return ControllerState() if __name__ == "__main__": MyBot().run()
-
π
self.get_ball_prediction_struct()->self.ball_prediction -
β
def is_hot_reload_enabled(self) -> bool- This method is no longer needed, and as such as been removed. -
π
def set_game_state(self, game_state: GameState)- This method has been replaced by the following, where theGameStatewrapper has been removed:def set_game_state( self, balls: dict[int, DesiredBallState] = {}, cars: dict[int, DesiredCarState] = {}, match_info: Optional[DesiredMatchInfo] = None, commands: list[ConsoleCommand] = [], )
For
ballsandcars, theintkey is the index of the ball or car you want to modify.Example usage, where the game speed is set to 2x:
from rlbot.flat import DesiredGameInfoState # ... self.set_game_state( game_info=DesiredGameInfoState(game_speed=2) )
-
π QuickChats & MatchComms have been combined into one
-
def handle_quick_chat(self, index: int, team: int, quick_chat: QuickChats)- This method has been replaced by the following, wherecontentis abytesobject that contains the message, anddisplayis an optionalstrthat was owned in-game as a v4 QuickChat used to:from rlbot.flat import * # ... def handle_match_comm( self, index: int, team: int, content: bytes, display: Optional[str], team_only: bool, ): # Be careful with `content`, it can be anything! Make sure to validate it before using it. self.logger.info(f"Received match communication from index {index}! {display}")
-
def send_quick_chat(self, team_only: bool, quick_chat: QuickChats)->def send_match_comm(self, content: bytes, display: Optional[str] = None, team_only: bool = False)- Example:from rlbot.flat import * # ... # Print "What a save!" in the chat for all players to see # Note: `b""` is an empty byte string, which is the equivalent of `None` for bytes. # We're using it here to show that we don't need to send any extra data with the message. self.send_match_comm(b"", "What a save!") # Send "15 bot_has_ball" to only our team, with no message to print to the screen # Unlike v4 which operated on an honor system, v5 will not send this message to the other team self.send_match_comm(b"15 bot_has_ball", team_only=True)
-
-
β¨
draw_polyline_3dshould now be preferred as it allows more lines to be drawn per tick. -
β¨
TextHAlignis for horizontal text alignment, and can beLeft,Center, orRight -
β¨
TextVAlignis for vertical text alignment, and can beTop,Center, orBottom -
β¨
BallAnchor/CarAnchorspecifies a point offset from a ball or car on the field. In order words, a point that is relative to a ball's/car's position and orientation. Note: Unless you want to track a different object, this will continuously update the position until the entity is removed. This means that you only have to re-render the line when, for example, a car is demo'd or when the ball is scored and the next kickoff starts. -
β¨
RenderAnchorspecifies either aBallAnchororCarAnchoralongsideVector3that's relative to the center of the field. Note: Useful if you want, for example, a string that follows a car around the field but is alwaysz=-100under the car without the car's orientation being considered. -
π
begin_renderingis now REQUIRED to be called before any rendering is done, andend_renderingmust be called to send the registered renders to RLBot. You can specify agroup_idwhen callingbegin_rendering. A group of renders will persist in the game until another render group is sent with the samegroup_id. -
π
clear_screenhas been renamed toclear_render_group -
π
clear_all_touched_render_groupshas been renamed toclear_all_render_groups -
π All colors defined within
Rendererare now variables instead of functions -
π
draw_line_3dis now defined as follows:def draw_line_3d( self, start: flat.RenderAnchor | flat.BallAnchor | flat.CarAnchor | flat.Vector3, end: flat.RenderAnchor | flat.BallAnchor | flat.CarAnchor | flat.Vector3, color: flat.Color, ): ...
-
π
draw_string_3dis now defined as follows:def draw_string_3d( self, text: str, anchor: flat.RenderAnchor | flat.BallAnchor | flat.CarAnchor | flat.Vector3, scale: float, foreground: flat.Color, background: flat.Color = flat.Color(), h_align: flat.TextHAlign = flat.TextHAlign.Left, v_align: flat.TextVAlign = flat.TextVAlign.Top, ): ...
Note:
foregroundis the text color. -
π
draw_string_2dis now defined as follows:def draw_string_2d( self, text: str, x: float, y: float, scale: float, foreground: flat.Color, background: flat.Color = flat.Color(), h_align: flat.TextHAlign = flat.TextHAlign.Left, v_align: flat.TextVAlign = flat.TextVAlign.Top, ): ...
Note: x/y are now go from 0 to 1 (they aren't measure in pixels, pretending that the screen is 16:9 1080p).
foregroundis the text color. -
π
draw_rect_2dis now defined as follows:def draw_rect_2d( self, x: float, y: float, width: float, height: float, color: flat.Color, centered: bool = True, ): ...
-
π
draw_rect_3dis now defined as follows:def draw_rect_3d( self, anchor: flat.RenderAnchor | flat.BallAnchor | flat.CarAnchor | flat.Vector3, width: float, height: float, color: flat.Color, ): ...
- π Now 120 TPS instead of 60 TPS.
- β
num_sliceshas been removed due to it not being needed anymore.- The length of the list
slicesis now limited to the number of slices in the prediction. Iterating over the list until the end is now the proper way to access all the data.
- The length of the list
- β
num_boostsandnum_goalshave been removed due to them not being needed anymore.- The length of the lists
boost_padsandgoalsare now limited to the number of boosts and goals on the field. Iterating over the lists until the end is now the proper way to access all the data.
- The length of the lists
-
β¨
auto_start_agents- Whether or not RLBot should automatically start the bots & scripts. If set toFalse, they will not start until the user manually starts them. -
β¨
wait_for_agents- Whether or not RLBot should automatically start the bots & scripts. If set toFalse, they will not start until the user manually starts them. -
β¨
script_configurations- The scripts that are running in the match. -
β¨
freeplay- Whether or not to start the match in Freeplay instead of a normal Exhibition match. May be useful for testing purposes. -
β¨
launcher&launcher_arg- How RLBot should start the game. The options are:-
Launcher.Steam- Start the game through Steam.-
launcher_argdoes nothing
-
-
Launcher.Epic- Windows only - Start the game through the Epic Games Store.- No
launcher_argrequired anymore!
- No
-
Launcher.Custom- Start the game through a custom method. Currently:-
launcher_arg = "legendary"- Start the game through the Epic Games Store via the Legendary launcher. -
launcher_arg = "heroic"- Start the game through the Heroic Launcher.
-
-
Launcher.NoLaunch- The game will not be started by RLBot.
-
-
β
game_map- Usegame_map_upkinstead. If you don't know the file names:import random from rlbot.utils.maps import GAME_MAP_TO_UPK, STANDARD_MAPS # grab random map name from the list STANDARD_MAPS random_map = random.choice(STANDARD_MAPS) # convert the map name to the upk file name game_map_upk = GAME_MAP_TO_UPK[random_map]
See tests/run_forever.py for a more complete example on running a match with a random map.
-
π
mutator_settings->mutators+ new options!Interested in what mutators go with what games modes? Check out this list!
- β¨
multi_ballMultiBallMutator.OneMultiBallMutator.TwoMultiBallMutator.FourMultiBallMutator.Six
- β¨
max_timeMaxTimeMutator.DefaultMaxTimeMutator.ElevenMinutes
- β¨
game_eventGameEventMutator.DefaultGameEventMutator.HauntedGameEventMutator.Rugby
- β¨
audioAudioMutator.DefaultAudioMutator.Haunted
- π
max_score- β¨
MaxScoreMutator.Seven - β¨
MaxScoreMutator.TenGoals - β¨
MaxScoreMutator.TwentyGoals - β¨
MaxScoreMutator.ThirtyGoals - β¨
MaxScoreMutator.FortyGoals - β¨
MaxScoreMutator.FiftyGoals - β¨
MaxScoreMutator.SixtyGoals - β¨
MaxScoreMutator.SeventyGoals - β¨
MaxScoreMutator.EightyGoals - β¨
MaxScoreMutator.NinetyGoals - β¨
MaxScoreMutator.HundredGoals
- β¨
- π
ball_type- β¨
BallTypeMutator.Beachball - β¨
BallTypeMutator.Anniversary - β¨
BallTypeMutator.Haunted - β¨
BallTypeMutator.Ekin - β¨
BallTypeMutator.SpookyCube - β¨
BallTypeMutator.Egg - β¨
BallTypeMutator.PlayerSeeking - β¨
BallTypeMutator.Dropshot - β¨
BallTypeMutator.ScoreAbsorb
- β¨
- π
ball_weight- β¨
BallWeightMutator.CurveBall - β¨
BallWeightMutator.BeachBallCurve - β¨
BallWeightMutator.MagnusFutBall
- β¨
- π
ball_size- β¨
BallSizeMutator.Medium
- β¨
- π
ball_size- β¨
BallBouncinessMutator.Lowish
- β¨
- π
rumble- β¨
RumbleMutator.HauntedBallBeam - β¨
RumbleMutator.Tactical - β¨
RumbleMutator.BatmanRumble - β¨
RumbleMutator.GrapplingOnly - β¨
RumbleMutator.HaymakerOnly
- β¨
- π
boost_strength- β¨
BoostStrengthMutator.Five
- β¨
- π
gravity- β¨
GravityMutator.Reverse
- β¨
- π
demolish- β¨
DemolishMutator.OnBallContact - β¨
DemolishMutator.OnBallContactFF
- β¨
- β¨
ball_gravity- β¨
BallGravityMutator.Default - β¨
BallGravityMutator.Low - β¨
BallGravityMutator.High - β¨
BallGravityMutator.SuperHigh
- β¨
- β¨
territory- β¨
TerritoryMutator.Off - β¨
TerritoryMutator.Territory
- β¨
- β¨
stale_ball- β¨
StaleBallMutator.Unlimited - β¨
StaleBallMutator.ThirtySeconds
- β¨
- β¨
jump- β¨
JumpMutator.Default - β¨
JumpMutator.Grounded - β¨
JumpMutator.Two - β¨
JumpMutator.Three - β¨
JumpMutator.Four - β¨
JumpMutator.Unlimited - β¨
JumpMutator.NoJump
- β¨
- β¨
dodge_timer- β¨
DodgeTimerMutator.OnePointTwentyFiveSeconds - β¨
DodgeTimerMutator.TwoSeconds - β¨
DodgeTimerMutator.ThreeSeconds - β¨
DodgeTimerMutator.Unlimited
- β¨
- β¨
input_restriction- β¨
InputRestrictionMutator.Default - β¨
InputRestrictionMutator.Backwards
- β¨
- β¨
-
π
game_cars->players- π
PlayerInfo- β¨
latest_touch- The last time the player touched the ball.- Will be
Noneif the player has not touched the ball since the last kickoff. - Contains the
ball_indexof the ball that was touched.
- Will be
- β¨
air_state- The current state of the car in the air. Possible values are:-
AirState.OnGround- All 4 wheels are touching the ground. -
AirState.Jumping- Lasts whilejumpis being held by the player -
AirState.DoubleJumping- Lasts for ~13 ticks. -
AirState.Dodging- Lasts for the duration of the torque applied by the dodge, or ~79 ticks. -
AirState.InAir- The car is in the air, but not in any of the other states.
-
- β¨
dodge_timeout- A countdown, in seconds, until the dodge expires. Starts counting down after the car releases jump.-1otherwise. - β¨
has_dodged- A compliment tohas_double_jumped, this isTrueorFalsedepending on if the player has dodged since it last touched the ground. - β¨
dodge_elapsed- The time, in seconds, since the car last started dodging. Continues counting until the car lands on the ground. - β¨
dodge_dir- The (-pitch, yaw + roll) of the car when on the first frame of it's dodge. - β¨
last_input- The last controller input the player used. - β¨
accolades- A list of the accolades (as strings) the player earned in the previous tick. Here are some examples of different accolades:-
Win,Loss,TimePlayed -
Shot,Assist,Center,Clear,PoolShot -
Goal,AerialGoal,BicycleGoal,BulletGoal,BackwardsGoal,LongGoal,OvertimeGoal,TurtleGoal -
AerialHit,BicycleHit,BulletHit,JuggleHit,FirstTouch,BallHit -
Save,EpicSave,FreezeSave -
HatTrick,Savior,Playmaker,MVP -
FastestGoal,SlowestGoal,FurthestGoal,OwnGoal -
MostBallTouches,FewestBallTouches,MostBoostPickups,FewestBoostPickups,BoostPickups -
CarTouches,Demolition,Demolish -
LowFive,HighFive
-
- π
is_demolished->demolished_timeout--1if the player is not demolished, otherwise the time remaining until the player respawns. - β
has_wheel_contact- this is directly replaced byair_state == AirState.OnGround
- β¨
- π
-
π
game_boosts->boost_pads -
π
game_teams->teams -
β
num_cars,num_boosts, andnum_teamshave been removed due to them not being needed anymore.- The length of the lists
players,boost_pads, andteamsare now limited to the number of cars and boosts in the match. Iterating over the lists until the end is now the proper way to access all the data.
- The length of the lists
-
π
ball->balls-ballsis now a list, and RLBot officially supports multiple balls in a single match. However, there are also times in a normal game where this list has 0 items - it's recommended that this is checked for near the start of your logic, so your bot doesn't accidentally throw hundreds of errors.def get_output(self, packet: GamePacket) -> ControllerState: if len(packet.balls) == 0: return ControllerState() # Your logic here # ...
-
π
BallInfo- β
latest_touchhas been removed due to it now being tracked inPlayerInfo - β
drop_shot_infohas been removed due to RLBot not getting any dropshot data. - π
collision_shape->shape-
Old
collision_shapetype:class CollisionShape: type: ShapeType box: BoxShape sphere: SphereShape cylinder: CylinderShape
-
New
shapetype:class CollisionShape: item: Optional[BoxShape | SphereShape | CylinderShape]
-
- β
-
π
game_info->match_info- β¨
last_spectated- The index of the last player that was spectated. This value is the maximum a 32-bit unsigned integer can hold (4,294,967,295) if a player has not yet been spectated. - β
is_round_active,is_kickoff_pause, andis_match_ended-
is_round_activeis directly replaced bymatch_phase == MatchPhase.Active -
is_kickoff_pauseis directly replaced bymatch_phase == MatchPhase.Kickoff -
is_match_endedis directly replaced bymatch_phase == MatchPhase.Ended
-
- β¨
match_phase- The current state of the game. The options are:-
MatchPhase.Inactive- The game is not running. -
MatchPhase.Countdown- The 3.. 2.. 1.. countdown before the a kickoff. -
MatchPhase.Kickoff- After the countdown, but before the game timer starts counting down again. Usually the timer resumes after the ball gets hit by a car. -
MatchPhase.Active- Normal game play. -
MatchPhase.GoalScored- A goal has been scored and the goal animation is playing. -
MatchPhase.Replay- The goal replay is playing. -
MatchPhase.Paused- The game is paused. -
MatchPhase.Ended- The match finished and is on the end screen.
-
- β¨