Exemple #1
0
    def send_error_report(self, client: ClientManager.Client, cmd: str,
                          args: List[str], ex: Exception):
        """
        In case of an error caused by a client packet, send error report to user, notify moderators
        and have full traceback available on console and through /lasterror
        """

        # Send basic logging information to user
        info = (
            '=========\nThe server ran into a Python issue. Please contact the server owner '
            'and send them the following logging information:')
        etype, evalue, etraceback = sys.exc_info()
        tb = traceback.extract_tb(tb=etraceback)
        current_time = Constants.get_time()
        file, line_num, module, func = tb[-1]
        file = file[file.rfind('\\') + 1:]  # Remove unnecessary directories
        version = self.version
        info += '\r\n*Server version: {}'.format(version)
        info += '\r\n*Server time: {}'.format(current_time)
        info += '\r\n*Packet details: {} {}'.format(cmd, args)
        info += '\r\n*Client status: {}'.format(client)
        info += '\r\n*Area status: {}'.format(client.area)
        info += '\r\n*File: {}'.format(file)
        info += '\r\n*Line number: {}'.format(line_num)
        info += '\r\n*Module: {}'.format(module)
        info += '\r\n*Function: {}'.format(func)
        info += '\r\n*Error: {}: {}'.format(type(ex).__name__, ex)
        info += '\r\nYour help would be much appreciated.'
        info += '\r\n========='
        client.send_ooc(info)
        client.send_ooc_others(
            'Client {} triggered a Python error through a client packet. '
            'Do /lasterror to take a look at it.'.format(client.id),
            pred=lambda c: c.is_mod)

        # Print complete traceback to console
        info = 'TSUSERVERDR HAS ENCOUNTERED AN ERROR HANDLING A CLIENT PACKET'
        info += '\r\n*Server time: {}'.format(current_time)
        info += '\r\n*Packet details: {} {}'.format(cmd, args)
        info += '\r\n*Client status: {}'.format(client)
        info += '\r\n*Area status: {}'.format(client.area)
        info += '\r\n\r\n{}'.format("".join(
            traceback.format_exception(etype, evalue, etraceback)))
        logger.log_print(info)
        self.last_error = [info, etype, evalue, etraceback]

        # Log error to file
        logger.log_error(info, server=self, errortype='C')

        if self.in_test:
            raise ex
Exemple #2
0
    def notify_others_status(self, client: ClientManager.Client, area: AreaManager.Area,
                             name: str, status: str = 'stay'):
        # Assume client's special status is worth announcing
        # If client has custom status, send reminder in OOC to everyone but those not staff,
        # blind and deaf simultaneously.

        norm_mes =  f'You note something about {name} {{}}'
        vague_mes = 'You think there is something odd about someone'
        staff_mes = f'(X) {name} [{client.id}] {{}} and has a custom status: {client.status}'

        if status == 'stay':
            norm_mes = norm_mes.format(' who was already here')
            vague_mes += ' who was already here.'
            staff_mes = staff_mes.format('was already here{}')
        elif status == 'arrived':
            norm_mes = norm_mes.format(' who has just arrived')
            vague_mes += ' who has just arrived.'
            staff_mes = staff_mes.format('has just arrived{}')
        else:
            # Case status is left or anything else
            # Boring cases for which the function should not be called
            raise KeyError('Invalid call of notify_others_status with client {}. Player status: {}.'
                           'Status: {}'.format(client, client.status, status))

        if client.is_visible and area.lights:
            norm = norm_mes
            ybnd = vague_mes
            nbyd = norm_mes
            staff = staff_mes.format('')
        elif not client.is_visible and area.lights:
            norm = vague_mes
            ybnd = vague_mes
            nbyd = vague_mes
            staff = staff_mes.format(' while sneaking')
        elif client.is_visible and not area.lights:
            norm = vague_mes
            ybnd = vague_mes
            nbyd = vague_mes
            staff = staff_mes.format('')
        else:
            norm = vague_mes
            ybnd = vague_mes
            nbyd = vague_mes
            staff = staff_mes.format(' while sneaking')

        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(staff, is_zstaff_flex=True, in_area=area)
Exemple #3
0
        def _remove_player(self, user: ClientManager.Client):
            if user not in self._players:
                raise ZoneError.PlayerNotInZoneError(
                    'User {} is not a player of zone {}.'.format(user, self))

            self._players.remove(user)
            self._cleanup_removed_player(user)

            # If no more watchers nor players, delete the zone
            if not self._watchers and not self._players:
                self._server.zone_manager.delete_zone(self._zone_id)
                user.send_ooc(
                    '(X) Zone `{}` that you were in was automatically ended as no one '
                    'was in an area part of it or was watching it anymore.'.
                    format(self._zone_id),
                    is_staff=True)
                user.send_ooc_others(
                    'Zone `{}` was automatically ended as no one was in an '
                    'area part of it or was watching it anymore.'.format(
                        self._zone_id),
                    is_officer=True)
Exemple #4
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 #5
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 #6
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 #7
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)