def iterate_core_exports_of_type(self, type_name: str, sort=True) -> Iterator[UEProxyStructure]: ''' Yields a ready-to-use proxy for each class that inherits from `type_name` and exists in the core+DLC of the game. Classes that have a 'Default__' counterpart are excluded from the output. By default the results are sorted by class fullname. ''' # Gather classes of this type in the core # (core path prefixes were pre-calculated earlier) classes: Set[str] = set() for cls_name in find_sub_classes(type_name): if not cls_name.startswith('/Game'): continue if cls_name.startswith('/Game/Mods'): if not any( cls_name.startswith(prefix) for prefix in self.official_mod_prefixes): continue classes.add(cls_name) # The rest of the work is shared yield from self._iterate_exports(classes, sort)
def iterate_mod_exports_of_type(self, type_name: str, modid: str, sort=True, filter=None) -> Iterator[UEProxyStructure]: ''' Yields a ready-to-use proxy for each class that inherits from `type_name` and exists in the specified mod. Classes that have a 'Default__' counterpart are excluded from the output. By default the results are sorted by class fullname. ''' # Look for other mods that should be combined mod_tag = self.loader.get_mod_name(f'/Game/Mods/{modid}/') if not mod_tag: raise ValueError("Mod not found: " + modid) mod_tags: Set[str] = get_aliases_for_mod(mod_tag) mod_tags |= {mod_tag} # Work out the base path for these mods mod_paths = tuple(self.loader.clean_asset_name(f'/Game/Mods/{id}') + '/' for id in mod_tags) # Gather classes of this type in the mod classes: Set[str] = set() for cls_name in find_sub_classes(type_name): if filter and not filter(cls_name): continue for mod_path in mod_paths: if cls_name.startswith(mod_path): classes.add(cls_name) break # The rest of the work is shared yield from self._iterate_exports(classes, sort)
def output_result(result: str): indent = ' ' print(result) if args.subs: for sub_cls_name in find_sub_classes(result): print(f'{indent}{sub_cls_name}') if args.parents: for i, parent_cls_name in enumerate(find_parent_classes(result)): if args.no_script and not parent_cls_name.startswith('/Game'): break print(f'{indent*(i+1)}{parent_cls_name}')
def extract_core(self, _: Path): '''Perform sanity tests on core items.''' # Count species by prefix (/Game/<part> or /Game/Mods/<id>) counter: Counter = Counter() for clsname in find_sub_classes(PrimalItem.get_ue_type()): if not clsname.startswith('/Game'): continue parts = clsname.split('/') if clsname.startswith('/Game/Mods/'): modid = self.manager.loader.get_mod_id(clsname) assert modid parts[3] = modid parts = parts[:4] else: parts = parts[:3] counter.update(['/'.join(parts)]) # Check counts against configured limits overrides = get_overrides() reports = list() for path, min_count in overrides.sanity_checks.min_items.items(): count = counter.get(path, 0) logger.debug('%s = %d (need %d)', path, count, min_count) if count < min_count: reports.append((path, count, min_count)) # If anything failed, report and turn off Git push if not reports: return None # Disable git push to hopefully avoid damaging commits self.manager.config.git.SkipPush = False # Put together a report message header = "Items count sanity check failed! (git push disabled):\n" lines: List[str] = [] for path, count, min_count in reports: lines.append(f" {path} expected {min_count}, found {count}\n") # Log it logger.error(''.join([header] + lines)) # Send it to Discord send_to_discord(log=lines, header='Purlovia failed the items sanity check:')
def inflate_swap_rules(random_class_weights): ''' Mutates a rule set with pre-processed inheritance. Target's "from" field will turn into a list of classes. ''' for rule in random_class_weights: from_class = rule['from'] # Make sure number of weights is equal to the number of targets weights = fix_up_swap_rule_weights(rule) from_classes = [from_class] if not rule.get('exact', False): # Pre-process non-exact match from_classes += find_sub_classes(from_class) rule['exact'] = True rule['from'] = from_classes rule['weights'] = weights
def iterate_mod_exports_of_type(self, type_name: str, modid: str, sort=True) -> Iterator[UEProxyStructure]: ''' Yields a ready-to-use proxy for each class that inherits from `type_name` and exists in the specified mod. Classes that have a 'Default__' counterpart are excluded from the output. By default the results are sorted by class fullname. ''' # Work out the base path for this mod mod_path = self.loader.clean_asset_name(f'/Game/Mods/{modid}') + '/' # Gather classes of this type in the mod classes: Set[str] = set() for cls_name in find_sub_classes(type_name): if not cls_name.startswith(mod_path): continue classes.add(cls_name) # The rest of the work is shared yield from self._iterate_exports(classes, sort)