Skip to content

Commit acd455d

Browse files
committed
Support for the RenderingStatus message
1 parent ef8005a commit acd455d

File tree

8 files changed

+146
-32
lines changed

8 files changed

+146
-32
lines changed

rlbot/interface.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ class SocketRelay:
4545
controllable_team_info_handlers: list[
4646
Callable[[flat.ControllableTeamInfo], None]
4747
] = []
48+
rendering_status_handlers: list[Callable[[flat.RenderingStatus], None]] = []
4849
raw_handlers: list[Callable[[flat.CoreMessage], None]] = []
4950

5051
def __init__(
@@ -114,13 +115,11 @@ def send_msg(
114115
| flat.StopCommand
115116
| flat.SetLoadout
116117
| flat.InitComplete
118+
| flat.RenderingStatus
117119
),
118120
):
119121
self.send_bytes(flat.InterfacePacket(msg).pack())
120122

121-
def remove_render_group(self, group_id: int):
122-
self.send_msg(flat.RemoveRenderGroup(group_id))
123-
124123
def stop_match(self, shutdown_server: bool = False):
125124
self.send_msg(flat.StopCommand(shutdown_server))
126125

@@ -298,6 +297,9 @@ def handle_incoming_message(self, incoming_message: bytes) -> MsgHandlingResult:
298297
case flat.ControllableTeamInfo() as controllable_team_info:
299298
for handler in self.controllable_team_info_handlers:
300299
handler(controllable_team_info)
300+
case flat.RenderingStatus() as rendering_status:
301+
for handler in self.rendering_status_handlers:
302+
handler(rendering_status)
301303
case _:
302304
self.logger.warning(
303305
"Received unknown message type: %s",

rlbot/managers/bot.py

Lines changed: 50 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class Bot:
3131
index: int = -1
3232
name: str = ""
3333
player_id: int = 0
34+
can_render: bool = False
3435

3536
@property
3637
def spawn_id(self) -> int:
@@ -90,6 +91,9 @@ def __init__(self, default_agent_id: Optional[str] = None):
9091
self._handle_controllable_team_info
9192
)
9293
self._game_interface.packet_handlers.append(self._handle_packet)
94+
self._game_interface.rendering_status_handlers.append(
95+
self.rendering_status_update
96+
)
9397

9498
self.renderer = Renderer(self._game_interface)
9599

@@ -103,15 +107,6 @@ def _try_initialize(self):
103107
# Not ready to initialize
104108
return
105109

106-
# Search match settings for our name
107-
for player in self.match_config.player_configurations:
108-
match player.variety.item:
109-
case flat.CustomBot(name):
110-
if player.player_id == self.player_id:
111-
self.name = name
112-
self.logger = get_logger(self.name)
113-
break
114-
115110
try:
116111
self.initialize()
117112
except Exception as e:
@@ -127,6 +122,24 @@ def _try_initialize(self):
127122
def _handle_match_config(self, match_config: flat.MatchConfiguration):
128123
self.match_config = match_config
129124
self._has_match_settings = True
125+
self.can_render = (
126+
match_config.enable_rendering == flat.DebugRendering.OnByDefault
127+
)
128+
129+
# Search match settings for our name
130+
for player in self.match_config.player_configurations:
131+
match player.variety.item:
132+
case flat.CustomBot(name):
133+
if player.player_id == self.player_id:
134+
self.name = name
135+
self.logger = get_logger(self.name)
136+
break
137+
else: # else block runs if break was not hit
138+
self.logger.warning(
139+
"Bot with agent id '%s' did not find itself in the match settings",
140+
self._game_interface.agent_id,
141+
)
142+
130143
self._try_initialize()
131144

132145
def _handle_field_info(self, field_info: flat.FieldInfo):
@@ -228,6 +241,34 @@ def _handle_match_communication(self, match_comm: flat.MatchComm):
228241
match_comm.team_only,
229242
)
230243

244+
def rendering_status_update(self, update: flat.RenderingStatus):
245+
"""
246+
Called when the server sends a rendering status update for ANY bot or script.
247+
248+
By default, this will update `self.can_render` if appropriate.
249+
"""
250+
if update.is_bot and update.index == self.index:
251+
self.can_render = update.status
252+
253+
def update_rendering_status(
254+
self,
255+
status: bool,
256+
index: Optional[int] = None,
257+
is_bot: bool = True,
258+
):
259+
"""
260+
Requests the server to update the status of the ability for this bot to render.
261+
Will be ignored if rendering has been set to AlwaysOff in the match settings.
262+
If the status is successfully updated, the `self.rendering_status_update` method will be called which will update `self.can_render`.
263+
264+
- `status`: `True` to enable rendering, `False` to disable.
265+
- `index`: The index of the bot to update. If `None`, uses the bot's own index.
266+
- `is_bot`: `True` if `index` is a bot index, `False` if it is a script index.
267+
"""
268+
self._game_interface.send_msg(
269+
flat.RenderingStatus(self.index if index is None else index, is_bot, status)
270+
)
271+
231272
def handle_match_comm(
232273
self,
233274
index: int,

rlbot/managers/hivemind.py

Lines changed: 45 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ class Hivemind:
3333
indices: list[int] = []
3434
names: list[str] = []
3535
player_ids: list[int] = []
36+
can_render: bool = False
3637

3738
@property
3839
def spawn_ids(self) -> list[int]:
@@ -92,6 +93,9 @@ def __init__(self, default_agent_id: Optional[str] = None):
9293
self._handle_controllable_team_info
9394
)
9495
self._game_interface.packet_handlers.append(self._handle_packet)
96+
self._game_interface.rendering_status_handlers.append(
97+
self.rendering_status_update
98+
)
9599

96100
self.renderer = Renderer(self._game_interface)
97101

@@ -104,16 +108,6 @@ def _try_initialize(self):
104108
):
105109
return
106110

107-
# Search match settings for our spawn ids
108-
for player_id in self.player_ids:
109-
for player in self.match_config.player_configurations:
110-
match player.variety.item:
111-
case flat.CustomBot(name):
112-
if player.player_id == player_id:
113-
self.names.append(name)
114-
self.loggers.append(get_logger(name))
115-
break
116-
117111
try:
118112
self.initialize()
119113
except Exception as e:
@@ -131,6 +125,17 @@ def _try_initialize(self):
131125
def _handle_match_config(self, match_config: flat.MatchConfiguration):
132126
self.match_config = match_config
133127
self._has_match_settings = True
128+
129+
# Search match settings for our spawn ids
130+
for player_id in self.player_ids:
131+
for player in self.match_config.player_configurations:
132+
match player.variety.item:
133+
case flat.CustomBot(name):
134+
if player.player_id == player_id:
135+
self.names.append(name)
136+
self.loggers.append(get_logger(name))
137+
break
138+
134139
self._try_initialize()
135140

136141
def _handle_field_info(self, field_info: flat.FieldInfo):
@@ -229,6 +234,36 @@ def run(
229234
self.retire()
230235
del self._game_interface
231236

237+
def rendering_status_update(self, update: flat.RenderingStatus):
238+
"""
239+
Called when the server sends a rendering status update for ANY bot or script.
240+
241+
By default, this will update `self.can_render` if appropriate.
242+
"""
243+
if update.is_bot and update.index in self.indices:
244+
self.can_render = update.status
245+
246+
def update_rendering_status(
247+
self,
248+
status: bool,
249+
index: Optional[int] = None,
250+
is_bot: bool = True,
251+
):
252+
"""
253+
Requests the server to update the status of the ability for this bot to render.
254+
Will be ignored if rendering has been set to AlwaysOff in the match settings.
255+
If the status is successfully updated, the `self.rendering_status_update` method will be called which will update `self.can_render`.
256+
257+
- `status`: `True` to enable rendering, `False` to disable.
258+
- `index`: The index of the bot to update. If `None`, uses the bot's own index.
259+
- `is_bot`: `True` if `index` is a bot index, `False` if it is a script index.
260+
"""
261+
self._game_interface.send_msg(
262+
flat.RenderingStatus(
263+
self.indices[0] if index is None else index, is_bot, status
264+
)
265+
)
266+
232267
def _handle_match_communication(self, match_comm: flat.MatchComm):
233268
self.handle_match_comm(
234269
match_comm.index,

rlbot/managers/rendering.py

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -55,10 +55,8 @@ class Renderer:
5555
_screen_height_factor = 1.0
5656

5757
def __init__(self, game_interface: SocketRelay):
58-
self._render_group: Callable[[flat.RenderGroup], None] = game_interface.send_msg
59-
60-
self._remove_render_group: Callable[[int], None] = (
61-
game_interface.remove_render_group
58+
self._send_msg: Callable[[flat.RenderGroup | flat.RemoveRenderGroup], None] = (
59+
game_interface.send_msg
6260
)
6361

6462
def set_resolution(self, screen_width: float, screen_height: float):
@@ -141,7 +139,7 @@ def end_rendering(self):
141139
)
142140
return
143141

144-
self._render_group(flat.RenderGroup(self._current_renders, self._group_id))
142+
self._send_msg(flat.RenderGroup(self._current_renders, self._group_id))
145143
self._current_renders.clear()
146144
self._group_id = None
147145

@@ -151,7 +149,7 @@ def clear_render_group(self, group_id: str = DEFAULT_GROUP_ID):
151149
Note: It is not possible to clear render groups of other bots.
152150
"""
153151
group_id_hash = Renderer._get_group_id(group_id)
154-
self._remove_render_group(group_id_hash)
152+
self._send_msg(flat.RemoveRenderGroup(group_id_hash))
155153
self._used_group_ids.discard(group_id_hash)
156154

157155
def clear_all_render_groups(self):
@@ -160,7 +158,7 @@ def clear_all_render_groups(self):
160158
Note: This does not clear render groups created by other bots.
161159
"""
162160
for group_id in self._used_group_ids:
163-
self._remove_render_group(group_id)
161+
self._send_msg(flat.RemoveRenderGroup(group_id))
164162
self._used_group_ids.clear()
165163

166164
def is_rendering(self):

rlbot/managers/script.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class Script:
2626

2727
index: int = 0
2828
name: str = "UnknownScript"
29+
can_render: bool = False
2930

3031
match_config = flat.MatchConfiguration()
3132
field_info = flat.FieldInfo()
@@ -59,6 +60,9 @@ def __init__(self, default_agent_id: Optional[str] = None):
5960
self._handle_ball_prediction
6061
)
6162
self._game_interface.packet_handlers.append(self._handle_packet)
63+
self._game_interface.rendering_status_handlers.append(
64+
self.rendering_status_update
65+
)
6266

6367
self.renderer = Renderer(self._game_interface)
6468

@@ -88,12 +92,15 @@ def _try_initialize(self):
8892

8993
def _handle_match_config(self, match_config: flat.MatchConfiguration):
9094
self.match_config = match_config
95+
self._has_match_settings = True
96+
self.can_render = (
97+
match_config.enable_rendering == flat.DebugRendering.OnByDefault
98+
)
9199

92100
for i, script in enumerate(match_config.script_configurations):
93101
if script.agent_id == self._game_interface.agent_id:
94102
self.index = i
95103
self.name = script.name
96-
self._has_match_settings = True
97104
break
98105
else: # else block runs if break was not hit
99106
self.logger.warning(
@@ -180,6 +187,34 @@ def _handle_match_communication(self, match_comm: flat.MatchComm):
180187
match_comm.team_only,
181188
)
182189

190+
def rendering_status_update(self, update: flat.RenderingStatus):
191+
"""
192+
Called when the server sends a rendering status update for ANY bot or script.
193+
194+
By default, this will update `self.can_render` if appropriate.
195+
"""
196+
if not update.is_bot and update.index == self.index:
197+
self.can_render = update.status
198+
199+
def update_rendering_status(
200+
self,
201+
status: bool,
202+
index: Optional[int] = None,
203+
is_bot: bool = False,
204+
):
205+
"""
206+
Requests the server to update the status of the ability for this bot to render.
207+
Will be ignored if rendering has been set to AlwaysOff in the match settings.
208+
If the status is successfully updated, the `self.rendering_status_update` method will be called which will update `self.can_render`.
209+
210+
- `status`: `True` to enable rendering, `False` to disable.
211+
- `index`: The index of the bot to update. If `None`, uses the script's own index.
212+
- `is_bot`: `True` if `index` is a bot index, `False` if it is a script index.
213+
"""
214+
self._game_interface.send_msg(
215+
flat.RenderingStatus(self.index if index is None else index, is_bot, status)
216+
)
217+
183218
def handle_match_comm(
184219
self,
185220
index: int,

tests/render_test.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ launcher = "Steam"
55
[match]
66
game_mode = "Soccar"
77
game_map_upk = "Stadium_P"
8-
enable_rendering = true
8+
enable_rendering = "OffByDefault"
99

1010
[[cars]]
1111
type = "Human"

tests/render_test/render.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ class RenderFun(Script):
88
last_state = flat.MatchPhase.Inactive
99
player_count = 0
1010

11+
def initialize(self):
12+
self.update_rendering_status(True)
13+
1114
def handle_packet(self, packet: flat.GamePacket):
1215
if (
1316
packet.match_info.match_phase != flat.MatchPhase.Replay

tests/run_only.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
DIR = Path(__file__).parent
77

8-
MATCH_CONFIG_PATH = DIR / "human_vs_necto.toml"
8+
MATCH_CONFIG_PATH = DIR / "render_test.toml"
99
RLBOT_SERVER_FOLDER = DIR / "../"
1010

1111
if __name__ == "__main__":

0 commit comments

Comments
 (0)