Skip to content

Match Comm

Virx edited this page Feb 18, 2025 · 2 revisions

Basics

Sending

fromrlbot.flatimportControllerState, GamePacketfromrlbot.managersimportBotclassQuickChatExampleAgent(Bot):
defget_output(self, packet: GamePacket) ->ControllerState:
# There won't be any content of the message for other bots,# but "I got it!" will be display for a human to see!self.send_match_comm(b"", "I got it!")
returnControllerState()
if__name__=="__main__":
QuickChatExampleAgent("rlbot_community/quickchat_example").run()

This is just a simple example that spams "I got it!" to the screen for a human to read.

The method send_match_comm exists in the Bot class, so our QuickChatExampleAgent automatically inherits this class method when deriving from Bot.

The empty content (the b"") part of the messages means that other bots will likely ignore it. A standard format for this field like TMCP should be used for this, but the field can contain any information at all. It does not have to be valid text.

Receiving

fromrlbot.flatimportControllerState, GamePacketfromrlbot.managersimportBotclassQuickChatExampleAgent(Bot):
defget_output(self, packet: GamePacket) ->ControllerState:
returnControllerState()
defhandle_match_comm(
self,
index: int,
team: int,
content: bytes,
display: Optional[str],
team_only: bool,
):
ifdisplayisNone:
returnifteam==2:
print(f"Script @ index {index} (team {team}) said \"{display}\"")
else:
print(f"Bot @ index {index} (team {team}) said \"{display}\"")
if__name__=="__main__":
QuickChatExampleAgent("rlbot_community/quickchat_example").run()

This example prints out every message that a human can see, and correctly identifies bots vs scripts.

Method definitions

self.send_match_comm

Emits a match communication message to other bots and scripts.

  • For Bot & Script:

    defsend_match_comm(
    self,
    content: bytes,
    display: Optional[str] =None,
    team_only: bool=False
    ): ...
  • For Hivemind:

    defsend_match_comm(
    self,
    index: int,
    content: bytes,
    display: Optional[str] =None,
    team_only: bool=False,
    ): ...
    • index: This additional argument is the index of the bot that the message should be sent from.

Args:

  • content: The content of the message containing arbitrary data.
  • display: The message to be displayed in the game in "quick chat", or None to display nothing.
  • team_only: If True, only your team (blue/orange) will receive the message. For scripts, this means only other scripts.

self.handle_match_comm

Called when a match communication message is received.

defhandle_match_comm(
self,
index: int,
team: int,
content: bytes,
display: Optional[str],
team_only: bool,
): ...
  • index: The sender's index.
    • If team is 0 or 1: This is the index of the player in the GamePacket .
    • If team is 2: This is the index of the script in self.match_config.script_configurations.
  • team: The team that the sender is from. 0 for blue, 1 for orange, and 2 for scripts.
  • content: The content of the message containing arbitrary data.
  • display: The message that is ebing displayed in the game in "quick chat", or None if nothing was displayed.
  • team_only: If True, only your team (blue/orange) received this message. For scripts, this means only other scripts.

A realistic example

The field content can be anything. It's just a bunch of raw bytes in a row. How can you do something useful with this? The following example sends & receives JSON, which is much easier to work with:

importjsonfromrlbot.flatimportControllerState, GamePacketfromrlbot.managersimportBotclassQuickChatExampleAgent(Bot):
player_names: list[str] = []
last_enemy_idx: int|None=Nonedefhandle_match_comm(
self,
index: int,
team: int,
content: bytes,
display: str|None,
team_only: bool,
):
ifteam!=self.teamornotcontent:
returnsender_name=self.player_names[index]
ifnotteam_only:
self.logger.warning(f"{sender_name} is leaking secrets to the other team!!")
returntry:
msg_str=content.decode("utf-8")
msg=json.loads(msg_str)
exceptUnicodeDecodeError:
returnexceptjson.JSONDecodeError:
returnif"target"inmsg:
self.logger.info(f"{sender_name} is targeting {msg['target']}")
defget_output(self, packet: GamePacket) ->ControllerState:
self.player_names= [p.nameforpinpacket.players]
first_enemy_idx=Nonefori, playerinenumerate(packet.players):
ifplayer.team!=self.teamandplayer.demolished_timeout==-1:
first_enemy_idx=ibreakiffirst_enemy_idxisnotNoneandfirst_enemy_idx!=self.last_enemy_idx:
player_name=self.player_names[first_enemy_idx]
msg= {"target": f"{player_name} - Player[{first_enemy_idx}]"}
msg_str=json.dumps(msg)
self.send_match_comm(msg_str.encode("utf-8"), "Here's Johnny!", team_only=True)
self.last_enemy_idx=first_enemy_idxreturnControllerState()
if__name__=="__main__":
QuickChatExampleAgent("rlbot_community/quickchat_example").run()

In get_output, we:

  1. Save the list of all the player names (into the variable player_names) for later use and use in handle_match_comm.

  2. Find the first player in packet that's on the enemy team and isn't yet demolished, saving the index in first_enemy_idx.

  3. If first_enemy_idx is different from the last tick and isn't None, send out a match comm:

    • This match comm will have a contents that is serialized JSON containing our wanted information.
    • Will print out a message in the game saying "Here's Johnny!" for a human to see.
    • A copy of this message will only be sent to the other bots on our team.
  4. Set self.last_enemy_idx to first_enemy_idx for us to use in the next tick.

In handle_match_comm, we:

  1. Ignore messages that aren't from our team or have no content.
  2. Check that the sent message was team only. If it's not, we call out our teammate and ignore the message! After all, we can't have our opponents knowing what we're going to do...
  3. Try to decode the message from bytes into JSON. If it's not valid JSON, we silently ignore the message. We can't understand it.
  4. Check if the variable target is inside our JSON. If it is, we log what our teammate is targeting! Realistically, we would store this information and use it to inform our game strategy.

Clone this wiki locally