Exemple #1
0
 def add_jukebox_vote(self,
                      client: ClientManager.Client,
                      music_name: str,
                      length: int = -1,
                      showname: str = ''):
     """Cast a vote on the jukebox.
     Args:
         client (ClientManager.Client): Client that is requesting
         music_name (str): track name
         length (int, optional): length of track. Defaults to -1.
         showname (str, optional): showname of voter. Defaults to ''.
     """
     if not self.jukebox:
         return
     if length <= 0:
         self.remove_jukebox_vote(client, False)
     else:
         self.remove_jukebox_vote(client, True)
         self.jukebox_votes.append(
             self.JukeboxVote(client, music_name, length, showname))
         client.send_ooc('Your song was added to the jukebox.')
         if len(self.jukebox_votes) == 1:
             self.start_jukebox()
Exemple #2
0
        def add_to_shoutlog(self, client: ClientManager.Client, msg: str):
            """
            Add a shout message to the shout log of the area.

            Parameters
            ----------
            client: ClientManager.Client
                Client to record.
            msg: str
                Shout message to record.
            """

            if len(self.shoutlog) >= 20:
                self.shoutlog = self.shoutlog[1:]

            info = '{} | [{}] {} ({}) {}'.format(Constants.get_time(),
                                                 client.id, client.displayname,
                                                 client.get_ip(), msg)
            self.shoutlog.append(info)
Exemple #3
0
 def navigate_testimony(self,
                        client: ClientManager.Client,
                        command: str,
                        index: int = None) -> bool:
     """
     Navigate the current testimony using the commands >, <, =, and [>|<]<index>.
     Args:
         client (ClientManager.Client): requester
         command (str): either >, <, or =
         index (int): index of the statement to move to, or None
     Returns:
         bool: if the navigation was successful
     """
     if len(self.testimony.statements) <= 1:
         client.send_ooc('Testimony is empty, can\'t navigate!'
                         )  # should never happen
         return False
     if index == None:
         if command == '=':
             if self.examine_index == 0:
                 self.examine_index = 1
         elif command == '>':
             if len(self.testimony.statements
                    ) <= self.examine_index + 1:
                 self.broadcast_ooc(
                     'Reached end of testimony, looping...')
                 self.examine_index = 1
             else:
                 self.examine_index = self.examine_index + 1
         elif command == '<':
             if self.examine_index <= 1:
                 client.send_ooc(
                     'Can\'t go back, already on the first statement!')
                 return False
             else:
                 self.examine_index = self.examine_index - 1
     else:
         try:
             self.examine_index = int(index)
         except ValueError:
             client.send_ooc(
                 "That does not look like a valid statement number!")
             return False
     self.send_command('MS',
                       *self.testimony.statements[self.examine_index])
     return True
Exemple #4
0
 def remove_statement(self, client: ClientManager.Client, index: int) -> bool:
     """
     Remove the statement at <index>.
     Args:
         client (ClientManager.Client): requester
         index (int): index of the statement to remove
     Returns:
         bool: whether the statement was removed
     """
     if client not in self.owners and (self.evidence_mod == "HiddenCM" or self.evidence_mod == "Mods"):
         client.send_ooc('You don\'t have permission to amend testimony in this area!')
         return False
     if self.testimony.remove_statement(index):
         client.send_ooc('Removed statement ' + str(index) + ' successfully.')
         return True
     else:
         client.send_ooc('Couldn\'t remove statement ' + str(index) + '. Are you sure it exists?')
         return True
Exemple #5
0
 def amend_testimony(self, client: ClientManager.Client, index:int, statement: list) -> bool:
     """
     Replace the statement at <index> with a new <statement>.
     Args:
         client (ClientManager.Client): requester
         index (int): index of the statement to amend
         statement (list): the new statement
     Returns:
         bool: whether the statement was amended
     """
     if client not in self.owners and (self.evidence_mod == "HiddenCM" or self.evidence_mod == "Mods"):
         client.send_ooc('You don\'t have permission to amend testimony in this area!')
         return False
     if self.testimony.amend_statement(index, statement):
         client.send_ooc('Amended statement ' + str(index) + ' successfully.')
         return True
     else:
         client.send_ooc('Couldn\'t amend statement ' + str(index) + '. Are you sure it exists?')
         return False
Exemple #6
0
 def insert_testimony(self, client: ClientManager.Client, index:int, statement: list) -> bool:
     """
     Insert into the testimony a new <statement> after the statement at <index>.
     Args:
         client (ClientManager.Client): requester
         index (int): index of the statement to insert AFTER
         statement (list): the affected statement
     Returns:
         bool: whether the insert was successful
     """
     if client not in self.owners and (self.evidence_mod == "HiddenCM" or self.evidence_mod == "Mods"):
         client.send_ooc('You don\'t have permission to amend testimony in this area!')
         return False
     if self.testimony.insert_statement(index, statement):
         client.send_ooc('Inserted a new statement after statement ' + str(index) + ' successfully.')
         return True
     else:
         client.send_ooc('Couldn\'t find statement ' + str(index) + '. Are you sure it exists?')
         return False
Exemple #7
0
 def start_examination(self, client: ClientManager.Client) -> bool:
     """
     Start an examination of this area's testimony.
     Args:
         client (ClientManager.Client): requester
     Returns:
         bool: whether the examination was started
     """
     if client not in self.owners and (self.evidence_mod == "HiddenCM" or self.evidence_mod == "Mods"):
         client.send_ooc('You don\'t have permission to start a new examination in this area!')
         return False
     elif self.is_testifying:
         client.send_ooc('You can\'t start an examination during a testimony! (Hint: Say \'/end\' to stop recording!)')
         return False
     elif self.is_examining:
         client.send_ooc('You can\'t start an examination until you finish this one!')
         return False
     self.examine_index = 0
     self.is_examining = True
     self.send_command('RT', 'testimony2')
     return True
Exemple #8
0
        def _cleanup_removed_player(self, player: ClientManager.Client):
            self.listener.unsubscribe(player)
            # Restore their gamemode if needed (if player moved to a new zone, this zone will
            # be in charge of updating the gamemode)
            if not player.area.in_zone or player.area.in_zone == self:
                player.send_gamemode(name='')

            # Remove handicap
            if self.is_property('Handicap'):
                # Avoid double notification
                try:
                    player.change_handicap(False)
                except ClientError:
                    # If the player no longer had a handicap, no need to do anything
                    # This can happen if /unhandicap was run with a client in an area part of
                    # a zone with a handicap
                    pass

            # Remove chat tick rate
            if self.is_property('Chat_tick_rate'):
                player.send_chat_tick_rate(chat_tick_rate=None)
Exemple #9
0
        def _add_player(self, user: ClientManager.Client):
            if user in self._players:
                raise ZoneError.PlayerConflictError(
                    'User is already a player in the zone.')
            if user.area not in self._areas:
                raise ZoneError.PlayerNotInZoneError(
                    'User is in an area not part of the zone.')

            self._players.add(user)
            self.listener.subscribe(user)

            user.send_gamemode(name=self.get_mode())

            if self.is_property('Handicap'):
                length, name, announce_if_over = self.get_property('Handicap')
                user.change_handicap(True,
                                     length=length,
                                     name=name,
                                     announce_if_over=announce_if_over)

            if self.is_property('Chat_tick_rate'):
                chat_tick_rate = self.get_property('Chat_tick_rate')
                user.send_chat_tick_rate(chat_tick_rate=chat_tick_rate)
Exemple #10
0
        def new_client(self, client: ClientManager.Client):
            """Add a client to the area."""
            self.clients.add(client)
            self.server.area_manager.send_arup_players()
            if client.char_id != -1:
                database.log_room('area.join', client, self)

            # Update the timers
            timer = self.server.area_manager.timer
            if timer.set:
                s = int(not timer.started)
                current_time = timer.static
                if timer.started:
                    current_time = timer.target - arrow.get()
                int_time = int(current_time.total_seconds()) * 1000
                # Unhide the timer
                client.send_command('TI', 0, 2)
                # Start the timer
                client.send_command('TI', 0, s, int_time)
            else:
                # Stop the timer
                client.send_command('TI', 0, 3, 0)
                # Hide the timer
                client.send_command('TI', 0, 1)

            for timer_id, timer in enumerate(self.timers):
                # Send static time if applicable
                if timer.set:
                    s = int(not timer.started)
                    current_time = timer.static
                    if timer.started:
                        current_time = timer.target - arrow.get()
                    int_time = int(current_time.total_seconds()) * 1000
                    # Start the timer
                    client.send_command('TI', timer_id + 1, s, int_time)
                    # Unhide the timer
                    client.send_command('TI', timer_id + 1, 2)
                    client.send_ooc(f'Timer {timer_id+1} is at {current_time}')
                else:
                    # Stop the timer
                    client.send_command('TI', timer_id + 1, 1, 0)
                    # Hide the timer
                    client.send_command('TI', timer_id + 1, 3)
Exemple #11
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)
Exemple #12
0
        def change_lights(self,
                          new_lights: bool,
                          initiator: ClientManager.Client = None,
                          area: AreaManager.Area = None):
            """
            Change the light status of the area and send related announcements.

            This also updates the light status for parties.

            Parameters
            ----------
            new_lights: bool
                New light status
            initiator: server.ClientManager.Client, optional
                Client who triggered the light status change.
            area: server.AreaManager.Area, optional
                Broadcasts light change messages to chosen area. Used if
                the initiator is elsewhere, such as in /zone_lights.
                If not None, the initiator will receive no notifications of
                light status changes.

            Raises
            ------
            AreaError
                If the new light status matches the current one.
            """

            status = {True: 'on', False: 'off'}
            if self.lights == new_lights:
                raise AreaError('The lights are already turned {}.'.format(
                    status[new_lights]))

            # Change background to match new status
            if new_lights:
                if self.background == self.server.config[
                        'blackout_background']:
                    intended_background = self.background_backup
                else:
                    intended_background = self.background
            else:
                if self.background != self.server.config['blackout_background']:
                    self.background_backup = self.background
                intended_background = self.background

            self.lights = new_lights
            self.change_background(
                intended_background,
                validate=False)  # Allow restoring custom bg.

            # Announce light status change
            if initiator:  # If a player initiated the change light sequence, send targeted messages
                if area is None:
                    if not initiator.is_blind:
                        initiator.send_ooc('You turned the lights {}.'.format(
                            status[new_lights]))
                    elif not initiator.is_deaf:
                        initiator.send_ooc('You hear a flicker.')
                    else:
                        initiator.send_ooc(
                            'You feel a light switch was flipped.')

                initiator.send_ooc_others('The lights were turned {}.'.format(
                    status[new_lights]),
                                          is_zstaff_flex=False,
                                          in_area=area if area else True,
                                          to_blind=False)
                initiator.send_ooc_others('You hear a flicker.',
                                          is_zstaff_flex=False,
                                          in_area=area if area else True,
                                          to_blind=True,
                                          to_deaf=False)
                initiator.send_ooc_others(
                    '(X) {} [{}] turned the lights {}.'.format(
                        initiator.displayname, initiator.id,
                        status[new_lights]),
                    is_zstaff_flex=True,
                    in_area=area if area else True)
            else:  # Otherwise, send generic message
                self.broadcast_ooc('The lights were turned {}.'.format(
                    status[new_lights]))

            # Notify the parties in the area that the lights have changed
            for party in self.parties:
                party.check_lights()

            for c in self.clients:
                found_something = c.area_changer.notify_me_rp(
                    self, changed_visibility=True, changed_hearing=False)
                if found_something and new_lights:
                    c.send_ic_attention()
Exemple #13
0
    def notify_others_blood(self, client: ClientManager.Client, area: AreaManager.Area,
                            char: str, status: str = 'stay', send_to_staff: bool = True):
        # Assume client's bleeding status is worth announcing (for example, it changed or lights on)
        # If bleeding, send reminder, and notify everyone in the area if not sneaking
        # (otherwise, just send vague message).
        others_bleeding = len([c for c in area.clients if c.is_bleeding and c != client])

        if client.is_bleeding and (status == 'stay' or status == 'arrived'):
            discriminant = (others_bleeding > 0) # Check if someone was bleeding already

            dsh = {True: 'You start hearing more drops of blood.',
                   False: 'You faintly start hearing drops of blood.'}
            dshs = {True: 'You start hearing and smelling more drops of blood.',
                    False: 'You faintly start hearing and smelling drops of blood.'}
            dss = {True: 'You start smelling more blood.',
                   False: 'You faintly start smelling blood.'}

            vis_status = 'now'
        elif ((client.is_bleeding and status == 'left')
              or (not client.is_bleeding and status == 'stay')):
            discriminant = (others_bleeding == 0) # Check if no one else in area was bleeding
            dsh = {True: 'You stop hearing drops of blood.',
                   False: 'You start hearing less drops of blood.'}
            dshs = {True: 'You stop hearing and smelling drops of blood.',
                    False: 'You start hearing and smelling less drops of blood.'}
            dss = {True: 'You stop smelling blood.',
                   False: 'You start smelling less blood.'}

            vis_status = 'no longer'
        else:
            # Case client is not bleeding and status is left or arrived (or anything but 'stay')
            # Boring cases for which the function should not be called
            raise KeyError('Invalid call of notify_others_blood with client {}. Bleeding: {}.'
                           'Status: {}'.format(client, client.is_bleeding, status))

        h_mes = dsh[discriminant] # hearing message
        s_mes = dss[discriminant] # smelling message
        hs_mes = dshs[discriminant] # hearing and smelling message
        ybyd = hs_mes
        darkened = 'darkened ' if not area.lights else ''

        if status == 'stay':
            connector = 'is {}'.format(vis_status)
            pconnector = 'was {}'.format(vis_status)
        elif status == 'left':
            connector = 'leave the {}area while still'.format(darkened)
            pconnector = 'left the {}area while still'.format(darkened)
        elif status == 'arrived':
            connector = 'arrive to the {}area while'.format(darkened)
            pconnector = 'arrived to the {}area while'.format(darkened)

        if client.is_visible and area.lights:
            norm = 'You see {} {} bleeding.'.format(char, connector)
            ybnd = h_mes
            nbyd = norm
            staff = norm
        elif not client.is_visible and area.lights:
            norm = h_mes
            ybnd = hs_mes
            nbyd = s_mes
            staff = '(X) {} {} bleeding and sneaking.'.format(char, pconnector)
        elif client.is_visible and not area.lights:
            norm = hs_mes
            ybnd = hs_mes
            nbyd = s_mes
            staff = '(X) {} {} bleeding.'.format(char, pconnector)
        elif not client.is_visible and not area.lights:
            norm = hs_mes
            ybnd = hs_mes
            nbyd = s_mes
            staff = ('(X) {} {} bleeding and sneaking.'.format(char, pconnector))

        staff = staff.replace('no longer bleeding and sneaking.',
                              'no longer bleeding, but is still sneaking.') # Ugly

        client.send_ooc_others(norm, is_zstaff_flex=False, in_area=area, to_blind=False,
                               to_deaf=False)
        client.send_ooc_others(ybnd, is_zstaff_flex=False, in_area=area, to_blind=True,
                               to_deaf=False)
        client.send_ooc_others(nbyd, is_zstaff_flex=False, in_area=area, to_blind=False,
                               to_deaf=True)
        client.send_ooc_others(ybyd, is_zstaff_flex=False, in_area=area, to_blind=True,
                               to_deaf=True)
        if send_to_staff:
            client.send_ooc_others(staff, is_zstaff_flex=True, in_area=area)
Exemple #14
0
    def notify_others_moving(self, client: ClientManager.Client, area: AreaManager.Area,
                             autopass_mes: str, blind_mes: str):
        staff = nbnd = ybnd = nbyd = '' # nbnd = notblindnotdeaf ybnd=yesblindnotdeaf

        # Autopass: at most footsteps if no lights
        # No autopass: at most footsteps if no lights
        # Blind: at most footsteps
        # Deaf: can hear autopass but not footsteps
        # No lights: at most footsteps

        # Remove trailing periods and add it again. This helps prevent duplicate periods.
        autopass_mes = autopass_mes[:-1] if autopass_mes.endswith('.') else autopass_mes

        if client.autopass:
            staff = autopass_mes + '.'
            nbnd = autopass_mes + '.'
            ybnd = blind_mes
            nbyd = autopass_mes + '.'
        else:
            staff = '(X) {} (no autopass).'.format(autopass_mes)

        if not area.lights:
            staff = '(X) {} while the lights were out.'.format(autopass_mes)
            nbnd = blind_mes
            ybnd = blind_mes
            nbyd = ''
        if not client.is_visible: # This should be the last statement
            staff = '(X) {} while sneaking.'.format(autopass_mes)
            nbnd = ''
            ybnd = ''
            nbyd = ''

        if client.autopass:
            client.send_ooc_others(staff, in_area=area, is_zstaff_flex=True)
        else:
            client.send_ooc_others(staff, in_area=area, is_zstaff_flex=True,
                                   pred=lambda c: c.get_nonautopass_autopass)
            client.send_ooc_others(nbnd, in_area=area, is_zstaff_flex=True,
                                   pred=lambda c: not c.get_nonautopass_autopass)

        client.send_ooc_others(nbnd, in_area=area, is_zstaff_flex=False, to_blind=False,
                               to_deaf=False)
        client.send_ooc_others(ybnd, in_area=area, is_zstaff_flex=False, to_blind=True,
                               to_deaf=False)
        client.send_ooc_others(nbyd, in_area=area, is_zstaff_flex=False, to_blind=False,
                               to_deaf=True)
Exemple #15
0
 def _cleanup_removed_watcher(self, user: ClientManager.Client):
     user.zone_watched = None