Exemple #1
0
    def net_cmd_cc(self, args: List[str]):
        """ Character selection.

        CC#<client_id:int>#<char_id:int>#<client_hdid:string>#%

        """

        pargs = self.process_arguments('CC', args, needs_auth=False)
        self.client.publish_inbound_command('CC', pargs)
        # Check if client is ready to actually join, and did not do weird packet shenanigans before
        if self.client.required_packets_received != {'HI', 'ID'}:
            return

        char_id = pargs['char_id']

        ever_chose_character = self.client.ever_chose_character  # Store for later
        try:
            self.client.change_character(char_id)
        except ClientError:
            return
        self.client.last_active = Constants.get_time()

        if not ever_chose_character:
            self.client.send_command_dict('GM', {'name': ''})
            self.client.send_command_dict('TOD', {'name': ''})
Exemple #2
0
    def net_cmd_hp(self, args: List[str]):
        """ Sets the penalty bar.

        HP#<type:int>#<new_value:int>#%

        """

        pargs = self.process_arguments('HP', args)

        if self.client.is_muted:  # Checks to see if the client has been muted by a mod
            self.client.send_ooc("You have been muted by a moderator")
            return

        self.client.publish_inbound_command('HP', pargs)
        try:
            side, health = pargs['side'], pargs['health']
            self.client.area.change_hp(side, health)
            info = 'changed penalty bar {} to {}.'.format(side, health)
            self.client.area.add_to_judgelog(self.client, info)
            logger.log_server('[{}]{} changed HP ({}) to {}'
                              .format(self.client.area.id, self.client.get_char_name(),
                                      side, health), self.client)
        except AreaError:
            pass
        self.client.last_active = Constants.get_time()
Exemple #3
0
    def net_cmd_rt(self, args: List[str]):
        """ Plays the Testimony/CE animation.

        RT#<type:string>#%

        """

        pargs = self.process_arguments('RT', args)

        if self.client.is_muted:  # Checks to see if the client has been muted by a mod
            self.client.send_ooc('You have been muted by a moderator.')
            return
        if not self.client.is_staff() and self.client.area.lobby_area:
            self.client.send_ooc('Judge buttons are disabled in this area.')
            return

        self.client.publish_inbound_command('RT', pargs)
        name = pargs['name']

        for client in self.client.area.clients:
            client.send_splash(name=name)
        self.client.area.add_to_judgelog(self.client, 'used judge button {}.'.format(name))
        logger.log_server('[{}]{} used judge button {}.'
                          .format(self.client.area.id, self.client.get_char_name(), name),
                          self.client)
        self.client.last_active = Constants.get_time()
Exemple #4
0
    def net_cmd_mc(self, args):
        """ Play music.

        MC#<song_name:int>#<???:int>#%

        """
        # First attempt to switch area,
        # because music lists typically include area names for quick access
        try:
            delimiter = args[0].find('-')
            area = self.server.area_manager.get_area_by_name(
                args[0][delimiter + 1:])
            self.client.change_area(
                area, from_party=True if self.client.party else False)

        # Otherwise, attempt to play music.
        except (AreaError, ValueError):
            if self.client.is_muted:  # Checks to see if the client has been muted by a mod
                self.client.send_ooc("You have been muted by a moderator.")
                return
            if not self.client.is_dj:
                self.client.send_ooc('You were blockdj\'d by a moderator.')
                return
            # We have to use fallback protocols for AO2d6 like clients, because if for whatever
            # reason if they don't set an in-client showname, they send less arguments. In
            # particular, they behave like DRO.
            pargs = self.process_arguments(
                'MC', args, fallback_protocols=[Clients.ClientDRO])
            if not pargs:
                return

            if 'cid' not in pargs or int(pargs['cid']) != self.client.char_id:
                return
            if self.client.change_music_cd():
                self.client.send_ooc(
                    'You changed song too many times recently. Please try again '
                    'after {} seconds.'.format(
                        int(self.client.change_music_cd())))
                return

            try:
                self.client.area.play_track(pargs['name'],
                                            self.client,
                                            raise_if_not_found=True,
                                            reveal_sneaked=True,
                                            pargs=pargs)
            except ServerError.MusicNotFoundError:
                self.client.send_ooc('Unrecognized area or music `{}`.'.format(
                    args[0]))
            except ServerError:
                return
        except (ClientError, PartyError) as ex:
            self.client.send_ooc(ex)

        self.client.last_active = Constants.get_time()
Exemple #5
0
    def net_cmd_de(self, args):
        """ Deletes a piece of evidence.

        DE#<id: int>#%

        """

        self.client.area.evi_list.del_evidence(
            self.client, self.client.evi_list[int(args[0])])
        self.client.area.broadcast_evidence_list()
        self.client.last_active = Constants.get_time()
Exemple #6
0
    def net_cmd_pe(self, args):
        """ Adds a piece of evidence.

        PE#<name: string>#<description: string>#<image: string>#%

        """
        if len(args) < 3:
            return
#        evi = Evidence(args[0], args[1], args[2], self.client.pos)
        self.client.area.evi_list.add_evidence(self.client, args[0], args[1],
                                               args[2], 'all')
        self.client.area.broadcast_evidence_list()
        self.client.last_active = Constants.get_time()
Exemple #7
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 #8
0
    def net_cmd_de(self, args: List[str]):
        """ Deletes a piece of evidence.

        DE#<id: int>#%

        """

        pargs = self.process_arguments('DE', args)
        self.client.publish_inbound_command('DE', pargs)

        self.client.area.evi_list.del_evidence(self.client,
                                               self.client.evi_list[int(pargs['evi_id'])])
        self.client.area.broadcast_evidence_list()
        self.client.last_active = Constants.get_time()
Exemple #9
0
    def net_cmd_cc(self, args):
        """ Character selection.

        CC#<client_id:int>#<char_id:int>#<hdid:string>#%

        """
        if not self.validate_net_cmd(
                args, ArgType.INT, ArgType.INT, ArgType.STR, needs_auth=False):
            return
        cid = args[1]
        try:
            self.client.change_character(cid)
        except ClientError:
            return
        self.client.last_active = Constants.get_time()
Exemple #10
0
    def net_cmd_mc(self, args):
        """ Play music.

        MC#<song_name:int>#<???:int>#%

        """
        # First attempt to switch area,
        # because music lists typically include area names for quick access
        try:
            delimiter = args[0].find('-')
            area = self.server.area_manager.get_area_by_name(
                args[0][delimiter + 1:])
            self.client.change_area(
                area, from_party=True if self.client.party else False)

        # Otherwise, attempt to play music.
        except (AreaError, ValueError):
            if self.client.is_muted:  # Checks to see if the client has been muted by a mod
                self.client.send_ooc("You have been muted by a moderator.")
                return
            if not self.client.is_dj:
                self.client.send_ooc('You were blockdj\'d by a moderator.')
                return
            if not self.validate_net_cmd(args, ArgType.STR, ArgType.INT):
                return
            if args[1] != self.client.char_id:
                return
            if self.client.change_music_cd():
                self.client.send_ooc(
                    'You changed song too many times recently. Please try again '
                    'after {} seconds.'.format(
                        int(self.client.change_music_cd())))
                return

            try:
                self.client.area.play_track(args[0],
                                            self.client,
                                            raise_if_not_found=True,
                                            reveal_sneaked=True)
            except ServerError.MusicNotFoundError:
                self.client.send_ooc('Unrecognized area or music `{}`.'.format(
                    args[0]))
            except ServerError:
                return
        except (ClientError, PartyError) as ex:
            self.client.send_ooc(ex)

        self.client.last_active = Constants.get_time()
Exemple #11
0
    def net_cmd_ee(self, args):
        """ Edits a piece of evidence.

        EE#<id: int>#<name: string>#<description: string>#<image: string>#%

        """

        if len(args) < 4:
            return

        evi = (args[1], args[2], args[3], 'all')

        self.client.area.evi_list.edit_evidence(
            self.client, self.client.evi_list[int(args[0])], evi)
        self.client.area.broadcast_evidence_list()
        self.client.last_active = Constants.get_time()
Exemple #12
0
    def net_cmd_ee(self, args: List[str]):
        """ Edits a piece of evidence.

        EE#<id: int>#<name: string>#<description: string>#<image: string>#%

        """

        pargs = self.process_arguments('EE', args)
        self.client.publish_inbound_command('EE', pargs)

        evi = (pargs['name'], pargs['description'], pargs['image'], 'all')

        self.client.area.evi_list.edit_evidence(self.client,
                                                self.client.evi_list[int(pargs['evi_id'])], evi)
        self.client.area.broadcast_evidence_list()
        self.client.last_active = Constants.get_time()
Exemple #13
0
    def net_cmd_pe(self, args: List[str]):
        """ Adds a piece of evidence.

        PE#<name: string>#<description: string>#<image: string>#%

        """

        pargs = self.process_arguments('PE', args)
        self.client.publish_inbound_command('PE', pargs)

        # evi = Evidence(args[0], args[1], args[2], self.client.pos)
        self.client.area.evi_list.add_evidence(self.client,
                                               pargs['name'],
                                               pargs['description'],
                                               pargs['image'], 'all')
        self.client.area.broadcast_evidence_list()
        self.client.last_active = Constants.get_time()
Exemple #14
0
        def add_to_judgelog(self, client: ClientManager.Client, msg: str):
            """
            Add a judge action to the judge log of the area.

            Parameters
            ----------
            client: ClientManager.Client
                Client to record.
            msg: str
                Judge action to record.
            """

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

            info = '{} | [{}] {} ({}) {}'.format(Constants.get_time(),
                                                 client.id, client.displayname,
                                                 client.get_ip(), msg)
            self.judgelog.append(info)
Exemple #15
0
        def add_to_shoutlog(self, client, msg):
            """
            Add a shout message to the shout log of the area.

            Parameters
            ----------
            client: server.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 #16
0
    def net_cmd_rt(self, args):
        """ Plays the Testimony/CE animation.

        RT#<type:string>#%

        """
        if self.client.is_muted:  # Checks to see if the client has been muted by a mod
            self.client.send_ooc('You have been muted by a moderator.')
            return
        if not self.validate_net_cmd(args, ArgType.STR):
            return
        if not args[0].startswith('testimony'):
            return
        self.client.area.send_command('RT', args[0])
        self.client.area.add_to_judgelog(
            self.client, 'used judge button {}.'.format(args[0]))
        logger.log_server(
            '[{}]{} used judge button {}.'.format(self.client.area.id,
                                                  self.client.get_char_name(),
                                                  args[0]), self.client)
        self.client.last_active = Constants.get_time()
Exemple #17
0
    def net_cmd_hp(self, args):
        """ Sets the penalty bar.

        HP#<type:int>#<new_value:int>#%

        """
        if self.client.is_muted:  # Checks to see if the client has been muted by a mod
            self.client.send_ooc("You have been muted by a moderator")
            return
        if not self.validate_net_cmd(args, ArgType.INT, ArgType.INT):
            return
        try:
            self.client.area.change_hp(args[0], args[1])
            info = 'changed penalty bar {} to {}.'.format(args[0], args[1])
            self.client.area.add_to_judgelog(self.client, info)
            logger.log_server(
                '[{}]{} changed HP ({}) to {}'.format(
                    self.client.area.id, self.client.get_char_name(), args[0],
                    args[1]), self.client)
        except AreaError:
            return
        self.client.last_active = Constants.get_time()
Exemple #18
0
    def net_cmd_ms(self, args):
        """ IC message.

        Refer to the implementation for details.

        """
        if self.client.is_muted:  # Checks to see if the client has been muted by a mod
            self.client.send_ooc("You have been muted by a moderator.")
            return
        if self.client.area.ic_lock and not self.client.is_staff():
            self.client.send_ooc(
                "IC chat in this area has been locked by a moderator.")
            return
        if not self.client.area.can_send_message():
            return

        pargs = self.process_arguments('ms', args)
        if not pargs:
            return

        if not self.client.area.iniswap_allowed:
            if self.client.area.is_iniswap(self.client, pargs['pre'],
                                           pargs['anim'], pargs['folder']):
                self.client.send_ooc("Iniswap is blocked in this area.")
                return
        if pargs[
                'folder'] in self.client.area.restricted_chars and not self.client.is_staff(
                ):
            self.client.send_ooc('Your character is restricted in this area.')
            return
        if pargs['msg_type'] not in ('chat', '0', '1'):
            return
        if pargs['anim_type'] not in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10):
            return
        if pargs['cid'] != self.client.char_id:
            return
        if pargs['sfx_delay'] < 0:
            return
        if pargs['button'] not in (0, 1, 2, 3, 4, 5, 6, 7):  # Shouts
            return
        if pargs[
                'button'] > 0 and not self.client.area.bullet and not self.client.is_staff(
                ):
            self.client.send_ooc('Bullets are disabled in this area.')
            return
        if pargs['evidence'] < 0:
            return
        if pargs['ding'] not in (0, 1, 2, 3, 4, 5, 6, 7):  # Effects
            return
        if pargs['color'] not in (0, 1, 2, 3, 4, 5, 6):
            return
        if pargs[
                'color'] == 5 and not self.client.is_mod and not self.client.is_cm:
            pargs['color'] = 0
        if pargs['color'] == 6:
            # Remove all unicode to prevent now yellow text abuse
            pargs['text'] = re.sub(r'[^\x00-\x7F]+', ' ', pargs['text'])
            if len(pargs['text'].strip(' ')) == 1:
                pargs['color'] = 0
            else:
                if pargs['text'].strip(' ') in ('<num>', '<percent>',
                                                '<dollar>', '<and>'):
                    pargs['color'] = 0
        if self.client.pos:
            pargs['pos'] = self.client.pos
        else:
            if pargs['pos'] not in ('def', 'pro', 'hld', 'hlp', 'jud', 'wit'):
                return
        self.client.pos = pargs['pos']

        # Truncate and alter message if message effect is in place
        raw_msg = pargs['text'][:256]
        msg = raw_msg
        if self.client.gimp:  #If you are gimped, gimp message.
            msg = Constants.gimp_message()
        if self.client.disemvowel:  #If you are disemvoweled, replace string.
            msg = Constants.disemvowel_message(msg)
        if self.client.disemconsonant:  #If you are disemconsonanted, replace string.
            msg = Constants.disemconsonant_message(msg)
        if self.client.remove_h:  #If h is removed, replace string.
            msg = Constants.remove_h_message(msg)

        gag_replaced = False
        if self.client.is_gagged:
            allowed_starters = ('(', '*', '[')
            if msg != ' ' and not msg.startswith(allowed_starters):
                gag_replaced = True
                msg = Constants.gagged_message()
            if msg != raw_msg:
                self.client.send_ooc_others(
                    '(X) {} tried to say `{}` but is currently gagged.'.format(
                        self.client.displayname, raw_msg),
                    is_zstaff_flex=True,
                    in_area=True)

        if pargs['evidence']:
            evidence_position = self.client.evi_list[pargs['evidence']] - 1
            if self.client.area.evi_list.evidences[
                    evidence_position].pos != 'all':
                self.client.area.evi_list.evidences[
                    evidence_position].pos = 'all'
                self.client.area.broadcast_evidence_list()

        # If client has GlobalIC enabled, set area range target to intended range and remove
        # GlobalIC prefix if needed.
        if self.client.multi_ic is None or not msg.startswith(
                self.client.multi_ic_pre):
            area_range = range(self.client.area.id, self.client.area.id + 1)
        else:
            # As msg.startswith('') is True, this also accounts for having no required prefix.
            start, end = self.client.multi_ic[
                0].id, self.client.multi_ic[1].id + 1
            start_area = self.server.area_manager.get_area_by_id(start)
            end_area = self.server.area_manager.get_area_by_id(end - 1)
            area_range = range(start, end)

            msg = msg.replace(self.client.multi_ic_pre, '', 1)
            if start != end - 1:
                self.client.send_ooc(
                    'Sent global IC message "{}" to areas {} through {}.'.
                    format(msg, start_area.name, end_area.name))
            else:
                self.client.send_ooc(
                    'Sent global IC message "{}" to area {}.'.format(
                        msg, start_area.name))

        pargs['msg'] = msg
        pargs['evidence'] = self.client.evi_list[pargs['evidence']]
        pargs[
            'showname'] = ''  # Dummy value, actual showname is computed later

        # Compute pairs
        # Based on tsuserver3.3 code
        # Only do this if character is paired, which would only happen for AO 2.6 clients

        self.client.charid_pair = pargs[
            'charid_pair'] if 'charid_pair' in pargs else -1
        self.client.offset_pair = pargs[
            'offset_pair'] if 'offset_pair' in pargs else 0
        self.client.flip = pargs['flip']
        self.client.char_folder = pargs['folder']

        if pargs['anim_type'] not in (5, 6):
            self.client.last_sprite = pargs['anim']

        pargs['other_offset'] = 0
        pargs['other_emote'] = 0
        pargs['other_flip'] = 0
        pargs['other_folder'] = ''
        if 'charid_pair' not in pargs or pargs['charid_pair'] < -1:
            pargs['charid_pair'] = -1

        if pargs['charid_pair'] > -1:
            for target in self.client.area.clients:
                if target == self.client:
                    continue
                # Check pair has accepted pair
                if target.char_id != self.client.charid_pair:
                    continue
                if target.charid_pair != self.client.char_id:
                    continue
                # Check pair is in same position
                if target.pos != self.client.pos:
                    continue

                pargs['other_offset'] = target.offset_pair
                pargs['other_emote'] = target.last_sprite
                pargs['other_flip'] = target.flip
                pargs['other_folder'] = target.char_folder
                break
            else:
                # There are no clients who want to pair with this client
                pargs['charid_pair'] = -1

        for area_id in area_range:
            target_area = self.server.area_manager.get_area_by_id(area_id)
            for c in target_area.clients:
                c.send_ic(params=pargs,
                          sender=self.client,
                          gag_replaced=gag_replaced)

            target_area.set_next_msg_delay(len(msg))

            # Deal with shoutlog
            if pargs['button'] > 0:
                info = 'used shout {} with the message: {}'.format(
                    pargs['button'], msg)
                target_area.add_to_shoutlog(self.client, info)

        self.client.area.set_next_msg_delay(len(msg))
        logger.log_server(
            '[IC][{}][{}]{}'.format(self.client.area.id,
                                    self.client.get_char_name(), msg),
            self.client)

        # Sending IC messages reveals sneaked players
        if not self.client.is_staff() and not self.client.is_visible:
            self.client.change_visibility(True)
            self.client.send_ooc_others(
                '(X) {} revealed themselves by talking ({}).'.format(
                    self.client.displayname, self.client.area.id),
                is_zstaff=True)

        self.server.tasker.create_task(self.client, [
            'as_afk_kick', self.client.area.afk_delay,
            self.client.area.afk_sendto
        ])
        if self.client.area.is_recording:
            self.client.area.recorded_messages.append(args)

        self.client.last_ic_message = msg
        self.client.last_active = Constants.get_time()
Exemple #19
0
    def net_cmd_ct(self, args):
        """ OOC Message

        CT#<name:string>#<message:string>#%

        """
        if self.client.is_ooc_muted:  # Checks to see if the client has been muted by a mod
            self.client.send_ooc("You have been muted by a moderator.")
            return
        if not self.validate_net_cmd(
                args, ArgType.STR, ArgType.STR, needs_auth=False):
            return
        if self.client.name != args[0] and self.client.fake_name != args[0]:
            if self.client.is_valid_name(args[0]):
                self.client.name = args[0]
                self.client.fake_name = args[0]
            else:
                self.client.fake_name = args[0]
                self.client.name = ''
        if self.client.name == '':
            self.client.send_ooc(
                'You must insert a name with at least one letter.')
            return
        if self.client.name.startswith(' '):
            self.client.send_ooc(
                'You must insert a name that starts with a letter.')
            return
        if self.server.config[
                'hostname'] in self.client.name or '<dollar>G' in self.client.name:
            self.client.send_ooc('That name is reserved.')
            return
        if args[1].startswith('/'):
            spl = args[1][1:].split(' ', 1)
            cmd = spl[0]
            arg = ''
            if len(spl) == 2:
                arg = spl[1][:1024]
            try:
                called_function = 'ooc_cmd_{}'.format(cmd)
                function = None  # Double assignment to check if it matched to a function later
                function = getattr(self.server.commands, called_function)
            except AttributeError:
                try:
                    function = getattr(self.server.commands_alt,
                                       called_function)
                except AttributeError:
                    logger.log_print('Attribute error with ' + called_function)
                    self.client.send_ooc('Invalid command.')

            if function:
                try:
                    function(self.client, arg)
                except TsuserverException as ex:
                    self.client.send_ooc(ex)
        else:
            # Censor passwords if accidentally said without a slash in OOC
            for password in self.server.all_passwords:
                for login in ['login ', 'logincm ', 'loginrp ', 'logingm ']:
                    if login + password in args[1]:
                        args[1] = args[1].replace(password, '[CENSORED]')
            if self.client.disemvowel:  #If you are disemvoweled, replace string.
                args[1] = Constants.disemvowel_message(args[1])
            if self.client.disemconsonant:  #If you are disemconsonanted, replace string.
                args[1] = Constants.disemconsonant_message(args[1])
            if self.client.remove_h:  #If h is removed, replace string.
                args[1] = Constants.remove_h_message(args[1])

            self.client.area.send_command('CT', self.client.name, args[1])
            self.client.last_ooc_message = args[1]
            logger.log_server(
                '[OOC][{}][{}][{}]{}'.format(self.client.area.id,
                                             self.client.get_char_name(),
                                             self.client.name, args[1]),
                self.client)
        self.client.last_active = Constants.get_time()
Exemple #20
0
    def net_cmd_ms(self, args):
        """ IC message.

        Refer to the implementation for details.

        """

        if self.client.is_muted:  # Checks to see if the client has been muted by a mod
            self.client.send_ooc("You have been muted by a moderator.")
            return
        if self.client.area.ic_lock and not self.client.is_staff():
            self.client.send_ooc(
                "IC chat in this area has been locked by a moderator.")
            return
        if not self.client.area.can_send_message():
            return
        pargs = self.process_arguments('ms', args)
        if not pargs:
            return

        # First, check if the player just sent the same message with the same character and did
        # not receive any other messages in the meantime.
        # This helps prevent record these messages and retransmit it to clients who may want to
        # filter these out
        if (pargs['text'] == self.client.last_ic_raw_message
                and self.client.last_ic_received_mine
                and self.client.get_char_name() == self.client.last_ic_char):
            return

        if not self.client.area.iniswap_allowed:
            if self.client.area.is_iniswap(self.client, pargs['pre'],
                                           pargs['anim'], pargs['folder']):
                self.client.send_ooc("Iniswap is blocked in this area.")
                return
        if pargs[
                'folder'] in self.client.area.restricted_chars and not self.client.is_staff(
                ):
            self.client.send_ooc('Your character is restricted in this area.')
            return
        if pargs['msg_type'] not in ('chat', '0', '1'):
            return
        if pargs['anim_type'] not in (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10):
            return
        if pargs['cid'] != self.client.char_id:
            return
        if pargs['sfx_delay'] < 0:
            return
        if pargs['button'] not in (0, 1, 2, 3, 4, 5, 6, 7):  # Shouts
            return
        if pargs[
                'button'] > 0 and not self.client.area.bullet and not self.client.is_staff(
                ):
            self.client.send_ooc('Bullets are disabled in this area.')
            return
        if pargs['evidence'] < 0:
            return
        if pargs['ding'] not in (0, 1, 2, 3, 4, 5, 6, 7):  # Effects
            return
        if pargs['color'] not in (0, 1, 2, 3, 4, 5, 6):
            return
        if pargs[
                'color'] == 5 and not self.client.is_mod and not self.client.is_cm:
            pargs['color'] = 0
        if pargs['color'] == 6:
            # Remove all unicode to prevent now yellow text abuse
            pargs['text'] = re.sub(r'[^\x00-\x7F]+', ' ', pargs['text'])
            if len(pargs['text'].strip(' ')) == 1:
                pargs['color'] = 0
            else:
                if pargs['text'].strip(' ') in ('<num>', '<percent>',
                                                '<dollar>', '<and>'):
                    pargs['color'] = 0
        if self.client.pos:
            pargs['pos'] = self.client.pos
        else:
            if pargs['pos'] not in ('def', 'pro', 'hld', 'hlp', 'jud', 'wit'):
                return
        self.client.pos = pargs['pos']

        # At this point, the message is guaranteed to be sent
        # First, update last raw message sent *before* any transformations. That is so that the
        # server can accurately ignore client sending the same message over and over again
        self.client.last_ic_raw_message = pargs['text']
        self.client.last_ic_char = self.client.get_char_name()

        # Truncate and alter message if message effect is in place
        raw_msg = pargs['text'][:256]
        msg = raw_msg
        if self.client.gimp:  #If you are gimped, gimp message.
            msg = Constants.gimp_message()
        if self.client.disemvowel:  #If you are disemvoweled, replace string.
            msg = Constants.disemvowel_message(msg)
        if self.client.disemconsonant:  #If you are disemconsonanted, replace string.
            msg = Constants.disemconsonant_message(msg)
        if self.client.remove_h:  #If h is removed, replace string.
            msg = Constants.remove_h_message(msg)

        gag_replaced = False
        if self.client.is_gagged:
            allowed_starters = ('(', '*', '[')
            if msg != ' ' and not msg.startswith(allowed_starters):
                gag_replaced = True
                msg = Constants.gagged_message()
            if msg != raw_msg:
                self.client.send_ooc_others(
                    '(X) {} [{}] tried to say `{}` but is currently gagged.'.
                    format(self.client.displayname, self.client.id, raw_msg),
                    is_zstaff_flex=True,
                    in_area=True)

        # Censor passwords if login command accidentally typed in IC
        for password in self.server.all_passwords:
            for login in ['login ', 'logincm ', 'loginrp ', 'logingm ']:
                if login + password in msg:
                    msg = msg.replace(password, '[CENSORED]')

        if pargs['evidence']:
            evidence_position = self.client.evi_list[pargs['evidence']] - 1
            if self.client.area.evi_list.evidences[
                    evidence_position].pos != 'all':
                self.client.area.evi_list.evidences[
                    evidence_position].pos = 'all'
                self.client.area.broadcast_evidence_list()

        # If client has GlobalIC enabled, set area range target to intended range and remove
        # GlobalIC prefix if needed.
        if self.client.multi_ic is None or not msg.startswith(
                self.client.multi_ic_pre):
            area_range = range(self.client.area.id, self.client.area.id + 1)
        else:
            # As msg.startswith('') is True, this also accounts for having no required prefix.
            start, end = self.client.multi_ic[
                0].id, self.client.multi_ic[1].id + 1
            start_area = self.server.area_manager.get_area_by_id(start)
            end_area = self.server.area_manager.get_area_by_id(end - 1)
            area_range = range(start, end)

            truncated_msg = msg.replace(self.client.multi_ic_pre, '', 1)
            if start != end - 1:
                self.client.send_ooc(
                    'Sent global IC message "{}" to areas {} through {}.'.
                    format(truncated_msg, start_area.name, end_area.name))
            else:
                self.client.send_ooc(
                    'Sent global IC message "{}" to area {}.'.format(
                        truncated_msg, start_area.name))

        pargs['msg'] = msg
        pargs['evidence'] = self.client.evi_list[pargs['evidence']]
        pargs[
            'showname'] = ''  # Dummy value, actual showname is computed later

        # Compute pairs
        # Based on tsuserver3.3 code
        # Only do this if character is paired, which would only happen for AO 2.6+ clients

        # Handle AO 2.8 logic
        # AO 2.8 sends their charid_pair in slightly longer format (\d+\^\d+)
        # The first bit corresponds to the proper charid_pair, the latter one to whether
        # the character should appear in front or behind the pair. We still want to extract
        # charid_pair so pre-AO 2.8 still see the pair; but make it so that AO 2.6 can send pair
        # messages. Thus, we 'invent' the missing arguments based on available info.
        if 'charid_pair_pair_order' in pargs:
            # AO 2.8 sender
            pargs['charid_pair'] = int(
                pargs['charid_pair_pair_order'].split('^')[0])
        elif 'charid_pair' in pargs:
            # AO 2.6 sender
            pargs['charid_pair_pair_order'] = f'{pargs["charid_pair"]}^0'
        else:
            # E.g. DRO
            pargs['charid_pair'] = -1
            pargs['charid_pair_pair_order'] = -1

        self.client.charid_pair = pargs[
            'charid_pair'] if 'charid_pair' in pargs else -1
        self.client.offset_pair = pargs[
            'offset_pair'] if 'offset_pair' in pargs else 0
        self.client.flip = pargs['flip']
        self.client.char_folder = pargs['folder']

        if pargs['anim_type'] not in (5, 6):
            self.client.last_sprite = pargs['anim']

        pargs['other_offset'] = 0
        pargs['other_emote'] = 0
        pargs['other_flip'] = 0
        pargs['other_folder'] = ''
        if 'charid_pair' not in pargs or pargs['charid_pair'] < -1:
            pargs['charid_pair'] = -1
            pargs['charid_pair_pair_order'] = -1

        if pargs['charid_pair'] > -1:
            for target in self.client.area.clients:
                if target == self.client:
                    continue
                # Check pair has accepted pair
                if target.char_id != self.client.charid_pair:
                    continue
                if target.charid_pair != self.client.char_id:
                    continue
                # Check pair is in same position
                if target.pos != self.client.pos:
                    continue

                pargs['other_offset'] = target.offset_pair
                pargs['other_emote'] = target.last_sprite
                pargs['other_flip'] = target.flip
                pargs['other_folder'] = target.char_folder
                break
            else:
                # There are no clients who want to pair with this client
                pargs['charid_pair'] = -1
                pargs['offset_pair'] = 0
                pargs['charid_pair_pair_order'] = -1

        for area_id in area_range:
            target_area = self.server.area_manager.get_area_by_id(area_id)
            for c in target_area.clients:
                c.send_ic(params=pargs,
                          sender=self.client,
                          gag_replaced=gag_replaced)

            target_area.set_next_msg_delay(len(msg))

            # Deal with shoutlog
            if pargs['button'] > 0:
                info = 'used shout {} with the message: {}'.format(
                    pargs['button'], msg)
                target_area.add_to_shoutlog(self.client, info)

        self.client.area.set_next_msg_delay(len(msg))
        logger.log_server(
            '[IC][{}][{}]{}'.format(self.client.area.id,
                                    self.client.get_char_name(), msg),
            self.client)

        # Sending IC messages reveals sneaked players
        if not self.client.is_staff() and not self.client.is_visible:
            self.client.change_visibility(True)
            self.client.send_ooc_others(
                '(X) {} [{}] revealed themselves by talking ({}).'.format(
                    self.client.displayname, self.client.id,
                    self.client.area.id),
                is_zstaff=True)

        self.server.tasker.create_task(self.client, [
            'as_afk_kick', self.client.area.afk_delay,
            self.client.area.afk_sendto
        ])
        if self.client.area.is_recording:
            self.client.area.recorded_messages.append(args)

        self.client.last_ic_message = msg
        self.client.last_active = Constants.get_time()
Exemple #21
0
    def net_cmd_ct(self, args: List[str]):
        """ OOC Message

        CT#<name:string>#<message:string>#%

        """

        pargs = self.process_arguments('CT', args)
        username, message = pargs['username'], pargs['message']

        # Trim out any leading/trailing whitespace characters up to a chain of spaces
        username = Constants.trim_extra_whitespace(username)
        message = Constants.trim_extra_whitespace(message)

        if self.client.is_ooc_muted:  # Checks to see if the client has been muted by a mod
            self.client.send_ooc("You have been muted by a moderator.")
            return
        if username == '' or not self.client.is_valid_name(username):
            self.client.send_ooc('You must insert a name with at least one letter.')
            return
        if username.startswith(' '):
            self.client.send_ooc('You must insert a name that starts with a letter.')
            return
        if Constants.contains_illegal_characters(username):
            self.client.send_ooc('Your name contains an illegal character.')
            return
        if (Constants.decode_ao_packet([self.server.config['hostname']])[0] in username
            or '$G' in username):
            self.client.send_ooc('That name is reserved.')
            return

        # After this the name is validated
        self.client.publish_inbound_command('CT', pargs)
        self.client.name = username

        if message.startswith('/'):
            spl = message[1:].split(' ', 1)
            cmd = spl[0]
            arg = ''
            if len(spl) == 2:
                arg = spl[1][:1024]
            arg = Constants.trim_extra_whitespace(arg)  # Do it again because args may be weird
            try:
                called_function = 'ooc_cmd_{}'.format(cmd)
                function = None  # Double assignment to check if it matched to a function later
                function = getattr(self.server.commands, called_function)
            except AttributeError:
                try:
                    function = getattr(self.server.commands_alt, called_function)
                except AttributeError:
                    self.client.send_ooc(f'Invalid command `{cmd}`.')

            if function:
                try:
                    function(self.client, arg)
                except TsuserverException as ex:
                    if ex.message:
                        self.client.send_ooc(ex)
                    else:
                        self.client.send_ooc(type(ex).__name__)
        else:
            # Censor passwords if accidentally said without a slash in OOC
            for password in self.server.all_passwords:
                for login in ['login ', 'logincm ', 'loginrp ', 'logingm ']:
                    if login + password in args[1]:
                        message = message.replace(password, '[CENSORED]')
            if self.client.disemvowel:  # If you are disemvoweled, replace string.
                message = Constants.disemvowel_message(message)
            if self.client.disemconsonant:  # If you are disemconsonanted, replace string.
                message = Constants.disemconsonant_message(message)
            if self.client.remove_h:  # If h is removed, replace string.
                message = Constants.remove_h_message(message)

            for client in self.client.area.clients:
                client.send_ooc(message, username=self.client.name)
            self.client.last_ooc_message = args[1]
            logger.log_server('[OOC][{}][{}][{}]{}'
                              .format(self.client.area.id, self.client.get_char_name(),
                                      self.client.name, message), self.client)
        self.client.last_active = Constants.get_time()