def ooc_cmd_bg(client, arg): """ Set the background of an area. Usage: /bg <background> """ if len(arg) == 0: pos_lock = '' if len(client.area.pos_lock) > 0: pos = ' '.join(str(l) for l in client.area.pos_lock) pos_lock = f'\nAvailable positions: {pos}.' client.send_ooc(f'Current background is {client.area.background}.{pos_lock}') return if not client in client.area.owners and not client.is_mod and client.area.bg_lock: raise AreaError("This area's background is locked!") if client.area.cannot_ic_interact(client): raise AreaError("You are not on the area's invite list!") if not client.is_mod and not (client in client.area.owners) and client.char_id == -1: raise ClientError("You may not do that while spectating!") try: client.area.change_background(arg) except AreaError: raise client.area.broadcast_ooc( f'{client.showname} changed the background to {arg}.') database.log_area('bg', client, client.area, message=arg)
def change_hp(self, side: int, health: int): """ Change a penalty healthbar. Parameters ---------- side: int Penalty bar to change (1 for def, 2 for pro). health: int New health value of the penalty bar. Raises ------ AreaError If an invalid penalty bar or health value was given. """ if not 1 <= side <= 2: raise AreaError('Invalid penalty side.') if not 0 <= health <= 10: raise AreaError('Invalid penalty value.') if side == 1: self.hp_def = health elif side == 2: self.hp_pro = health for client in self.clients: client.send_health(side=side, health=health)
def ooc_cmd_hub(client, arg): """ List hubs, or go to another hub. Usage: /hub [id/name] """ if arg == '': client.send_hub_list() return try: for hub in client.server.hub_manager.hubs: if hub.name.lower() == arg.lower() or hub.abbreviation == arg or (arg.isdigit() and hub.id == int(arg)): if hub == client.area.area_manager: raise AreaError('User already in specified hub.') preflist = client.server.supported_features.copy() if not hub.arup_enabled: preflist.remove('arup') client.send_command('FL', *preflist) client.change_area(hub.default_area()) client.area.area_manager.send_arup_status([client]) client.area.area_manager.send_arup_cms([client]) client.area.area_manager.send_arup_lock([client]) client.send_hub_info() return raise AreaError('Targeted hub not found!') except ValueError: raise ArgumentError('Hub ID must be a name, abbreviation or a number.') except (AreaError, ClientError): raise
def ooc_cmd_bg(client, arg): """ Set the background of an area. Usage: /bg <background> """ if len(arg) == 0: pos_lock = "" if len(client.area.pos_lock) > 0: pos = ", ".join(str(lpos) for lpos in client.area.pos_lock) pos_lock = f"\nAvailable positions: {pos}." client.send_ooc( f"Current background is {client.area.background}.{pos_lock}") return if client not in client.area.owners and not client.is_mod and client.area.bg_lock: raise AreaError("This area's background is locked!") if client.area.cannot_ic_interact(client): raise AreaError("You are not on the area's invite list!") if (not client.is_mod and not (client in client.area.owners) and client.char_id == -1): raise ClientError("You may not do that while spectating!") if client.area.dark and not client.is_mod and not (client in client.area.owners): raise ClientError("You must be authorized to do that.") try: client.area.change_background(arg) except AreaError: raise client.area.broadcast_ooc( f"{client.showname} changed the background to {arg}.") database.log_area("bg", client, client.area, message=arg)
def change_hp(self, side, val): """ Change a penalty healthbar. Parameters ---------- side: int Penalty bar to change (1 for def, 2 for pro). val: int New health value of the penalty bar. Raises ------ AreaError If an invalid penalty bar or health value was given. """ if not 0 <= val <= 10: raise AreaError('Invalid penalty value.') if not 1 <= side <= 2: raise AreaError('Invalid penalty side.') if side == 1: self.hp_def = val elif side == 2: self.hp_pro = val self.send_command('HP', side, val)
def remove_area(self, area): """ Remove an area instance. :param area: target area instance. """ if not (area in self.areas): raise AreaError('Area not found.') # Make a copy because it can change size during iteration # (causes runtime error otherwise) if self.default_area() != area: target_area = self.default_area() else: try: target_area = self.get_area_by_id(1) except: raise AreaError('May not remove last existing area!') clients = area.clients.copy() for client in clients: client.set_area(target_area) # Update area links for ar in self.areas: for link in ar.links.copy(): # Shift it down as one area was removed if int(link) > area.id: ar.links[str(int(link)-1)] = ar.links.pop(link) elif link == str(area.id): del ar.links[link] self.areas.remove(area)
def ooc_cmd_status(client, arg): """ Show or modify the current status of an area. Usage: /status <idle|rp|casing|looking-for-players|lfp|recess|gaming> """ if not client.area.area_manager.arup_enabled: raise AreaError("This hub does not use the /status system.") if len(arg) == 0: client.send_ooc(f"Current status: {client.area.status}") else: if (not client.area.can_change_status and not client.is_mod and client not in client.area.owners): raise AreaError( "This area's status cannot be changed by anyone who's not a CM or mod!" ) if client.area.cannot_ic_interact(client): raise AreaError("You are not on the area's invite list!") if (not client.is_mod and client not in client.area.owners and client.char_id == -1): raise ClientError("You may not do that while spectating!") try: client.area.change_status(arg) client.area.broadcast_ooc("{} changed status to {}.".format( client.showname, client.area.status)) database.log_area("status", client, client.area, message=arg) except AreaError: raise
def load_character_data(self, path="config/character_data.yaml"): """ Load all the character-specific information such as movement delay, keys, etc. :param path: filepath to the YAML file. """ try: if not os.path.isfile(path): raise with open(path, "r") as chars: data = yaml.safe_load(chars) except Exception: raise AreaError( f"Hub {self.name} trying to load character data: File path {path} is invalid!" ) try: for char in data.copy(): # Convert the old numeric way to store character data into character folder based one if isinstance(char, int) and self.is_valid_char_id(char): data[self.char_list[char]] = data.pop(char) self.character_data = data except Exception: raise AreaError( "Something went wrong while loading the character data!")
def change_hp(self, side, val): if not 0 <= val <= 10: raise AreaError('Invalid penalty value.') if not 1 <= side <= 2: raise AreaError('Invalid penalty side.') if side == 1: self.hp_def = val elif side == 2: self.hp_pro = val self.send_command('HP', side, val)
def ooc_cmd_save_hub(client, arg): """ Save the current Hub in the server's storage/hubs/<name>.yaml file. If blank and you're a mod, it will save to server's config/areas_new.yaml for the server owner to approve. Usage: /save_hub <name> """ if not client.is_mod: if arg == "": raise ArgumentError( "You must be authorized to save the default hub!") if len(arg) < 3: raise ArgumentError("Filename must be at least 3 symbols long!") try: if arg != "": path = "storage/hubs" num_files = len( [f for f in os.listdir(path) if os.path.isfile( os.path.join(path, f))] ) if num_files >= 1000: # yikes raise AreaError( "Server storage full! Please contact the server host to resolve this issue." ) try: arg = f"{path}/{arg}.yaml" if os.path.isfile(arg): with open(arg, "r", encoding="utf-8") as stream: hub = yaml.safe_load(stream) if "read_only" in hub and hub["read_only"] is True: raise ArgumentError( f"Hub {arg} already exists and it is read-only!" ) with open(arg, "w", encoding="utf-8") as stream: yaml.dump( client.area.area_manager.save( ignore=["can_gm", "max_areas"]), stream, default_flow_style=False, ) except ArgumentError: raise except Exception: raise AreaError(f"File path {arg} is invalid!") client.send_ooc(f"Saving as {arg}...") else: client.server.hub_manager.save("config/areas_new.yaml") client.send_ooc( "Saving all Hubs to areas_new.yaml. Contact the server owner to apply the changes." ) except AreaError: raise
def change_hp(self, side, val): """ Set the penalty bars. :param side: 1 for defense; 2 for prosecution :param val: value from 0 to 10 """ if not 0 <= val <= 10: raise AreaError('Invalid penalty value.') if not 1 <= side <= 2: raise AreaError('Invalid penalty side.') if side == 1: self.hp_def = val elif side == 2: self.hp_pro = val self.send_command('HP', side, val)
def change_background(self, bg, validate=True, override_blind=False): """ Change the background of the current area. Parameters ---------- bg: str New background name. validate: bool, optional Whether to first determine if background name is listed as a server background before changing. Defaults to True. override_blind: bool, optional Whether to send the intended background to blind people as opposed to the server blackout one. Defaults to False (send blackout). Raises ------ AreaError If the server attempted to validate the background name and failed. """ if validate and bg.lower() not in [ name.lower() for name in self.server.backgrounds ]: raise AreaError('Invalid background name.') self.background = bg for c in self.clients: if c.is_blind and not override_blind: c.send_command('BN', self.server.config['blackout_background']) else: c.send_command('BN', bg)
def get_rand_avail_char_id(self, allow_restricted=False): avail_set = set(range(len( self.server.char_list))) - self.get_chars_unusable( allow_restricted=allow_restricted) if len(avail_set) == 0: raise AreaError('No available characters.') return random.choice(tuple(avail_set))
def ooc_cmd_evidence_remove(client, arg): """ Remove a piece of evidence. Usage: /evidence_remove <evi_name/id> """ if arg == "": raise ArgumentError( "Use /evidence_remove <evi_name/id> to remove that piece of evidence." ) try: evi_list = client.area.get_evidence_list(client) evidence = None for i, evi in enumerate(evi_list): if (arg.isnumeric() and int(arg) - 1 == i) or arg.lower() == evi[0].lower(): evidence = evi break if evidence is None: raise AreaError( f"Target evidence not found! (/evidence_remove {arg})") evi_name = evidence[0] client.area.evi_list.del_evidence(client, i) database.log_area("evidence.del", client, client.area) client.area.broadcast_evidence_list() client.send_ooc(f"You have removed evidence '{evi_name}'.") except ValueError: raise except (AreaError, ClientError): raise
def load_music(self, path): try: if not os.path.isfile(path): raise AreaError( f"Hub {self.name} trying to load music list: File path {path} is invalid!" ) with open(path, "r", encoding="utf-8") as stream: music_list = yaml.safe_load(stream) prepath = "" for item in music_list: # deprecated, use 'replace_music' hub pref instead # if 'replace' in item: # self.replace_music = item['replace'] is True if "use_unique_folder" in item and item[ "use_unique_folder"] is True: prepath = os.path.splitext(os.path.basename(path))[0] + "/" if "category" not in item: continue if "songs" in item: for song in item["songs"]: song["name"] = prepath + song["name"] self.music_list = music_list except ValueError: raise except AreaError: raise
def change_status(self, value): allowed_values = ('idle', 'building-open', 'building-full', 'casing-open', 'casing-full', 'recess') if value.lower() not in allowed_values: raise AreaError('Invalid status. Possible values: {}'.format( ', '.join(allowed_values))) self.status = value.upper()
def ooc_cmd_area(client, arg): """ List areas, or go to another area. Usage: /area [id] or /area [name] """ if arg == "": client.send_area_list( full=client.is_mod or client in client.area.owners) return try: for area in client.area.area_manager.areas: a = arg.split(" ")[0] aid = a.strip("[]") if ((a.startswith("[") and a.endswith("]") and aid.isdigit() and area.id == int(aid)) or area.name.lower() == arg.lower() or area.abbreviation == arg or (arg.isdigit() and area.id == int(arg))): client.change_area(area) return raise AreaError("Targeted area not found!") except ValueError: raise ArgumentError( "Area ID must be a name, abbreviation or a number.") except (AreaError, ClientError): raise
def ooc_cmd_evidence_present(client, arg): """ Present a piece of evidence on your next IC message. Don't include [id] or make it 0 to stop presenting evidence. Usage: /evidence_present [id] """ if arg == "" or arg == "0": client.send_ooc("No longer presenting evidence.") client.presenting = 0 return try: evidence = None evi_list = client.area.get_evidence_list(client) # Check if evidence we're looking for exists for i, evi in enumerate(evi_list): print(arg.lower(), evi[0].lower()) if (arg.isnumeric() and int(arg) - 1 == i) or arg.lower() == evi[0].lower(): evidence = evi break if evidence is None: raise AreaError( f"Target evidence not found! (/evidence_present {arg})") client.presenting = i + 1 client.send_ooc( f"Will now present evidence [{client.presenting}] {evidence[0]} on next IC message." ) except ValueError: raise except (AreaError, ClientError): raise
def get_rand_avail_char_id(self): """Get a random available character ID.""" avail_set = set(range(len( self.server.char_list))) - {x.char_id for x in self.clients} if len(avail_set) == 0: raise AreaError('No available characters.') return random.choice(tuple(avail_set))
def create_area(self): """Create a new area instance and return it.""" idx = len(self.areas) if self.max_areas != -1 and idx >= self.max_areas: raise AreaError(f'Area limit reached! ({self.max_areas})') area = Area(self, f'Area {idx}') self.areas.append(area) return area
def change_hp(self, side: int, val: int): """Set the penalty bars. Args: side (int): 1 for defense; 2 for prosecution val (int): value from 0 to 10 Raises: AreaError: If side is not between 1-2 inclusive or val is not between 0-10 """ if not 0 <= val <= 10: raise AreaError('Invalid penalty value.') if not 1 <= side <= 2: raise AreaError('Invalid penalty side.') if side == 1: self.hp_def = val elif side == 2: self.hp_pro = val self.send_command('HP', side, val)
def get_area_by_name(self, name, case_sensitive=False): """Get an area by name.""" for area in self.areas: a_name = area.name.lower() if case_sensitive else area.name name = name.lower() if case_sensitive else name if a_name == name: return area raise AreaError('Area not found.')
def ooc_cmd_bg(client, arg): """ Set the background of a room. Usage: /bg <background> """ if len(arg) == 0: raise ArgumentError('You must specify a name. Use /bg <background>.') if not client.is_mod and client.area.bg_lock == "true": raise AreaError("This area's background is locked!") elif client.area.cannot_ic_interact(client): raise AreaError("You are not permitted to change the background in this area!") try: client.area.change_background(arg) except AreaError: raise client.area.broadcast_ooc( f'{client.char_name} changed the background to {arg}.') database.log_room('bg', client, client.area, message=arg)
def save(self, path='config/areas.yaml'): try: with open(path, 'w', encoding='utf-8') as stream: hubs = [] for hub in self.hubs: hubs.append(hub.save()) yaml.dump(hubs, stream, default_flow_style=False) except: raise AreaError(f'File path {path} is invalid!')
def change_passage_lock(self, client: ClientManager.Client, areas: List[AreaManager.Area], bilock: bool = False, change_passage_visibility: bool = False): now_reachable = [] num_areas = 2 if bilock else 1 # First check if the player should be able to change the passage at all for i in range(num_areas): # First check if it is the case a non-authorized use is trying to change passages to # areas that do not allow their passages to be modified if not areas[ i].change_reachability_allowed and not client.is_staff(): raise AreaError( 'You must be authorized to change passages in area {}.'. format(areas[i].name)) # And make sure that non-authorized users cannot create passages they cannot see if ((not areas[1 - i].name in areas[i].reachable_areas) and not (client.is_staff() or areas[1 - i].name in areas[i].visible_reachable_areas)): raise AreaError( 'You must be authorized to create a new passage from {} to ' '{}.'.format(areas[i].name, areas[1 - i].name)) # If we are at this point, we are committed to changing the passage locks for i in range(num_areas): if areas[1 - i].name in areas[ i].reachable_areas: # Case removing a passage now_reachable.append(False) areas[i].reachable_areas -= {areas[1 - i].name} if change_passage_visibility: areas[i].visible_reachable_areas -= {areas[1 - i].name} else: # Case creating a passage now_reachable.append(True) areas[i].reachable_areas.add(areas[1 - i].name) if change_passage_visibility: areas[i].visible_reachable_areas.add(areas[1 - i].name) for client in areas[i].clients: client.reload_music_list() return now_reachable
def save(self, path="config/areas.yaml"): try: with open(path, "w", encoding="utf-8") as stream: hubs = [] for hub in self.hubs: hubs.append(hub.save()) yaml.dump(hubs, stream, default_flow_style=False) except Exception: raise AreaError( f"Trying to save Hub list: File path {path} is invalid!")
def change_status(self, value): allowed_values = ('idle', 'rp', 'casing', 'looking-for-players', 'lfp', 'recess', 'gaming') if value.lower() not in allowed_values: raise AreaError('Invalid status. Possible values: {}'.format( ', '.join(allowed_values))) if value.lower() == 'lfp': value = 'looking-for-players' self.status = value.upper() self.server.area_manager.send_arup_status()
def get_area_by_abbreviation(self, abbreviation): """Get an area by name.""" for area in self.areas: if area.abbreviation == abbreviation: return area if area.is_hub: for sub in area.subareas: if sub.abbreviation == abbreviation: return sub raise AreaError('Area not found.')
def change_background(self, bg): """ Set the background. :param bg: background name :raises: AreaError if `bg` is not in background list """ if bg.lower() not in (name.lower() for name in self.server.backgrounds): raise AreaError('Invalid background name.') self.background = bg self.send_command('BN', self.background)
def save_character_data(self, path='config/character_data.yaml'): """ Save all the character-specific information such as movement delay, keys, etc. :param path: filepath to the YAML file. """ try: with open(path, 'w', encoding='utf-8') as stream: yaml.dump(self.character_data, stream, default_flow_style=False) except: raise AreaError(f'File path {path} is invalid!')