"""Utility for server administration via source rcon.""" import socket import struct from dataclasses import dataclass from loguru import logger @dataclass class RconPacket: # https://developer.valvesoftware.com/wiki/Source_RCON_Protocol#Basic_Packet_Structure size: int = None id: int = None type: int = None body: str = None terminator: bytes = b"\x00" def pack(self): body_encoded = ( self.body.encode("ascii") + self.terminator ) # The packet body field is a null-terminated string encoded in ASCII self.size = ( len(body_encoded) + 10 ) # Only value that can change is the length of the body, so do len(body) + 10. return ( struct.pack(" None: self.SERVER_IP = server_ip self.RCON_PORT = rcon_port self.RCON_PASSWORD = rcon_password def create_packet( self, command, request_id=1, type=RCONPacketType.SERVERDATA_EXECCOMMAND ): packet = RconPacket(id=request_id, type=type, body=command) final_packet = packet.pack() logger.debug(f"Final packet: {final_packet}") return final_packet def receive_all(self, sock, bytes_in: int = 4096): response = b"" while True: try: part = sock.recv(bytes_in) if not part: break response += part if len(part) < bytes_in: break except socket.error as e: logger.error(f"Error receiving data: {e}") break return response def decode_response(self, response): if len(response) < 12: return "Invalid response" size, request_id, type = struct.unpack("