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': ''})
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()
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()
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()
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()
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()
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
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()
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()
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()
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()
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()
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()
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)
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)
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()
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()
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()
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()
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()
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()