Esempio n. 1
0
    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)
Esempio n. 2
0
    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)
Esempio n. 3
0
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}')
Esempio n. 4
0
    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:')
Esempio n. 5
0
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
Esempio n. 6
0
    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)