Skip to content

Commit 4a56992

Browse files
authored
Merge pull request #1 from VirxEC/beta0
Beta 1
2 parents 3d554a4 + 7068953 commit 4a56992

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+794
-116
lines changed

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ description = "A high performance Python interface for communicating with RLBot
88
dynamic = ["version"]
99
requires-python = ">= 3.11"
1010
dependencies = [
11-
"rlbot_flatbuffers~=0.6.0",
11+
"rlbot_flatbuffers~=0.8.1",
1212
"psutil==6.*",
1313
]
1414
readme = "README.md"

rlbot/interface.py

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import logging
22
from enum import IntEnum
33
from pathlib import Path
4-
from socket import socket, timeout
4+
from socket import IPPROTO_TCP, TCP_NODELAY, socket, timeout
55
from threading import Thread
66
from time import sleep
77
from typing import Callable, Optional
@@ -29,8 +29,10 @@ class SocketDataType(IntEnum):
2929
REMOVE_RENDER_GROUP = 8
3030
MATCH_COMMUNICATION = 9
3131
BALL_PREDICTION = 10
32-
READY_MESSAGE = 11
32+
CONNECTION_SETTINGS = 11
3333
STOP_COMMAND = 12
34+
SET_LOADOUT = 13
35+
INIT_COMPLETE = 14
3436

3537

3638
MAX_SIZE_2_BYTES = 2**16 - 1
@@ -76,6 +78,7 @@ def __init__(
7678
self.logger = get_logger("interface") if logger is None else logger
7779

7880
self.socket = socket()
81+
self.socket.setsockopt(IPPROTO_TCP, TCP_NODELAY, 1)
7982

8083
def __del__(self):
8184
self.socket.close()
@@ -91,6 +94,12 @@ def send_bytes(self, data: bytes, data_type: SocketDataType):
9194
message = int_to_bytes(data_type) + int_to_bytes(size) + data
9295
self.socket.sendall(message)
9396

97+
def send_init_complete(self, init_complete: flat.InitComplete):
98+
self.send_bytes(init_complete.pack(), SocketDataType.INIT_COMPLETE)
99+
100+
def send_set_loadout(self, set_loadout: flat.SetLoadout):
101+
self.send_bytes(set_loadout.pack(), SocketDataType.SET_LOADOUT)
102+
94103
def send_match_comm(self, match_comm: flat.MatchComm):
95104
self.send_bytes(match_comm.pack(), SocketDataType.MATCH_COMMUNICATION)
96105

@@ -180,12 +189,12 @@ def connect_and_run(
180189
for handler in self.on_connect_handlers:
181190
handler()
182191

183-
flatbuffer = flat.ReadyMessage(
192+
flatbuffer = flat.ConnectionSettings(
184193
wants_ball_predictions,
185194
wants_match_communcations,
186195
close_after_match,
187196
).pack()
188-
self.send_bytes(flatbuffer, SocketDataType.READY_MESSAGE)
197+
self.send_bytes(flatbuffer, SocketDataType.CONNECTION_SETTINGS)
189198

190199
incoming_message = read_from_socket(self.socket)
191200
self.handle_incoming_message(incoming_message)

rlbot/managers/bot.py

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ def __init__(self):
4141
self._game_interface.match_settings_handlers.append(self._handle_match_settings)
4242
self._game_interface.field_info_handlers.append(self._handle_field_info)
4343
self._game_interface.match_communication_handlers.append(
44-
self._handle_match_communication
44+
self.handle_match_communication
4545
)
4646
self._game_interface.ball_prediction_handlers.append(
4747
self._handle_ball_prediction
@@ -61,6 +61,7 @@ def _initialize_agent(self):
6161
exit()
6262

6363
self._initialized_bot = True
64+
self._game_interface.send_init_complete(flat.InitComplete(self.spawn_id))
6465

6566
def _handle_match_settings(self, match_settings: flat.MatchSettings):
6667
self.match_settings = match_settings
@@ -84,19 +85,10 @@ def _handle_field_info(self, field_info: flat.FieldInfo):
8485
if not self._initialized_bot and self._has_match_settings:
8586
self._initialize_agent()
8687

87-
def _handle_match_communication(self, match_comm: flat.MatchComm):
88-
if match_comm.team_only and self.team != match_comm.team:
89-
return
90-
91-
self.handle_match_communication(match_comm)
92-
9388
def _handle_ball_prediction(self, ball_prediction: flat.BallPrediction):
9489
self.ball_prediction = ball_prediction
9590

9691
def _handle_packet(self, packet: flat.GameTickPacket):
97-
if not self._initialized_bot:
98-
return
99-
10092
if (
10193
self.index == -1
10294
or len(packet.players) <= self.index
@@ -184,6 +176,15 @@ def set_game_state(self, game_state: flat.DesiredGameState):
184176
"""
185177
self._game_interface.send_game_state(game_state)
186178

179+
def set_loadout(self, loadout: flat.PlayerLoadout, spawn_id: int):
180+
"""
181+
Sets the loadout of a bot.
182+
183+
For use as a loadout generator, call inside of `initialize_agent`.
184+
Will be ignored if called outside of `initialize_agent` when state setting is disabled.
185+
"""
186+
self._game_interface.send_set_loadout(flat.SetLoadout(spawn_id, loadout))
187+
187188
def initialize_agent(self):
188189
"""
189190
Called for all heaver initialization that needs to happen.
@@ -195,7 +196,7 @@ def initialize_agent(self):
195196
def retire(self):
196197
"""Called after the game ends"""
197198

198-
def get_output(self, game_tick_packet: flat.GameTickPacket) -> flat.ControllerState:
199+
def get_output(self, packet: flat.GameTickPacket) -> flat.ControllerState:
199200
"""
200201
Where all the logic of your bot gets its input and returns its output.
201202
"""

rlbot/managers/hivemind.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ def __init__(self):
4242
self._game_interface.match_settings_handlers.append(self._handle_match_settings)
4343
self._game_interface.field_info_handlers.append(self._handle_field_info)
4444
self._game_interface.match_communication_handlers.append(
45-
self._handle_match_communication
45+
self.handle_match_communication
4646
)
4747
self._game_interface.ball_prediction_handlers.append(
4848
self._handle_ball_prediction
@@ -51,6 +51,21 @@ def __init__(self):
5151

5252
self.renderer = Renderer(self._game_interface)
5353

54+
def _initialize_agent(self):
55+
try:
56+
self.initialize_agent()
57+
except Exception as e:
58+
self._logger.critical(
59+
"Hivemind %s failed to initialize due the following error: %s",
60+
"Unknown" if len(self.names) == 0 else self.names[0],
61+
e,
62+
)
63+
print_exc()
64+
exit()
65+
66+
self._initialized_bot = True
67+
self._game_interface.send_init_complete(flat.InitComplete(self.spawn_ids[0]))
68+
5469
def _handle_match_settings(self, match_settings: flat.MatchSettings):
5570
self.match_settings = match_settings
5671
self._has_match_settings = True
@@ -64,30 +79,19 @@ def _handle_match_settings(self, match_settings: flat.MatchSettings):
6479
break
6580

6681
if not self._initialized_bot and self._has_field_info:
67-
self.initialize_agent()
68-
self._initialized_bot = True
82+
self._initialize_agent()
6983

7084
def _handle_field_info(self, field_info: flat.FieldInfo):
7185
self.field_info = field_info
7286
self._has_field_info = True
7387

7488
if not self._initialized_bot and self._has_match_settings:
75-
self.initialize_agent()
76-
self._initialized_bot = True
77-
78-
def _handle_match_communication(self, match_comm: flat.MatchComm):
79-
if match_comm.team_only and self.team != match_comm.team:
80-
return
81-
82-
self.handle_match_communication(match_comm)
89+
self._initialize_agent()
8390

8491
def _handle_ball_prediction(self, ball_prediction: flat.BallPrediction):
8592
self.ball_prediction = ball_prediction
8693

8794
def _handle_packet(self, packet: flat.GameTickPacket):
88-
if not self._initialized_bot:
89-
return
90-
9195
if len(self.indicies) != len(self.spawn_ids) or any(
9296
packet.players[i].spawn_id not in self.spawn_ids for i in self.indicies
9397
):
@@ -178,6 +182,15 @@ def set_game_state(self, game_state: flat.DesiredGameState):
178182
"""
179183
self._game_interface.send_game_state(game_state)
180184

185+
def set_loadout(self, loadout: flat.PlayerLoadout, spawn_id: int):
186+
"""
187+
Sets the loadout of a bot.
188+
189+
For use as a loadout generator, call inside of `initialize_agent`.
190+
Will be ignored if called outside of `initialize_agent` when state setting is disabled.
191+
"""
192+
self._game_interface.send_set_loadout(flat.SetLoadout(spawn_id, loadout))
193+
181194
def initialize_agent(self):
182195
"""
183196
Called for all heaver initialization that needs to happen.
@@ -190,7 +203,7 @@ def retire(self):
190203
"""Called after the game ends"""
191204

192205
def get_outputs(
193-
self, game_tick_packet: flat.GameTickPacket
206+
self, packet: flat.GameTickPacket
194207
) -> dict[int, flat.ControllerState]:
195208
"""
196209
Where all the logic of your bot gets its input and returns its output.

rlbot/managers/match.py

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,13 @@ def get_player_config(
7272
if CURRENT_OS == OS.LINUX and "run_command_linux" in settings:
7373
run_command = settings["run_command_linux"]
7474

75-
loadout_path = settings.get("looks_config", None)
75+
loadout_path = settings.get("loadout_config", None)
76+
77+
if loadout_path is None:
78+
loadout_path = settings.get("looks_config", None)
79+
if loadout_path is not None:
80+
DEFAULT_LOGGER.error("looks_config is deprecated, use loadout_config.")
81+
7682
if loadout_path is not None:
7783
loadout_path = parent / loadout_path
7884

@@ -97,9 +103,9 @@ def get_player_config(
97103
class MatchManager:
98104
logger = DEFAULT_LOGGER
99105
packet: Optional[flat.GameTickPacket] = None
100-
game_state = flat.GameStateType.Inactive
101106
rlbot_server_process: Optional[psutil.Process] = None
102107
rlbot_server_port = RLBOT_SERVER_PORT
108+
initialized = False
103109

104110
def __init__(
105111
self,
@@ -143,10 +149,9 @@ def ensure_server_started(self, print_version_info: bool = True):
143149

144150
def _packet_reporter(self, packet: flat.GameTickPacket):
145151
self.packet = packet
146-
self.game_state = packet.game_info.game_state_type
147152

148153
def wait_for_valid_packet(self):
149-
while self.game_state in {
154+
while self.packet is not None and self.packet.game_info.game_state_type in {
150155
flat.GameStateType.Inactive,
151156
flat.GameStateType.Ended,
152157
}:
@@ -158,21 +163,31 @@ def start_match(
158163
self.logger.info("Python attempting to start match.")
159164
self.rlbot_interface.start_match(match_config, self.rlbot_server_port)
160165

166+
if not self.initialized:
167+
self.rlbot_interface.send_init_complete(flat.InitComplete())
168+
self.initialized = True
169+
161170
if wait_for_start:
162171
self.wait_for_valid_packet()
163172
self.logger.info("Match has started.")
164173

165174
def disconnect(self):
166175
self.rlbot_interface.disconnect()
167176

177+
def stop_match(self):
178+
self.rlbot_interface.stop_match()
179+
180+
def set_game_state(self, game_state: flat.DesiredGameState):
181+
self.rlbot_interface.send_game_state(game_state)
182+
168183
def shut_down(self, ensure_shutdown=True):
169184
self.logger.info("Shutting down RLBot...")
170185

171186
# in theory this is all we need for the server to cleanly shut itself down
172187
try:
173188
self.rlbot_interface.stop_match(shutdown_server=True)
174189
except BrokenPipeError:
175-
match gateway.find_server_process(self.main_executable_name):
190+
match gateway.find_server_process(self.main_executable_name)[0]:
176191
case psutil.Process() as proc:
177192
self.logger.warning(
178193
"Can't communicate with RLBotServer, ensuring shutdown."

0 commit comments

Comments
 (0)