예제 #1
0
    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
예제 #2
0
    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
예제 #3
0
        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)