async def _check_session_key(self): Logger.info('[Auth Session Manager]: checking session key') session_key = await RedisConnection.create().get('#{}-session-key'.format(self.account_name)) if not session_key: raise Exception('Session key does not exists') self.session_key = b64decode(session_key)
def run(): Logger.info('[Web Server]: init') app = web.Application() app.router.add_routes([web.get('/', WebServer.websocket_handler)]) web.run_app(app, host=Config.WebServer.Connection.host, port=Config.WebServer.Connection.port)
async def process(self) -> tuple: data = self._parse_packet() try: with PlayerManager() as player_mgr: player_mgr.new(name=data['name'], race=data['race'], char_class=data['char_class'], gender=data['gender'], skin=data['skin'], face=data['face'], hair_style=data['hair_style'], hair_color=data['hair_color'], facial_hair=data['facial_hair'], account=self.connection.account) Logger.notify(f'Character \"{data["name"]}\" created') response = pack( '<B', CharCreateResponseCode.CHAR_CREATE_SUCCESS.value) return WorldOpCode.SMSG_CHAR_CREATE, [response] except IntegrityError: response = pack( '<B', CharCreateResponseCode.CHAR_CREATE_NAME_IN_USE.value) return WorldOpCode.SMSG_CHAR_CREATE, [response]
async def authenticate_on_login_server(self): while True: try: request = await asyncio.wait_for(self.reader.read(1024), timeout=0.01) if request: opcode, packet = request[0], request[1:] try: handler = AuthManager.AUTH_HANDLERS[LoginOpCode( opcode)] except ValueError: Logger.error( '[AuthManager]: Incorrect request, check the opcode' ) pass else: response = await handler( packet=packet, srp=self.srp, temp_ref=self.temp_ref).process() if response: self.writer.write(response) except TimeoutError: pass finally: await asyncio.sleep(0.01)
async def send_update_packet_to_player(self): while True: try: player_name, update_packets = await asyncio.wait_for( QueuesRegistry.update_packets_queue.get(), timeout=0.01) except TimeoutError: pass else: try: writer = self.connections[player_name]['writer'] header_crypt = self.connections[player_name][ 'header_crypt'] except KeyError: # on login step player object not registered in self.connections, # just ignore pass else: try: for update_packet in update_packets: response = WorldPacketManager.generate_packet( opcode=WorldOpCode.SMSG_UPDATE_OBJECT, data=update_packet, header_crypt=header_crypt) Logger.test( 'Send update packet to {}'.format(player_name)) writer.write(response) await writer.drain() except BrokenPipeError: del self.connections[player_name] except ConnectionResetError: del self.connections[player_name] finally: await asyncio.sleep(0.01)
async def process(self): Logger.debug('[Login Challenge]: processing') self._parse_data() try: with AccountManager() as account_mgr: account = account_mgr.get(name=self.account_name).account if account is None: raise Exception('Account \'{}\' is not found'.format(self.account_name)) account.os = self.os account.ip = '.'.join([str(i) for i in self.ip_addr]) account.platform = self.platform account.timezone = self.timezone account.locale = self.locale account_mgr.update() self.account = account self.temp_ref.account = account # TODO: define account exceptions except Exception as e: Logger.error('[Login Challenge]: error = {}'.format(e)) return None finally: return self._get_response()
def load_world_data(): def get_command(db_name): return 'mysql -h{host} -u"{username}" -p"{password}" -D"{db_name}"'.format( host=Config.Database.Connection.host, username=Config.Database.Connection.username, password=Config.Database.Connection.password, db_name=db_name) for key in MAP_DIRNAME_TO_DBNAME.keys(): files = [] command = get_command(db_name=MAP_DIRNAME_TO_DBNAME[key]['db_name']) fixture_dir = MAP_DIRNAME_TO_DBNAME[key]['dir_name'] for (dirpath, _, filenames) in walk('DB/Fixtures/{}'.format(fixture_dir)): filenames.sort() for file in filenames: files.append(path.join(dirpath, file)) for file in files: Logger.notify('Start loading {}'.format(file)) subprocess.run('{} < {}'.format(command, file), shell=True) Logger.success('{} successfully imported'.format(file))
def _setup_encryption(self): Logger.info('[Auth Manager]: setup encryption') try: header_crypt = HeaderCrypt(self.session_key) except Exception as e: raise Exception('[Auth Manager]: error on setup encryption = {}'.format(e)) else: self.world_packet_manager.set_header_crypt(header_crypt)
def send_auth_challenge(self): # auth seed need to generate header_crypt Logger.info('[Auth Manager]: sending auth challenge') self.auth_seed = int.from_bytes(urandom(4), 'little') auth_seed_bytes = pack('<I', self.auth_seed) # TODO: make it like standard request handler response = WorldPacketManager.generate_packet(WorldOpCode.SMSG_AUTH_CHALLENGE, auth_seed_bytes) self.writer.write(response)
def _get_response(self): try: response = pack('<2B20sQ2B', LoginOpCode.LOGIN_PROOF.value, LoginResult.SUCCESS.value, self.srp.server_proof, 0x00800000, 0x00, 0x00) except Exception as e: Logger.error('[Login Proof]: {}'.format(e)) else: return response
def _parse_data(self): try: parsed_data = unpack(LoginProof.LOGIN_PROOF_FORMAT, self.packet) self.client_ephemeral = int.from_bytes(parsed_data[0], 'little') self.client_proof = parsed_data[1] self.checksum = parsed_data[2] self.unk = parsed_data[3] except StructError as e: Logger.error('[Login Proof]: on unpacking data(len={}), error={}'.format(len(self.packet), StructError))
async def handle_connection(self, reader: StreamReader, writer: StreamWriter): peername = writer.get_extra_info('peername') Logger.info('[Login Server]: Accepted connection from {}'.format(peername)) temp_ref = TempRef() auth = AuthManager(reader, writer, temp_ref=temp_ref) await auth.process(step=AuthStep.FIRST) writer.close()
async def run(self): while True: self.last_update = Timer.get_ms_time() try: await asyncio.wait_for(self.update(), timeout=1.0) except TimeoutError: Logger.warning('[World Manager]: Timeout...') finally: await asyncio.sleep(self.heartbeat)
def delete(self, **kwargs): if kwargs: try: self.session.query(Account).filter_by(**kwargs).delete() except Exception as e: Logger.error( '[AccountManager]: Error has occured on account delete, {}' .format(e)) return self
def get(self, **kwargs): if kwargs: try: self.account = self.session.query(Account).filter_by( **kwargs).first() except Exception as e: Logger.error( '[AccountManager]: Error has occured, account will be None, error: {}' .format(e)) return self
def set_default_equipment(self): self.session.expunge(self.player.region) self.session.expunge(self.player.account) self.session.expunge(self.player) with EquipmentManager() as equipment_mgr: try: self.equipment = equipment_mgr.set_default_equipment( player=self.player).get_items() except Exception as e: Logger.error('[PlayerMgr]: equipment error = {}'.format(e))
def _generate_server_hash(self): Logger.info( '[Auth Session Manager]: generating server hash for account "{}"'. format(self.account_name)) to_hash = (self.account_name.encode('ascii') + bytes(4) + self.client_seed + int.to_bytes(self.auth_seed, 4, 'little') + self.session_key) self.server_hash = sha1(to_hash).digest()
def guid(self): _guid = self.id if hasattr(self, 'low_guid'): Logger.test('has low guid') _guid = self.low_guid | (self.high_guid << 48) if bool(self.entry): _guid = (self.low_guid | (self.entry << 24) | (self.high_guid << 48)) return _guid
def _parse_account_name(buffer: BytesIO): Logger.info('[Character Create]: parsing account name') result = bytes() while True: char = buffer.read(1) if char and char != b'\x00': result += char else: break return result.decode('utf-8')
async def process(self): region_id = unpack('<I', self.packet[-4:])[0] if not self.temp_ref.player.region.region_id == region_id: region = RegionManager().get_region(region_id=region_id) self.temp_ref.player.region = region player_mgr = PlayerManager() player_mgr.set(self.temp_ref.player).save() Logger.notify('[Zone Update]: saving player') return None, None
async def process(self): self._load_player() if not self.temp_ref.player: Logger.error('[Player Init]: player not exists') raise PlayerNotExists await QueuesRegistry.connections_queue.put( (self.temp_ref.player.name, self.reader, self.writer, self.header_crypt)) await QueuesRegistry.players_queue.put(self.temp_ref.player) return None, None
async def process(self) -> tuple: # identifier = unpack('<I', self.data[-4:])[0] identifier = unpack('<I', self.data[:4])[0] if not self.connection.player.region.identifier == identifier: region = RegionManager().get_region(identifier=identifier) self.connection.player.region = region with PlayerManager() as player_mgr: player_mgr.set(self.connection.player).save() Logger.notify('[Zone Update]: saving player') return None, None
def _check_session_key(self): Logger.info('[Auth Session Manager]: checking session key') key = '#{}-session-key'.format(self.account_name) try: session_key = self.session_keys[key] except KeyError: Logger.error('[AuthMgr]: session with this key does not exists') self.writer.close() else: if not session_key: raise Exception('[AuthMgr]: Session key does not exists') self.session_key = b64decode(session_key)
async def remove_connection(self): while True: try: player_name = QueuesRegistry.disconnect_queue.get_nowait() except asyncio.QueueEmpty: pass else: if player_name and self.connections and player_name in self.connections: del self.connections[player_name] Logger.info( '[World Server]: player "{}" disconnected'.format( player_name)) finally: await asyncio.sleep(0.01)
async def accept_connection(self, websocket: WebSocketCommonProtocol): while True: try: request = await asyncio.wait_for(websocket.recv(), timeout=0.01) except TimeoutError: pass except ConnectionClosedOK: return else: parsed_request = json.loads(request) if 'type' in parsed_request and parsed_request['type'] == RequestType.SUBSCRIBE.value: Logger.success('[Websocket Server]: new connection was added, {}'.format(websocket.local_address)) self.connections.append(websocket) finally: await asyncio.sleep(1)
async def refresh_connections(self): while True: try: player_name, reader, writer, header_crypt = await asyncio.wait_for( QueuesRegistry.connections_queue.get(), timeout=1.0 ) except TimeoutError: pass else: self.connections[player_name] = { 'reader': reader, 'writer': writer, 'header_crypt': header_crypt } Logger.info('[World Server]: new connection for player "{}"'.format(player_name))
def _get_verify_login_packet(self): player = self.temp_ref.player if not player: Logger.error('[Login Verify]: player not exists') return None return pack( '<I4f', player.map_id, # map id player.x, # x player.y, # y player.z, # z player.orientation # orientation )
def load(self): current_dir = path.dirname(__file__) main_config = 'Config.yml' with open(path.join(current_dir, main_config), 'r') as stream: try: data = yaml.load(stream, Loader=yaml.Loader) self.config = json.loads( json.dumps(data), object_hook=lambda d: namedtuple('Configs', d.keys()) (*d.values())) except yaml.YAMLError as error: Logger.error('[Config Manager]: {}'.format(error)) else: return self
async def process(self): test_item1 = session.player.equipment.get_object_field(CharacterEquipSlot.BODY) test_item2 = session.player.equipment.get_object_field(CharacterEquipSlot.CHEST) test_item3 = session.player.equipment.get_object_field(CharacterEquipSlot.LEGS) test_item4 = session.player.equipment.get_object_field(CharacterEquipSlot.FEET) spawned_item = UpdatePacket(test_item1, ObjectUpdateType.CREATE_OBJECT.value, False) for field in self.SPAWN_FIELDS: spawned_item.add_field(field, test_item1.get_object_field(field)) response = spawned_item.update(send_packed_guid=True) Logger.error('[ItemSpawn]: {}'.format(response)) return WorldOpCode.SMSG_UPDATE_OBJECT, response
async def refresh_creatures(self): for region in self.regions: try: region_units = region.units.copy() except DetachedInstanceError as e: Logger.error('[Region Manager]: {}'.format(e)) else: units = region_units.copy() # finally building packet for player that contains unit list movement_flags = ( UpdateObjectFlags.UPDATEFLAG_HIGHGUID.value | UpdateObjectFlags.UPDATEFLAG_LIVING.value | UpdateObjectFlags.UPDATEFLAG_HAS_POSITION.value) spawn_dist = Config.World.Gameplay.spawn_dist online_players = region.get_online_players() if not spawn_dist == 0: for player_name in online_players: player = online_players[player_name] # list of unit managers each ready to build the update packet update_packets = [] if spawn_dist > 0: units = [ unit for unit in units if RegionManager._is_unit_in_spawn_radius( unit, player) ] for unit in units: with UnitManager() as unit_mgr: unit_mgr.set(unit) unit_mgr.movement.set_update_flags( movement_flags) batch_builder = unit_mgr.prepare() \ .build_update_packet(RegionManager.UNIT_SPAWN_FIELDS) update_packets.append(batch_builder) asyncio.ensure_future( QueuesRegistry.update_packets_queue.put( (player.name, update_packets)))