Skip to content

Commit 602fe40

Browse files
committed
Implement group ids spec
1 parent 140e30a commit 602fe40

File tree

12 files changed

+96
-98
lines changed

12 files changed

+96
-98
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.9.1",
11+
"rlbot_flatbuffers~=0.10.0",
1212
"psutil==6.*",
1313
]
1414
readme = "README.md"

rlbot/interface.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ class SocketDataType(IntEnum):
3434
STOP_COMMAND = 12
3535
SET_LOADOUT = 13
3636
INIT_COMPLETE = 14
37+
TEAM_CONTROLLABLES = 15
3738

3839

3940
MAX_SIZE_2_BYTES = 2**16 - 1
@@ -70,11 +71,16 @@ class SocketRelay:
7071
match_settings_handlers: list[Callable[[flat.MatchSettings], None]] = []
7172
match_communication_handlers: list[Callable[[flat.MatchComm], None]] = []
7273
ball_prediction_handlers: list[Callable[[flat.BallPrediction], None]] = []
74+
team_controllables_handlers: list[Callable[[flat.TeamControllables], None]] = []
7375
raw_handlers: list[Callable[[SocketMessage], None]] = []
7476

7577
def __init__(
76-
self, connection_timeout: float = 120, logger: Optional[logging.Logger] = None
78+
self,
79+
group_id: str,
80+
connection_timeout: float = 120,
81+
logger: Optional[logging.Logger] = None,
7782
):
83+
self.group_id = group_id
7884
self.connection_timeout = connection_timeout
7985
self.logger = get_logger("interface") if logger is None else logger
8086

@@ -95,8 +101,8 @@ def send_bytes(self, data: bytes, data_type: SocketDataType):
95101
message = int_to_bytes(data_type) + int_to_bytes(size) + data
96102
self.socket.sendall(message)
97103

98-
def send_init_complete(self, init_complete: flat.InitComplete):
99-
self.send_bytes(init_complete.pack(), SocketDataType.INIT_COMPLETE)
104+
def send_init_complete(self):
105+
self.send_bytes(bytes(), SocketDataType.INIT_COMPLETE)
100106

101107
def send_set_loadout(self, set_loadout: flat.SetLoadout):
102108
self.send_bytes(set_loadout.pack(), SocketDataType.SET_LOADOUT)
@@ -191,6 +197,7 @@ def connect(
191197
handler()
192198

193199
flatbuffer = flat.ConnectionSettings(
200+
self.group_id,
194201
wants_ball_predictions,
195202
wants_match_communications,
196203
close_after_match,
@@ -285,6 +292,13 @@ def handle_incoming_message(self, incoming_message: SocketMessage):
285292
ball_prediction = flat.BallPrediction.unpack(incoming_message.data)
286293
for handler in self.ball_prediction_handlers:
287294
handler(ball_prediction)
295+
case SocketDataType.TEAM_CONTROLLABLES:
296+
if len(self.team_controllables_handlers) > 0:
297+
player_mappings = flat.TeamControllables.unpack(
298+
incoming_message.data
299+
)
300+
for handler in self.team_controllables_handlers:
301+
handler(player_mappings)
288302

289303
def disconnect(self):
290304
if not self.is_connected:

rlbot/managers/bot.py

Lines changed: 54 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -27,20 +27,21 @@ class Bot:
2727
_initialized_bot = False
2828
_has_match_settings = False
2929
_has_field_info = False
30+
_has_player_mapping = False
3031

3132
_latest_packet: Optional[flat.GameTickPacket] = None
3233
_latest_prediction = flat.BallPrediction()
3334

3435
def __init__(self):
35-
spawn_id = os.environ.get("RLBOT_SPAWN_IDS")
36+
group_id = os.environ.get("RLBOT_GROUP_ID")
3637

37-
if spawn_id is None:
38-
self.logger.warning("RLBOT_SPAWN_IDS environment variable not set")
39-
else:
40-
self.spawn_id = int(spawn_id)
41-
self.logger.info("Spawn ID: %s", self.spawn_id)
38+
if group_id is None:
39+
self.logger.critical("RLBOT_GROUP_ID environment variable is not set")
40+
exit(1)
4241

43-
self._game_interface = SocketRelay(logger=self.logger)
42+
self.logger.info(group_id)
43+
44+
self._game_interface = SocketRelay(group_id, logger=self.logger)
4445
self._game_interface.match_settings_handlers.append(self._handle_match_settings)
4546
self._game_interface.field_info_handlers.append(self._handle_field_info)
4647
self._game_interface.match_communication_handlers.append(
@@ -49,11 +50,29 @@ def __init__(self):
4950
self._game_interface.ball_prediction_handlers.append(
5051
self._handle_ball_prediction
5152
)
53+
self._game_interface.team_controllables_handlers.append(
54+
self._handle_player_mappings
55+
)
5256
self._game_interface.packet_handlers.append(self._handle_packet)
5357

5458
self.renderer = Renderer(self._game_interface)
5559

5660
def _initialize_agent(self):
61+
# search match settings for our name
62+
for player in self.match_settings.player_configurations:
63+
if player.spawn_id == self.spawn_id:
64+
self.name = player.name
65+
self.logger = get_logger(self.name)
66+
break
67+
68+
self.logger.info(
69+
"Bot %s initialized - index %s / team %s / spawn id %s",
70+
self.name,
71+
self.index,
72+
self.team,
73+
self.spawn_id,
74+
)
75+
5776
try:
5877
self.initialize_agent()
5978
except Exception as e:
@@ -64,36 +83,42 @@ def _initialize_agent(self):
6483
exit()
6584

6685
self._initialized_bot = True
67-
self._game_interface.send_init_complete(flat.InitComplete(self.spawn_id))
86+
self._game_interface.send_init_complete()
6887

6988
def _handle_match_settings(self, match_settings: flat.MatchSettings):
7089
self.match_settings = match_settings
7190
self._has_match_settings = True
7291

73-
# search match settings for our spawn id
74-
for player in self.match_settings.player_configurations:
75-
if player.spawn_id == self.spawn_id:
76-
self.team = player.team
77-
self.name = player.name
78-
self.logger = get_logger(self.name)
79-
break
80-
81-
if self.spawn_id == 0:
82-
match player.variety.item:
83-
case flat.RLBot():
84-
self.team = player.team
85-
self.name = player.name
86-
self.logger = get_logger(self.name)
87-
break
88-
89-
if not self._initialized_bot and self._has_field_info:
92+
if (
93+
not self._initialized_bot
94+
and self._has_field_info
95+
and self._has_player_mapping
96+
):
9097
self._initialize_agent()
9198

9299
def _handle_field_info(self, field_info: flat.FieldInfo):
93100
self.field_info = field_info
94101
self._has_field_info = True
95102

96-
if not self._initialized_bot and self._has_match_settings:
103+
if (
104+
not self._initialized_bot
105+
and self._has_match_settings
106+
and self._has_player_mapping
107+
):
108+
self._initialize_agent()
109+
110+
def _handle_player_mappings(self, player_mappings: flat.TeamControllables):
111+
self.team = player_mappings.team
112+
controllable = player_mappings.controllables[0]
113+
self.spawn_id = controllable.spawn_id
114+
self.index = controllable.index
115+
self._has_player_mapping = True
116+
117+
if (
118+
not self._initialized_bot
119+
and self._has_match_settings
120+
and self._has_field_info
121+
):
97122
self._initialize_agent()
98123

99124
def _handle_ball_prediction(self, ball_prediction: flat.BallPrediction):
@@ -103,36 +128,8 @@ def _handle_packet(self, packet: flat.GameTickPacket):
103128
self._latest_packet = packet
104129

105130
def _packet_processor(self, packet: flat.GameTickPacket):
106-
if (
107-
self.index == -1
108-
or len(packet.players) <= self.index
109-
or packet.players[self.index].spawn_id != self.spawn_id
110-
):
111-
# spawn id should only be 0 if RLBOT_SPAWN_IDS was not set
112-
if self.spawn_id == 0:
113-
# in this case, if there's only one player, we can assume it's us
114-
player_index = -1
115-
for i, player in enumerate(packet.players):
116-
# skip human players/psyonix bots
117-
if not player.is_bot:
118-
continue
119-
120-
if player_index != -1:
121-
self.logger.error(
122-
"Multiple bots in the game, please set RLBOT_SPAWN_IDS"
123-
)
124-
return
125-
126-
player_index = i
127-
self.index = player_index
128-
129-
for i, player in enumerate(packet.players):
130-
if player.spawn_id == self.spawn_id:
131-
self.index = i
132-
break
133-
134-
if self.index == -1:
135-
return
131+
if len(packet.players) <= self.index:
132+
return
136133

137134
self.ball_prediction = self._latest_prediction
138135

@@ -279,9 +276,7 @@ def set_loadout(self, loadout: flat.PlayerLoadout, spawn_id: int):
279276
def initialize_agent(self):
280277
"""
281278
Called for all heaver initialization that needs to happen.
282-
Field info and match settings are fully loaded at this point, and won't return garbage data.
283-
284-
NOTE: `self.index` is not set at this point, and should not be used. `self.team` and `self.name` _are_ set with correct information.
279+
Field info, match settings, name, index, and team are fully loaded at this point, and won't return garbage data.
285280
"""
286281

287282
def retire(self):

rlbot/managers/hivemind.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -34,15 +34,13 @@ class Hivemind:
3434
_latest_prediction = flat.BallPrediction()
3535

3636
def __init__(self):
37-
spawn_ids = os.environ.get("RLBOT_SPAWN_IDS")
37+
group_id = os.environ.get("RLBOT_GROUP_ID")
3838

39-
if spawn_ids is None:
40-
self._logger.warning("RLBOT_SPAWN_IDS environment variable not set")
41-
else:
42-
self._logger.info("Spawn ID: %s", spawn_ids)
43-
self.spawn_ids = [int(id) for id in spawn_ids.split(",")]
39+
if group_id is None:
40+
self._logger.critical("RLBOT_GROUP_ID environment variable is not set")
41+
exit(1)
4442

45-
self._game_interface = SocketRelay(logger=self._logger)
43+
self._game_interface = SocketRelay(group_id, logger=self._logger)
4644
self._game_interface.match_settings_handlers.append(self._handle_match_settings)
4745
self._game_interface.field_info_handlers.append(self._handle_field_info)
4846
self._game_interface.match_communication_handlers.append(
@@ -68,7 +66,7 @@ def _initialize_agent(self):
6866
exit()
6967

7068
self._initialized_bot = True
71-
self._game_interface.send_init_complete(flat.InitComplete(self.spawn_ids[0]))
69+
self._game_interface.send_init_complete()
7270

7371
def _handle_match_settings(self, match_settings: flat.MatchSettings):
7472
self.match_settings = match_settings

rlbot/managers/match.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ def get_player_config(
9090
str(run_command),
9191
loadout,
9292
0,
93+
settings.get("group_id", ""),
9394
settings.get("hivemind", False),
9495
)
9596

@@ -109,7 +110,7 @@ def __init__(
109110
self.main_executable_path = main_executable_path
110111
self.main_executable_name = main_executable_name
111112

112-
self.rlbot_interface: SocketRelay = SocketRelay()
113+
self.rlbot_interface: SocketRelay = SocketRelay("")
113114
self.rlbot_interface.packet_handlers.append(self._packet_reporter)
114115

115116
def ensure_server_started(self, print_version_info: bool = True):
@@ -158,7 +159,7 @@ def start_match(
158159
self.rlbot_interface.start_match(match_config, self.rlbot_server_port)
159160

160161
if not self.initialized:
161-
self.rlbot_interface.send_init_complete(flat.InitComplete())
162+
self.rlbot_interface.send_init_complete()
162163
self.initialized = True
163164

164165
if wait_for_start:

rlbot/managers/script.py

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,13 @@ class Script:
2828
_has_field_info = False
2929

3030
def __init__(self):
31-
spawn_id = os.environ.get("RLBOT_SPAWN_IDS")
31+
group_id = os.environ.get("RLBOT_GROUP_ID")
3232

33-
if spawn_id is None:
34-
self.logger.warning("RLBOT_SPAWN_IDS environment variable not set")
35-
else:
36-
self.spawn_id = int(spawn_id)
37-
self.logger.info("Spawn ID: %s", self.spawn_id)
33+
if group_id is None:
34+
self.logger.critical("RLBOT_GROUP_ID environment variable is not set")
35+
exit(1)
3836

39-
self._game_interface = SocketRelay(logger=self.logger)
37+
self._game_interface = SocketRelay(group_id, logger=self.logger)
4038
self._game_interface.match_settings_handlers.append(self._handle_match_settings)
4139
self._game_interface.field_info_handlers.append(self._handle_field_info)
4240
self._game_interface.match_communication_handlers.append(
@@ -62,7 +60,7 @@ def _initialize_agent(self):
6260
exit()
6361

6462
self._initialized_bot = True
65-
self._game_interface.send_init_complete(flat.InitComplete(self.spawn_id))
63+
self._game_interface.send_init_complete()
6664

6765
def _handle_match_settings(self, match_settings: flat.MatchSettings):
6866
self.match_settings = match_settings

rlbot/version.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
__version__ = "5.0.0-beta.8"
1+
__version__ = "5.0.0-beta.9"
22

33

44
RESET_SEQ = "\033[0m"
@@ -15,6 +15,9 @@ def _get_color(color: int) -> str:
1515
)
1616

1717
RELEASE_NOTES = {
18+
"5.0.0-beta.9": """
19+
Update to new sockets spec
20+
""",
1821
"5.0.0-beta.8": """
1922
Fix spelling errors in interface
2023
""",

tests/atba/atba.bot.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ run_command = "..\\..\\venv\\Scripts\\python atba.py"
1212
# The command RLBot will call to start your bot on linux
1313
# If this isn't set, RLBot may attempt to run the Windows command under Wine
1414
run_command_linux = "../../venv/bin/python atba.py"
15+
group_id = "botmaker/atba"
1516

1617
[details]
1718
description = "Made possible by RLBot"

tests/hivemind/bot.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@ loadout_config = "../necto/loadout.toml"
44
location = ""
55
run_command = "..\\..\\venv\\Scripts\\python bot.py"
66
run_command_linux = "../../venv/bin/python bot.py"
7+
group_id = "botmaker/hives"
78
# must set to true if you want the bot to be ran as a hivemind
89
hivemind = true

tests/necto/agent.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ def act(
1818
self, state: tuple[np.ndarray, np.ndarray, np.ndarray], beta: float
1919
) -> tuple[np.ndarray, list[torch.Tensor]]:
2020
tensor_state = tuple(torch.from_numpy(s).float() for s in state)
21+
2122
with torch.no_grad():
2223
out, weights = self.actor(tensor_state)
2324

0 commit comments

Comments
 (0)