diff --git a/agent/base_agent.py b/agent/base_agent.py index c28d9be..fc5c327 100644 --- a/agent/base_agent.py +++ b/agent/base_agent.py @@ -5,6 +5,7 @@ from world.robot import T1, Robot from behaviors.behavior_manager import BehaviorManager from world.world import World from communication.server import Server +from communication.monitor_client import MonitorClient from communication.world_parser import WorldParser logger = logging.getLogger(__file__) @@ -35,6 +36,7 @@ class Base_Agent: self.server: Server = Server( host=host, port=port, world_parser=self.world_parser ) + self.monitor: MonitorClient = MonitorClient(host=host, port=port + 1) self.robot: Robot = T1(agent=self) self.skills_manager: BehaviorManager = BehaviorManager(agent=self) self.decision_maker: Agent = Agent(agent=self) @@ -53,6 +55,7 @@ class Base_Agent: - Sends the next set of commands to the server. """ self.server.connect() + self.monitor.connect() self.server.send_immediate( f"(init {self.robot.name} {self.world.team_name} {self.world.number})" @@ -78,4 +81,5 @@ class Base_Agent: Logs a shutdown message and closes the server connection. """ logger.info("Shutting down.") - self.server.shutdown() \ No newline at end of file + self.monitor.close() + self.server.shutdown() diff --git a/communication/monitor_client.py b/communication/monitor_client.py new file mode 100644 index 0000000..64e1e69 --- /dev/null +++ b/communication/monitor_client.py @@ -0,0 +1,63 @@ +import logging +import socket + + +logger = logging.getLogger(__name__) + + +class MonitorClient: + """ + TCP client for the RCSSServerMJ monitor port. + + Sends monitor commands via the length-prefixed S-expression protocol. + """ + + def __init__(self, host: str = "localhost", port: int = 60001): + self._host = host + self._port = port + self._socket: socket.socket | None = None + + def connect(self) -> None: + self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self._socket.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1) + self._socket.connect((self._host, self._port)) + logger.info("Monitor connection established to %s:%d.", self._host, self._port) + + def close(self) -> None: + if self._socket is not None: + self._socket.close() + self._socket = None + + def send(self, msg: str) -> None: + data = msg.encode() + self._socket.send(len(data).to_bytes(4, byteorder="big") + data) + + def place_ball( + self, + pos: tuple[float, float, float], + vel: tuple[float, float, float] | None = None, + ) -> None: + msg = f"(ball (pos {pos[0]} {pos[1]} {pos[2]})" + if vel is not None: + msg += f" (vel {vel[0]} {vel[1]} {vel[2]})" + msg += ")" + self.send(msg) + + def drop_ball(self) -> None: + self.send("(dropBall)") + + def kick_off(self, side: str = "Left") -> None: + self.send(f"(kickOff {side})") + + def set_play_mode(self, mode: str) -> None: + self.send(f"(playMode {mode})") + + def place_player( + self, + unum: int, + team_name: str, + pos: tuple[float, float, float], + ) -> None: + self.send( + f"(agent (unum {unum}) (team {team_name}) (pos {pos[0]} {pos[1]} {pos[2]}))" + )