def __init__(self, server, connection): super().__init__(connection) self.server = server self.socket.settimeout(self.RECV_TIMEOUT) self.world_packet_receiver = WorldPacketReceiver(self.socket) self.outgoing_queue = queue.Queue() self.shared_data = {} self.account = None self.session_cipher = None self.player = None
class WorldConnection(ConnectionAutomaton): """ Handle the communication between a client and the world server. Attributes: - world_packet_receiver: object that helps with world packet reception - outgoing_queue: a thread-safe queue with messages for that client, e.g. chat messages from other players. - shared_data: dict, holds misc temporary values that can be of use for several handlers; anything living longer than a few seconds should probably be stored somewhere else. - account: player account - session_cipher: current session cipher; this and the account attribute are set only when the AuthSessionHandler succeeds (when state >= AUTH_OK). - player: Player object for the currently player char. Set only once the PlayerLoginHandler verifies them, unset when client leaves world. """ AUTH_CHALLENGE_BIN = Struct("<I") LEGAL_OPS = { WorldConnectionState.INIT: [ OpCode.CMSG_AUTH_SESSION ], WorldConnectionState.ERROR: [ ], WorldConnectionState.AUTH_OK: [ OpCode.CMSG_CHAR_ENUM , OpCode.CMSG_CHAR_CREATE , OpCode.CMSG_CHAR_DELETE , OpCode.CMSG_PLAYER_LOGIN ] } UNMANAGED_OPS = [ OpCode.CMSG_PING ] UNMANAGED_STATES = [ WorldConnectionState.IN_WORLD ] DEFAULT_HANDLER = NopHandler OP_HANDLERS = { OpCode.CMSG_CHAR_CREATE: CharCreateHandler, OpCode.CMSG_CHAR_ENUM: CharEnumHandler, OpCode.CMSG_CHAR_DELETE: CharDeleteHandler, OpCode.CMSG_PLAYER_LOGIN: PlayerLoginHandler, OpCode.CMSG_LOGOUT_REQUEST: LogoutRequestHandler, OpCode.CMSG_NAME_QUERY: NameQueryHandler, OpCode.CMSG_MESSAGECHAT: MessageHandler, OpCode.CMSG_JOIN_CHANNEL: JoinChannelHandler, OpCode.CMSG_LEAVE_CHANNEL: LeaveChannelHandler, OpCode.MSG_MOVE_START_FORWARD: MovementHandler, OpCode.MSG_MOVE_START_BACKWARD: MovementHandler, OpCode.MSG_MOVE_STOP: MovementHandler, OpCode.MSG_MOVE_START_STRAFE_LEFT: MovementHandler, OpCode.MSG_MOVE_START_STRAFE_RIGHT: MovementHandler, OpCode.MSG_MOVE_STOP_STRAFE: MovementHandler, OpCode.MSG_MOVE_JUMP: MovementHandler, OpCode.MSG_MOVE_START_TURN_LEFT: MovementHandler, OpCode.MSG_MOVE_START_TURN_RIGHT: MovementHandler, OpCode.MSG_MOVE_STOP_TURN: MovementHandler, OpCode.MSG_MOVE_START_PITCH_UP: MovementHandler, OpCode.MSG_MOVE_START_PITCH_DOWN: MovementHandler, OpCode.MSG_MOVE_STOP_PITCH: MovementHandler, OpCode.MSG_MOVE_SET_RUN_MODE: MovementHandler, OpCode.MSG_MOVE_SET_WALK_MODE: MovementHandler, OpCode.MSG_MOVE_FALL_LAND: MovementHandler, OpCode.MSG_MOVE_SET_FACING: MovementHandler, OpCode.MSG_MOVE_WORLDPORT_ACK: MoveWorldportAckHandler, OpCode.MSG_MOVE_HEARTBEAT: MovementHandler, OpCode.CMSG_QUERY_TIME: TimeQueryHandler, OpCode.CMSG_PING: PingHandler, OpCode.CMSG_AUTH_SESSION: AuthSessionHandler, OpCode.CMSG_ZONEUPDATE: ZoneUpdateHandler, OpCode.CMSG_UPDATE_ACCOUNT_DATA: UpdateAccountDataHandler } INIT_STATE = WorldConnectionState.INIT END_STATES = [ WorldConnectionState.ERROR ] MAIN_ERROR_STATE = WorldConnectionState.ERROR RECV_TIMEOUT = float(CONFIG["world"]["recv_timeout"]) def __init__(self, server, connection): super().__init__(connection) self.server = server self.socket.settimeout(self.RECV_TIMEOUT) self.world_packet_receiver = WorldPacketReceiver(self.socket) self.outgoing_queue = queue.Queue() self.shared_data = {} self.account = None self.session_cipher = None self.player = None def set_session_cipher(self, session_cipher): self.session_cipher = session_cipher self.world_packet_receiver.session_cipher = self.session_cipher def _recv_packet(self): try: packet = self.world_packet_receiver.get_next_packet() return packet except ConnectionResetError: LOG.info("Lost connection with " + self.account.name + ".") return None def _parse_packet(self, packet): return packet.opcode, packet.data def send_packet(self, world_packet): ready_packet = world_packet.to_socket(self.session_cipher) self.socket.sendall(ready_packet) def _actions_before_main_loop(self): LOG.debug("Sending auth challenge to setup session cipher.") self._send_auth_challenge() def _send_auth_challenge(self): auth_seed = int.from_bytes(os.urandom(4), "little") self.shared_data["auth_seed"] = auth_seed packet_data = self.AUTH_CHALLENGE_BIN.pack(auth_seed) packet = WorldPacket(OpCode.SMSG_AUTH_CHALLENGE, packet_data) self.send_packet(packet) def _actions_at_loop_begin(self): while not self.outgoing_queue.empty(): try: packet = self.outgoing_queue.get(block = False) except queue.Empty: return self.send_packet(packet) def _actions_after_main_loop(self): LOG.debug("WorldConnection: session ended.") if self.account and self.session_cipher: AccountSessionManager.delete_session(self.account) if self.player: self.unset_player() with self.server.world_connections_lock: self.server.world_connections.remove(self) def set_player(self, char_data): """ Ask the ObjectManager to create a Player object with the char_data from the database. """ self.player = self.server.object_manager.add_player(char_data) def unset_player(self): """ Transfer the Player data back to the database, after a logout or after the connection has been closed. """ self.server.object_manager.remove_player(self.player.guid) self.player = None
class WorldConnection(ConnectionAutomaton): """ Handle the communication between a client and the world server. Attributes: - world_packet_receiver: object that helps with world packet reception - outgoing_queue: a thread-safe queue with messages for that client, e.g. chat messages from other players. - shared_data: dict, holds misc temporary values that can be of use for several handlers; anything living longer than a few seconds should probably be stored somewhere else. - account: player account - session_cipher: current session cipher; this and the account attribute are set only when the AuthSessionHandler succeeds (when state >= AUTH_OK). - player: Player object for the currently player char. Set only once the PlayerLoginHandler verifies them, unset when client leaves world. """ AUTH_CHALLENGE_BIN = Struct("<I") LEGAL_OPS = { WorldConnectionState.INIT: [OpCode.CMSG_AUTH_SESSION], WorldConnectionState.ERROR: [], WorldConnectionState.AUTH_OK: [ OpCode.CMSG_CHAR_ENUM, OpCode.CMSG_CHAR_CREATE, OpCode.CMSG_CHAR_DELETE, OpCode.CMSG_PLAYER_LOGIN ] } UNMANAGED_OPS = [OpCode.CMSG_PING] UNMANAGED_STATES = [WorldConnectionState.IN_WORLD] DEFAULT_HANDLER = NopHandler OP_HANDLERS = { OpCode.CMSG_CHAR_CREATE: CharCreateHandler, OpCode.CMSG_CHAR_ENUM: CharEnumHandler, OpCode.CMSG_CHAR_DELETE: CharDeleteHandler, OpCode.CMSG_PLAYER_LOGIN: PlayerLoginHandler, OpCode.CMSG_LOGOUT_REQUEST: LogoutRequestHandler, OpCode.CMSG_NAME_QUERY: NameQueryHandler, OpCode.CMSG_MESSAGECHAT: MessageHandler, OpCode.CMSG_JOIN_CHANNEL: JoinChannelHandler, OpCode.CMSG_LEAVE_CHANNEL: LeaveChannelHandler, OpCode.MSG_MOVE_START_FORWARD: MovementHandler, OpCode.MSG_MOVE_START_BACKWARD: MovementHandler, OpCode.MSG_MOVE_STOP: MovementHandler, OpCode.MSG_MOVE_START_STRAFE_LEFT: MovementHandler, OpCode.MSG_MOVE_START_STRAFE_RIGHT: MovementHandler, OpCode.MSG_MOVE_STOP_STRAFE: MovementHandler, OpCode.MSG_MOVE_JUMP: MovementHandler, OpCode.MSG_MOVE_START_TURN_LEFT: MovementHandler, OpCode.MSG_MOVE_START_TURN_RIGHT: MovementHandler, OpCode.MSG_MOVE_STOP_TURN: MovementHandler, OpCode.MSG_MOVE_START_PITCH_UP: MovementHandler, OpCode.MSG_MOVE_START_PITCH_DOWN: MovementHandler, OpCode.MSG_MOVE_STOP_PITCH: MovementHandler, OpCode.MSG_MOVE_SET_RUN_MODE: MovementHandler, OpCode.MSG_MOVE_SET_WALK_MODE: MovementHandler, OpCode.MSG_MOVE_FALL_LAND: MovementHandler, OpCode.MSG_MOVE_SET_FACING: MovementHandler, OpCode.MSG_MOVE_WORLDPORT_ACK: MoveWorldportAckHandler, OpCode.MSG_MOVE_HEARTBEAT: MovementHandler, OpCode.CMSG_QUERY_TIME: TimeQueryHandler, OpCode.CMSG_PING: PingHandler, OpCode.CMSG_AUTH_SESSION: AuthSessionHandler, OpCode.CMSG_ZONEUPDATE: ZoneUpdateHandler, OpCode.CMSG_UPDATE_ACCOUNT_DATA: UpdateAccountDataHandler } INIT_STATE = WorldConnectionState.INIT END_STATES = [WorldConnectionState.ERROR] MAIN_ERROR_STATE = WorldConnectionState.ERROR RECV_TIMEOUT = float(CONFIG["world"]["recv_timeout"]) def __init__(self, server, connection): super().__init__(connection) self.server = server self.socket.settimeout(self.RECV_TIMEOUT) self.world_packet_receiver = WorldPacketReceiver(self.socket) self.outgoing_queue = queue.Queue() self.shared_data = {} self.account = None self.session_cipher = None self.player = None def set_session_cipher(self, session_cipher): self.session_cipher = session_cipher self.world_packet_receiver.session_cipher = self.session_cipher def _recv_packet(self): try: packet = self.world_packet_receiver.get_next_packet() return packet except ConnectionResetError: LOG.info("Lost connection with " + self.account.name + ".") return None def _parse_packet(self, packet): return packet.opcode, packet.data def send_packet(self, world_packet): ready_packet = world_packet.to_socket(self.session_cipher) self.socket.sendall(ready_packet) def _actions_before_main_loop(self): LOG.debug("Sending auth challenge to setup session cipher.") self._send_auth_challenge() def _send_auth_challenge(self): auth_seed = int.from_bytes(os.urandom(4), "little") self.shared_data["auth_seed"] = auth_seed packet_data = self.AUTH_CHALLENGE_BIN.pack(auth_seed) packet = WorldPacket(OpCode.SMSG_AUTH_CHALLENGE, packet_data) self.send_packet(packet) def _actions_at_loop_begin(self): while not self.outgoing_queue.empty(): try: packet = self.outgoing_queue.get(block=False) except queue.Empty: return self.send_packet(packet) def _actions_after_main_loop(self): LOG.debug("WorldConnection: session ended.") if self.account and self.session_cipher: AccountSessionManager.delete_session(self.account) if self.player: self.unset_player() with self.server.world_connections_lock: self.server.world_connections.remove(self) def set_player(self, char_data): """ Ask the ObjectManager to create a Player object with the char_data from the database. """ self.player = self.server.object_manager.add_player(char_data) def unset_player(self): """ Transfer the Player data back to the database, after a logout or after the connection has been closed. """ self.server.object_manager.remove_player(self.player.guid) self.player = None