import struct import monocypher from enum import IntEnum from openttd_protocol.wire.tcp import TCPProtocol from openttd_protocol.wire.read import read_uint8, read_string, read_uint16, read_uint32 from openttd_protocol.wire.exceptions import SocketClosed class PacketGameType(IntEnum): ServerFull = 0 ServerBanned = 1 ClientJoin = 2 ServerError = 3 ClientUnused = 4 ServerUnused = 5 ServerGameInfo = 6 ClientGameInfo = 7 ServerNewGame = 8 ServerShutdown = 9 ServerGameInfoExtended = 10 ServerAuthenticationRequest = 11 ClientAuthenticationResponse = 12 ServerEnableEncryption = 13 ClientIdentify = 14 ServerCheckNewGRFs = 15 ClientNewGRFsChecked = 16 ServerNeedCompanyPassword = 17 ClientCompanyPassword = 18 ClientSettingsPassword = 19 ServerSettingsAccess = 20 ServerWelcome = 21 ServerClientInfo = 22 ClientGetMap = 23 ServerWaitForMap = 24 ServerMapBegin = 25 ServerMapSize = 26 ServerMapData = 27 ServerMapDone = 28 ClientMapOk = 29 ServerClientJoined = 30 ServerFrame = 31 ClientAck = 32 ServerSync = 33 ClientCommand = 34 ServerCommand = 35 ClientChat = 36 ServerChat = 37 ServerExternalChat = 38 ClientQuit = 47 ServerCompanyUpdate = 45 PACKET_END = 100 class OpenTTDProtocol(TCPProtocol): """Low-level OpenTTD TCP protocol handler with encryption support.""" PacketType = PacketGameType PACKET_END = PacketGameType.PACKET_END def __init__(self, handler): super().__init__(handler) self.handler = handler def receive_packet(self, source, data): try: if self.handler.encryption_enabled: if not self.handler._recv_aead: self.handler._recv_aead = monocypher.IncrementalAuthenticatedEncryption(self.handler._session_key_recv, self.handler._encryption_nonce) length, rest = read_uint16(data) payload = self.handler._recv_aead.unlock(bytes(rest[:16]), bytes(rest[16:])) if payload is None: raise SocketClosed("Decryption failed") data = memoryview(struct.pack(" 0: if len(data) >= 13: data = data[12:] token, _ = read_uint8(data) return {"frame": f, "token": token} @staticmethod def receive_ServerChat(source, data): _, data = read_uint8(data) cid, data = read_uint32(data) _, data = read_uint8(data) msg, _ = read_string(data) return {"client_id": cid, "message": msg} @staticmethod def receive_ServerCompanyUpdate(source, data): mask, _ = read_uint16(data) return {"passworded_mask": mask} @staticmethod def receive_ServerMapDone(source, data): return {} @staticmethod def receive_ServerClientInfo(source, data): return {} @staticmethod def receive_ServerSync(source, data): return {} @staticmethod def receive_ServerClientJoined(source, data): return {} @staticmethod def receive_ServerMapBegin(source, data): return {} @staticmethod def receive_ServerMapSize(source, data): return {"size": 0} @staticmethod def receive_ServerMapData(source, data): return {"data": data} @staticmethod def receive_ServerConfigurationUpdate(source, data): return {} @staticmethod def receive_ServerExternalChat(source, data): return {} @staticmethod def receive_ServerCommand(source, data): return {} @staticmethod def receive_ServerFull(source, data): return {} @staticmethod def receive_ServerBanned(source, data): return {} @staticmethod def receive_ClientAck(source, data): f, data = read_uint32(data) t, _ = read_uint8(data) return {"frame": f, "token": t} @staticmethod def receive_ClientIdentify(source, data): return {}