def prepare_area_list(self, c: ClientManager.Client = None, from_area: AreaManager.Area = None) -> List[str]: """ Return the area list of the server. If given c and from_area, it will send an area list that matches the perspective of client `c` as if they were in area `from_area`. Parameters ---------- c: ClientManager.Client Client whose perspective will be taken into account, by default None from_area: AreaManager.Area Area from which the perspective will be considered, by default None Returns ------- list of str Area list that matches intended perspective. """ # Determine whether to filter the areas in the results need_to_check = (from_area is None or (c is not None and (c.is_staff() or c.is_transient))) # Now add areas prepared_area_list = list() for area in self.area_manager.areas: if need_to_check or area.name in from_area.visible_reachable_areas: prepared_area_list.append("{}-{}".format(area.id, area.name)) return prepared_area_list
def change_passage_lock(self, client: ClientManager.Client, areas: List[AreaManager.Area], bilock: bool = False, change_passage_visibility: bool = False): now_reachable = [] num_areas = 2 if bilock else 1 # First check if the player should be able to change the passage at all for i in range(num_areas): # First check if it is the case a non-authorized use is trying to change passages to # areas that do not allow their passages to be modified if not areas[ i].change_reachability_allowed and not client.is_staff(): raise AreaError( 'You must be authorized to change passages in area {}.'. format(areas[i].name)) # And make sure that non-authorized users cannot create passages they cannot see if ((not areas[1 - i].name in areas[i].reachable_areas) and not (client.is_staff() or areas[1 - i].name in areas[i].visible_reachable_areas)): raise AreaError( 'You must be authorized to create a new passage from {} to ' '{}.'.format(areas[i].name, areas[1 - i].name)) # If we are at this point, we are committed to changing the passage locks for i in range(num_areas): if areas[1 - i].name in areas[ i].reachable_areas: # Case removing a passage now_reachable.append(False) areas[i].reachable_areas -= {areas[1 - i].name} if change_passage_visibility: areas[i].visible_reachable_areas -= {areas[1 - i].name} else: # Case creating a passage now_reachable.append(True) areas[i].reachable_areas.add(areas[1 - i].name) if change_passage_visibility: areas[i].visible_reachable_areas.add(areas[1 - i].name) for client in areas[i].clients: client.reload_music_list() return now_reachable
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)