def is_iniswap(self, client: ClientManager.Client, anim1: str, anim2: str, char: str) -> bool: """ Decide if a client is iniswapping or using files outside their claimed character folder. Assumes that server permitted iniswaps do not count as iniswaps. Parameters ---------- client: ClientManager.Client Client to test. anim1: str Location of the preanimation the client used. anim2: str Location of the main animation the client used. char: str Name of the folder the client claims their files are. Returns ------- bool True if either anim1 or anim2 point to an external location through '../../' or their claimed character folder does not match the expected server name and the performed iniswap is not in the list of allowed iniswaps by the server. """ if char == client.get_char_name(): return False if '..' in anim1 or '..' in anim2: return True for char_link in self.server.allowed_iniswaps: if client.get_char_name() in char_link and char in char_link: return False return True
def play_track(self, name: str, client: ClientManager.Client, raise_if_not_found: bool = False, reveal_sneaked: bool = False, pargs: Dict[str, Any] = None): """ Wrapper function to play a music track in an area. Parameters ---------- name : str Name of the track to play client : ClientManager.Client Client who initiated the track change request. effect : int, optional Accompanying effect to the track (only used by AO 2.8.4+). Defaults to 0. raise_if_not_found : bool, optional If True, it will raise ServerError if the track name is not in the server's music list nor the client's music list. If False, it will not care about it. Defaults to False. reveal_sneaked : bool, optional If True, it will change the visibility status of the sender client to True (reveal them). If False, it will keep their visibility as it was. Defaults to False. pargs : dict of str to Any If given, they are arguments to an MC packet that was given when the track was requested, and will override any other arguments given. If not, this is ignored. Defaults to None (and converted to an empty dictionary). Raises ------ ServerError.FileInvalidNameError: If `name` references parent or current directories (e.g. "../hi.mp3") ServerError.MusicNotFoundError: If `name` is not a music track in the server or client's music list and `raise_if_not_found` is True. ServerError (with code 'FileInvalidName') If `name` references parent or current directories (e.g. "../hi.mp3") """ if not pargs: pargs = dict() if Constants.includes_relative_directories(name): info = f'Music names may not reference parent or current directories: {name}' raise ServerError.FileInvalidNameError(info) try: name, length, source = self.server.get_song_data(name, c=client) except ServerError.MusicNotFoundError: if raise_if_not_found: raise name, length, source = name, -1, '' if 'name' not in pargs: pargs['name'] = name if 'char_id' not in pargs: pargs['char_id'] = client.char_id pargs['showname'] = client.showname # Ignore AO shownames if 'loop' not in pargs: pargs['loop'] = -1 if 'channel' not in pargs: pargs['channel'] = 0 if 'effects' not in pargs: pargs['effects'] = 0 def loop(char_id): for client in self.clients: loop_pargs = pargs.copy() # Overwrite in case char_id changed (e.g., server looping) loop_pargs['char_id'] = char_id client.send_music(**loop_pargs) if self.music_looper: self.music_looper.cancel() if length > 0: f = lambda: loop(-1) # Server should loop now self.music_looper = asyncio.get_event_loop().call_later( length, f) loop(pargs['char_id']) # Record the character name and the track they played. self.current_music_player = client.displayname self.current_music = name self.current_music_source = source logger.log_server( '[{}][{}]Changed music to {}.'.format(self.id, client.get_char_name(), name), client) # Changing music reveals sneaked players, so do that if requested if not client.is_staff( ) and not client.is_visible and reveal_sneaked: client.change_visibility(True) client.send_ooc_others( '(X) {} [{}] revealed themselves by playing music ({}).'. format(client.displayname, client.id, client.area.id), is_zstaff=True)