class GameState(): user_tile_type: TileState turn: TileState winner: TileState _state: List[TileState] _board_size: int _listener: Callable multi_palyer_connection: Connection = None def __init__(self, board_size: int, listener: Callable, is_server: bool = None) -> None: self._board_size = board_size self._state = [TileState.EMPTY] * (board_size * board_size) self._listener = listener self.winner = None self.turn = TileState.X self.user_tile_type = TileState.X self.setup_multiplayer_connection(is_server) def setup_multiplayer_connection(self, is_server: bool = None): if is_server != None: self.multi_palyer_connection = Connection() if is_server: self.multi_palyer_connection.serve("localhost", 8080) self.multi_palyer_connection.wait_client_thread.join() else: self.multi_palyer_connection.connect("localhost", 8080) self.user_tile_type = TileState.O self.multi_palyer_connection.recive_listener = self.multiplayer_event def multiplayer_event(self, payload): if payload['packet'] == Packets.TILE_CHANGE: tile_type = payload['tile_type'] x = payload['x'] y = payload['y'] self.set(x, y, tile_type, from_network=True) def new_game(self): self._state = [TileState.EMPTY] * (self._board_size * self._board_size) self.winner = None self._listener() def _index(self, x, y): return y * self._board_size + x def get(self, x: int, y: int): index = self._index(x, y) return self._state[index] def set(self, x: int, y: int, state: TileState, /, from_network=False):
class Protocol(): def __init__(self, client): self._client = client self._conn = None def connected(self): return self._conn != None def login(self, character_name, password): # Connect self._conn = Connection() self._conn.connect() # Send login packet login_packet = OutgoingPacket() login_packet.add_u8(0x0A) login_packet.add_u8(0) login_packet.add_u8(0) login_packet.add_u16(0) login_packet.add_u8(0) login_packet.add_string(character_name) login_packet.add_string(password) self._conn.send_packet(login_packet) # Receive login response while True: packet = self._conn.recv_packet() if packet: break time.sleep(0.1) packet_type = packet.get_u8() if packet_type == 0x14: error_message = packet.get_string() print("login: could not login: \"{}\"".format(error_message)) self._conn.close() self._conn = None return False elif packet_type == 0x0A: self._parse_login_packet(packet) return True else: print("login: unknown packet type: {}".format(packet_type)) self._conn.close() self._conn = None return False def logout(self): # Gracefully logout logout_packet = OutgoingPacket() logout_packet.add_u8(0x14) self._conn.send_packet(logout_packet) # Just wait for the socket to close by the server try: while True: handle_packet() except: self._conn = None def disconnect(self): # Just close the socket self._conn.close() self._conn = None def move(self, direction): packet = OutgoingPacket() packet.add_u8(0x65 + direction) self._conn.send_packet(packet) def handle_packet(self): packet = self._conn.recv_packet() if not packet: return False packet_type = packet.get_u8() #print("parse_packet: skipping unknown packet type: 0x{:02x}".format(packet_type)) return True # Helpers def _parse_position(self, packet): x = packet.get_u16() y = packet.get_u16() z = packet.get_u8() return x, y, z def _parse_item(self, packet): item_type_id = packet.get_u16() # TODO(simon): parse count if stackable & parse subtype if multitype return Item(item_type_id) def _parse_creature(self, packet): creature_type = packet.get_u16() if creature_type == 0x0061: # New creature packet.get_u32() # creature ids to remove creature_id = packet.get_u32() creature_name = packet.get_string() elif creature_type == 0x0062: # TODO(simon): Handle known creature raise Exception() else: print("Unknown creature_type: {}".format(creature_type)) raise Exception() # Skip health, direction, outfit, 0xDC00 ?? and speed packet.skip(11) return Creature(creature_id, creature_name) def _parse_tile(self, packet): tile = Tile() tile.ground = self._parse_item(packet) for stackpos in range(10): if packet.peek_u16() >= 0xFF00: packet.get_u16() return tile elif packet.peek_u16() == 0x0061 or packet.peek_u16() == 0x0062: tile.creatures.append(self._parse_creature(packet)) else: tile.items.append(self._parse_item(packet)) raise Exception() def _parse_login_packet(self, packet): self._client.player_id = packet.get_u32() packet.skip(2) tmp = packet.get_u8() if tmp != 0x64: print("Expected full map (0x64) but got: {}".format(tmp)) raise Exception() self._client.player_position = self._parse_position(packet) x, y, z = self._client.player_position for x_ in range(x - 9, x + 9): for y_ in range(y - 7, y + 7): self._client.tiles[(x_, y_)] = self._parse_tile(packet)