Skip to content

feat(mod): 新增模组关系页,补全内嵌检测与依赖解析#3441

Open
Lokins577 wants to merge 8 commits into
devfrom
feat/jij-detection-completeness
Open

feat(mod): 新增模组关系页,补全内嵌检测与依赖解析#3441
Lokins577 wants to merge 8 commits into
devfrom
feat/jij-detection-completeness

Conversation

@Lokins577

@Lokins577 Lokins577 commented Jul 19, 2026

Copy link
Copy Markdown
Member

修复之前在 #3378 中暴露出的问题并基于此为PCL-CE增加模组关系自动分析能力、模组调整时支持级联功能。

TODO:

  • 自动解析模组依赖
  • 级联处理
  • 下载页前置存在性提示
  • UI实现

resolves #3439

本PR移植自HMCL #6230 ,由Opus4.8对代码进行了审查。

Summary by Sourcery

添加 Jar-in-Jar 模组关系分析、缓存及管理界面,并将具备依赖感知能力的级联处理集成到模组启用/禁用和删除操作中。

新功能:

  • 为每个实例引入持久化的 Jar-in-Jar 解析缓存,加速嵌入模组的发现,并支持后续依赖关系分析。
  • 添加 Jar-in-Jar 依赖索引,根据 Minecraft 版本约束,在已安装、已禁用、打包内置以及缺失模组之间评估依赖状态。
  • 在实例视图中提供专门的 Jar-in-Jar 关系页面,展示嵌入模组树及其依赖状态,并可从模组管理页面跳转。
  • 在禁用或删除模组时启用级联处理,通过检测受影响的依赖模组,为用户提供联动禁用/删除操作的选项。

错误修复:

  • 通过在递归解析过程中强制全局节点预算,防止病态嵌套的 Jar-in-Jar 结构过度展开。

增强改进:

  • 扩展 Jar-in-Jar 解析,支持包括 Quilt 配置和清单声明的嵌入式依赖在内的更多元数据来源,并可检测加载器和目标 Minecraft 版本。
  • 改进从 Fabric 和 Forge/NeoForge 元数据中提取模组依赖的逻辑,忽略平台伪依赖,并更准确地处理可选/仅服务器依赖。
  • 优化崩溃报告中的模组列表,使用清洗后的版本信息并包含嵌套的 Jar-in-Jar 信息,同时利用新的缓存机制。
Original summary in English

Summary by Sourcery

Add Jar-in-Jar mod relationship analysis, caching, and management UI, and integrate dependency-aware cascade handling into mod enable/disable and delete operations.

New Features:

  • Introduce persistent Jar-in-Jar parsing cache per instance to accelerate embedded mod discovery and support later dependency analysis.
  • Add Jar-in-Jar dependency index that evaluates dependency status across installed, disabled, bundled, and missing mods based on Minecraft version constraints.
  • Provide a dedicated Jar-in-Jar relationship page in the instance view to surface embedded mod trees and dependency statuses, with navigation from the mod management page.
  • Enable cascade handling when disabling or deleting mods by detecting affected dependents and offering user choices for joint disable/delete actions.

Bug Fixes:

  • Prevent pathological nested Jar-in-Jar structures from over-expanding by enforcing a global node budget during recursive parsing.

Enhancements:

  • Extend Jar-in-Jar parsing to support additional metadata sources including Quilt config and manifest-declared embedded dependencies, with loader and target Minecraft version detection.
  • Improve mod dependency extraction from Fabric and Forge/NeoForge metadata, ignoring platform pseudo-dependencies and handling optional/server-only dependencies more accurately.
  • Refine crash report mod listing to include cleaned versions and nested Jar-in-Jar information while leveraging the new cache.

@pcl-ce-automation pcl-ce-automation Bot added 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查 size: XL PR 大小评估:超大型 labels Jul 19, 2026
@sourcery-ai

sourcery-ai Bot commented Jul 19, 2026

Copy link
Copy Markdown
Contributor

Reviewer's Guide

添加一个 Jar-in-Jar(嵌入模组)依赖分析系统,包含缓存、UI 和崩溃报告集成,并在模组启用/禁用/删除操作中基于嵌入依赖实现级联行为。

基于 Jar-in-Jar 依赖的模组启用/禁用/删除级联行为序列图

sequenceDiagram
    actor User
    participant PageInstanceCompResource
    participant ModJarInJarIndex

    User->>PageInstanceCompResource: EDMods(modList, isEnable=false)
    activate PageInstanceCompResource
    PageInstanceCompResource->>PageInstanceCompResource: _JijFindAffected(list)
    PageInstanceCompResource->>ModJarInJarIndex: new ModJarInJarIndex(allMods, mcVersion)
    PageInstanceCompResource->>ModJarInJarIndex: FindAffected(targets)
    ModJarInJarIndex-->>PageInstanceCompResource: affectedMods
    PageInstanceCompResource->>PageInstanceCompResource: _AskJijCascade(affectedMods, false)
    PageInstanceCompResource-->>PageInstanceCompResource: choice
    alt choice == 1 (cascade disable)
        PageInstanceCompResource->>PageInstanceCompResource: EDModsCore(list ∪ affectedMods, false)
    else choice == 0 (only selected)
        PageInstanceCompResource->>PageInstanceCompResource: EDModsCore(list, false)
    end
    deactivate PageInstanceCompResource

    User->>PageInstanceCompResource: DeleteMods(modList)
    activate PageInstanceCompResource
    PageInstanceCompResource->>PageInstanceCompResource: _JijFindAffected(list)
    PageInstanceCompResource->>ModJarInJarIndex: FindAffected(targets)
    ModJarInJarIndex-->>PageInstanceCompResource: affectedMods
    PageInstanceCompResource->>PageInstanceCompResource: _AskJijCascade(affectedMods, true)
    PageInstanceCompResource-->>PageInstanceCompResource: choice
    alt choice == 1 (disable affected)
        PageInstanceCompResource->>PageInstanceCompResource: EDModsCore(affectedMods, false)
        PageInstanceCompResource->>PageInstanceCompResource: DeleteModsCore(list)
    else choice == 2 (delete affected)
        PageInstanceCompResource->>PageInstanceCompResource: DeleteModsCore(list ∪ affectedMods)
    else choice == 0 (only selected)
        PageInstanceCompResource->>PageInstanceCompResource: DeleteModsCore(list)
    end
    deactivate PageInstanceCompResource
Loading

模组加载与崩溃报告导出过程中 Jar-in-Jar 缓存使用的序列图

sequenceDiagram
    participant ModLocalComp
    participant ModJarInJar
    participant ModJarInJarCache
    participant CrashReportExporter

    ModLocalComp->>ModJarInJarCache: UseInstance(instancePath)
    ModLocalComp->>ModJarInJar: ResolveCached(path, jar)
    ModJarInJar->>ModJarInJarCache: TryGet(path, lastModified, size)
    alt cache hit
        ModJarInJarCache-->>ModJarInJar: List EmbeddedModNode
        ModJarInJar->>ModLocalComp: _FromNodes(nodes, parentPath)
    else cache miss
        ModJarInJarCache-->>ModJarInJar: null
        ModJarInJar->>ModJarInJar: Resolve(parentPath, jar)
        ModJarInJar-->>ModLocalComp: EmbeddedMods
        ModJarInJar->>ModJarInJarCache: Set(path, lastModified, size, _ToNodes(EmbeddedMods))
    end

    CrashReportExporter->>ModJarInJarCache: UseInstance(instance.PathInstance)
    CrashReportExporter->>CrashReportExporter: _WriteModInfo(reportFolder, instance)
    CrashReportExporter->>ModJarInJarCache: Flush()
Loading

文件级改动

Change Details Files
引入持久化的 Jar-in-Jar 元数据缓存,并在模组和崩溃报告加载嵌入模组的所有路径上使用该缓存。
  • 在现有的 ModJarInJar.Resolve 外面封装一个新的内部方法 _Resolve,该方法接受一个节点数量预算,并增加 Quilt 和基于 manifest 的嵌入 jar 发现,以及加载器检测和 MC 版本提取。
  • 为 ModJarInJar 添加 ResolveCached,用文件指纹(路径、最后修改时间、大小)通过 ModJarInJarCache 读写嵌入模组树。
  • 实现 ModJarInJarCache,采用按实例存储 JarInJar.json、带版本的缓存格式、原子写入,并通过 path+fingerprint 查找条目。
  • 将 ModLocalComp.Load 切换为在填充 EmbeddedMods 时使用 ResolveCached,而不是 Resolve。
  • 在模组资源列表加载以及崩溃报告模组信息导出期间初始化和刷新 ModJarInJarCache。
Plain Craft Launcher 2/Modules/Minecraft/ModJarInJar.cs
Plain Craft Launcher 2/Modules/Minecraft/ModJarInJarCache.cs
Plain Craft Launcher 2/Modules/Minecraft/ModLocalComp.cs
Plain Craft Launcher 2/Modules/Minecraft/CrashAnalysis/Export/CrashReportExporter.cs
改进从模组元数据中解析和表示依赖关系,以支持准确的 Jar-in-Jar 依赖分析。
  • 在填充 Dependencies 时忽略平台/加载器伪依赖(forge、fabric、quilt、java 等),但保留 minecraft 以支持基于 MC 版本的决策。
  • 处理 Fabric depends 条目,其中版本要求可能是字符串或数组,避免出现 JSON 片段作为版本号的情况。
  • 扩展 Forge/NeoForge mods.toml 依赖解析,包含无名称的 dependencies 区块、基于可选/类型的过滤、仅服务器依赖排除,以及通过正则提取 modId 的 inline-table 回退方案。
  • 为 LocalCompFile 添加 SetJijMetadata 方法以及 JijLoader 和 JijTargetMcVersion 字段,以便从缓存中存储嵌入模组元数据,而无需重新加载 jar。
Plain Craft Launcher 2/Modules/Minecraft/ModLocalComp.cs
Plain Craft Launcher 2/Modules/Minecraft/ModJarInJar.cs
Plain Craft Launcher 2/Modules/Minecraft/ModJarInJarDependency.cs
添加 Jar-in-Jar 关系分析索引和 UI,用于可视化以及对基于嵌入模组的依赖发出警告,并在模组管理页面中集成级联操作。
  • 实现 ModJarInJarIndex,支持提供者映射、自打包 ID、基于 MC 版本的嵌入树过滤、类 Maven 的版本范围匹配,以及反向依赖闭包计算(FindAffected)。
  • 添加 JijDepStatus 枚举,用于表示依赖的 Installed/Disabled/Bundled/Missing 状态。
  • 添加 PageInstanceCompJarInJar WPF 页面,用于展示依赖警告、每个模组的依赖状态及嵌入模组树,采用卡片式布局和共享的显示名逻辑。
  • 在 FormMain、ModMain 和 PageInstanceLeft 中接入新的 VersionModJarInJar 页面子类型,并将 BtnManageJarInJar 按钮仅在 Mod 组件类型下导航到新页面。
  • 在 PageInstanceCompResource 中,通过 EDMods/DeleteMods 包装器实现级联启用/禁用/删除,这些包装器调用 ModJarInJarIndex.FindAffected,并使用单选框对话框来选择级联行为。
Plain Craft Launcher 2/Modules/Minecraft/ModJarInJarDependency.cs
Plain Craft Launcher 2/Pages/PageInstance/PageInstanceCompJarInJar.xaml.cs
Plain Craft Launcher 2/Pages/PageInstance/PageInstanceCompJarInJar.xaml
Plain Craft Launcher 2/Pages/PageInstance/PageInstanceCompResource.xaml.cs
Plain Craft Launcher 2/Pages/PageInstance/PageInstanceLeft.xaml.cs
Plain Craft Launcher 2/FormMain.xaml.cs
Plain Craft Launcher 2/Modules/ModMain.cs
优化崩溃报告中的模组列表输出,使其更好地反映嵌入模组并处理占位版本号。
  • 在崩溃报告导出中扫描模组之前调用 ModJarInJarCache.UseInstance,并在结束后调用 Flush,以持久化任何已解析的嵌入数据。
  • 通过 _CleanVersion 清洗崩溃报告中的模组版本字符串,隐藏诸如 ${version} 或 ${file.jarVersion} 之类尚未解析的模板占位符。
  • 在列出顶层模组和嵌入模组时都应用 _CleanVersion。
Plain Craft Launcher 2/Modules/Minecraft/CrashAnalysis/Export/CrashReportExporter.cs

可能关联的问题

  • [C#]: 无法于自定义主页打开特定文件夹 #3379:该 PR 实现了该 issue 中请求的模组关系解析、Jar-in-Jar 级联行为和 UI,与其 TODO 列表相匹配。
  • #N/A:该 PR 实现了新的依赖和关系分析 UI 与逻辑,有效完成了所请求的依赖显示重构。

Tips and commands

Interacting with Sourcery

  • 触发新的评审: 在 pull request 中评论 @sourcery-ai review
  • 继续讨论: 直接回复 Sourcery 的评审评论。
  • 从评审评论生成 GitHub issue: 通过回复评审评论请求 Sourcery 从该评论创建 issue。也可以在评审评论中回复 @sourcery-ai issue 来创建对应的 issue。
  • 生成 pull request 标题: 在 pull request 标题中任意位置写上 @sourcery-ai,即可随时生成一个标题。也可以在 pull request 中评论 @sourcery-ai title 来(重新)生成标题。
  • 生成 pull request 摘要: 在 pull request 正文中任意位置写上 @sourcery-ai summary,即可在该位置生成 PR 摘要。也可以在 pull request 中评论 @sourcery-ai summary 来在任意时间(重新)生成摘要。
  • 生成 Reviewer's Guide: 在 pull request 中评论 @sourcery-ai guide,即可在任意时间(重新)生成评审指南。
  • 解决所有 Sourcery 评论: 在 pull request 中评论 @sourcery-ai resolve,即可解决所有 Sourcery 评论。如果你已经处理完所有评论且不再希望看到它们,这会很有用。
  • 关闭所有 Sourcery 评审: 在 pull request 中评论 @sourcery-ai dismiss,即可关闭所有现有的 Sourcery 评审。特别适合在你希望以全新的评审开始时使用——别忘了再评论 @sourcery-ai review 以触发新的评审!

Customizing Your Experience

访问你的 dashboard 以:

  • 启用或停用评审特性,例如 Sourcery 自动生成的 pull request 摘要、评审指南等。
  • 更改评审语言。
  • 添加、移除或编辑自定义评审指令。
  • 调整其他评审设置。

Getting Help

Original review guide in English

Reviewer's Guide

Adds a Jar-in-Jar (embedded mod) dependency analysis system with caching, UI, and crash report integration, plus cascaded enable/disable/delete behavior for mods based on embedded dependencies.

Sequence diagram for cascaded mod enable/disable/delete based on Jar-in-Jar dependencies

sequenceDiagram
    actor User
    participant PageInstanceCompResource
    participant ModJarInJarIndex

    User->>PageInstanceCompResource: EDMods(modList, isEnable=false)
    activate PageInstanceCompResource
    PageInstanceCompResource->>PageInstanceCompResource: _JijFindAffected(list)
    PageInstanceCompResource->>ModJarInJarIndex: new ModJarInJarIndex(allMods, mcVersion)
    PageInstanceCompResource->>ModJarInJarIndex: FindAffected(targets)
    ModJarInJarIndex-->>PageInstanceCompResource: affectedMods
    PageInstanceCompResource->>PageInstanceCompResource: _AskJijCascade(affectedMods, false)
    PageInstanceCompResource-->>PageInstanceCompResource: choice
    alt choice == 1 (cascade disable)
        PageInstanceCompResource->>PageInstanceCompResource: EDModsCore(list ∪ affectedMods, false)
    else choice == 0 (only selected)
        PageInstanceCompResource->>PageInstanceCompResource: EDModsCore(list, false)
    end
    deactivate PageInstanceCompResource

    User->>PageInstanceCompResource: DeleteMods(modList)
    activate PageInstanceCompResource
    PageInstanceCompResource->>PageInstanceCompResource: _JijFindAffected(list)
    PageInstanceCompResource->>ModJarInJarIndex: FindAffected(targets)
    ModJarInJarIndex-->>PageInstanceCompResource: affectedMods
    PageInstanceCompResource->>PageInstanceCompResource: _AskJijCascade(affectedMods, true)
    PageInstanceCompResource-->>PageInstanceCompResource: choice
    alt choice == 1 (disable affected)
        PageInstanceCompResource->>PageInstanceCompResource: EDModsCore(affectedMods, false)
        PageInstanceCompResource->>PageInstanceCompResource: DeleteModsCore(list)
    else choice == 2 (delete affected)
        PageInstanceCompResource->>PageInstanceCompResource: DeleteModsCore(list ∪ affectedMods)
    else choice == 0 (only selected)
        PageInstanceCompResource->>PageInstanceCompResource: DeleteModsCore(list)
    end
    deactivate PageInstanceCompResource
Loading

Sequence diagram for Jar-in-Jar cache usage during mod loading and crash report export

sequenceDiagram
    participant ModLocalComp
    participant ModJarInJar
    participant ModJarInJarCache
    participant CrashReportExporter

    ModLocalComp->>ModJarInJarCache: UseInstance(instancePath)
    ModLocalComp->>ModJarInJar: ResolveCached(path, jar)
    ModJarInJar->>ModJarInJarCache: TryGet(path, lastModified, size)
    alt cache hit
        ModJarInJarCache-->>ModJarInJar: List EmbeddedModNode
        ModJarInJar->>ModLocalComp: _FromNodes(nodes, parentPath)
    else cache miss
        ModJarInJarCache-->>ModJarInJar: null
        ModJarInJar->>ModJarInJar: Resolve(parentPath, jar)
        ModJarInJar-->>ModLocalComp: EmbeddedMods
        ModJarInJar->>ModJarInJarCache: Set(path, lastModified, size, _ToNodes(EmbeddedMods))
    end

    CrashReportExporter->>ModJarInJarCache: UseInstance(instance.PathInstance)
    CrashReportExporter->>CrashReportExporter: _WriteModInfo(reportFolder, instance)
    CrashReportExporter->>ModJarInJarCache: Flush()
Loading

File-Level Changes

Change Details Files
Introduce persistent Jar-in-Jar metadata caching and use it wherever mods and crash reports load embedded mods.
  • Wrap existing ModJarInJar.Resolve with a new internal _Resolve that accepts a node budget and adds Quilt and manifest-based embedded jar discovery plus loader detection and MC version extraction.
  • Add ResolveCached to ModJarInJar to read/write embedded mod trees via ModJarInJarCache using file fingerprint (path, last modified, size).
  • Implement ModJarInJarCache with per-instance JarInJar.json storage, versioned cache format, atomic writes, and entry lookup by path+fingerprint.
  • Switch ModLocalComp.Load to use ResolveCached instead of Resolve when populating EmbeddedMods.
  • Initialize and flush ModJarInJarCache during mod resource list loading and crash report mod info export.
Plain Craft Launcher 2/Modules/Minecraft/ModJarInJar.cs
Plain Craft Launcher 2/Modules/Minecraft/ModJarInJarCache.cs
Plain Craft Launcher 2/Modules/Minecraft/ModLocalComp.cs
Plain Craft Launcher 2/Modules/Minecraft/CrashAnalysis/Export/CrashReportExporter.cs
Improve dependency parsing and representation from mod metadata to support accurate Jar-in-Jar dependency analysis.
  • Ignore platform/loader pseudo-dependencies (forge, fabric, quilt, java, etc.) when populating Dependencies, but keep minecraft for MC-version-based decisions.
  • Handle Fabric depends entries whose version requirement may be a string or array, avoiding JSON-blob versions.
  • Expand Forge/NeoForge mods.toml dependency parsing to include unnamed dependencies sections, optional/type-based filtering, server-only exclusion, and an inline-table fallback via regex for modId.
  • Add SetJijMetadata plus JijLoader and JijTargetMcVersion fields to LocalCompFile to store embedded mod metadata from cache without re-loading jars.
Plain Craft Launcher 2/Modules/Minecraft/ModLocalComp.cs
Plain Craft Launcher 2/Modules/Minecraft/ModJarInJar.cs
Plain Craft Launcher 2/Modules/Minecraft/ModJarInJarDependency.cs
Add a Jar-in-Jar relationship analysis index and UI for visualising and warning about embedded-mod-based dependencies, and integrate cascaded operations in the mod management page.
  • Implement ModJarInJarIndex with provider mapping, self-bundled IDs, MC-version-aware embedded tree filtering, Maven-style version range matching, and reverse dependency closure computation (FindAffected).
  • Add JijDepStatus enum to represent Installed/Disabled/Bundled/Missing states for dependencies.
  • Add PageInstanceCompJarInJar WPF page to show dependency warnings, dependency status per mod, and embedded-mod trees, with card-based layout and shared display-name logic.
  • Wire a new VersionModJarInJar page subtype into FormMain, ModMain, and PageInstanceLeft, and hook BtnManageJarInJar to navigate to the new page only for Mod comp type.
  • Implement cascaded enable/disable/delete in PageInstanceCompResource via EDMods/DeleteMods wrappers that call ModJarInJarIndex.FindAffected and a radio-button dialog to choose cascade behavior.
Plain Craft Launcher 2/Modules/Minecraft/ModJarInJarDependency.cs
Plain Craft Launcher 2/Pages/PageInstance/PageInstanceCompJarInJar.xaml.cs
Plain Craft Launcher 2/Pages/PageInstance/PageInstanceCompJarInJar.xaml
Plain Craft Launcher 2/Pages/PageInstance/PageInstanceCompResource.xaml.cs
Plain Craft Launcher 2/Pages/PageInstance/PageInstanceLeft.xaml.cs
Plain Craft Launcher 2/FormMain.xaml.cs
Plain Craft Launcher 2/Modules/ModMain.cs
Refine crash report mod listing output to better reflect embedded mods and handle placeholder versions.
  • Include ModJarInJarCache.UseInstance before scanning mods in crash report export and Flush afterwards to persist any parsed embedded data.
  • Sanitize mod version strings in crash reports via _CleanVersion to hide unresolved template tokens like ${version} or ${file.jarVersion}.
  • Apply _CleanVersion when listing both top-level mods and embedded mods in crash reports.
Plain Craft Launcher 2/Modules/Minecraft/CrashAnalysis/Export/CrashReportExporter.cs

Possibly linked issues

  • [C#]: 无法于自定义主页打开特定文件夹 #3379: The PR implements the mod relationship parsing, Jar-in-Jar cascading, and UI requested in the issue, matching its TODO list.
  • #N/A: PR implements new dependency and relationship analysis UI and logic, effectively fulfilling the requested dependency display refactor.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - 我已经给出了一些高层次的反馈:

  • 平台/加载器的依赖 ID 集合(ModLocalComp 中的 _IgnoredDepIds 和 ModJarInJarIndex 中的 _platformIds)目前是重复且略有差异的;建议将这些 ID 集中到一个共享的辅助工具中,以避免未来出现不一致。
  • PageInstanceCompJarInJar.RefreshList 假设 ModLocalComp.compResourceListLoader.output 已经被填充;如果该页面可能在加载器完成之前就被打开,请添加空/空集合检查,或挂钩到加载器生命周期中,以避免潜在的空引用或过期数据。
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The platform/loader dependency ID sets (_IgnoredDepIds in ModLocalComp and _platformIds in ModJarInJarIndex) are duplicated and slightly divergent; consider centralizing these IDs into a single shared helper to avoid future inconsistencies.
- PageInstanceCompJarInJar.RefreshList assumes ModLocalComp.compResourceListLoader.output is populated; if this page can be opened before the loader has finished, add a null/empty check or hook into the loader lifecycle to avoid potential null references or stale data.

Sourcery 对开源项目是免费的——如果你觉得我们的评审有帮助,欢迎分享 ✨
帮我变得更有用!请在每条评论上点击 👍 或 👎,我会根据你的反馈改进后续的评审。
Original comment in English

Hey - I've left some high level feedback:

  • The platform/loader dependency ID sets (_IgnoredDepIds in ModLocalComp and _platformIds in ModJarInJarIndex) are duplicated and slightly divergent; consider centralizing these IDs into a single shared helper to avoid future inconsistencies.
  • PageInstanceCompJarInJar.RefreshList assumes ModLocalComp.compResourceListLoader.output is populated; if this page can be opened before the loader has finished, add a null/empty check or hook into the loader lifecycle to avoid potential null references or stale data.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The platform/loader dependency ID sets (_IgnoredDepIds in ModLocalComp and _platformIds in ModJarInJarIndex) are duplicated and slightly divergent; consider centralizing these IDs into a single shared helper to avoid future inconsistencies.
- PageInstanceCompJarInJar.RefreshList assumes ModLocalComp.compResourceListLoader.output is populated; if this page can be opened before the loader has finished, add a null/empty check or hook into the loader lifecycle to avoid potential null references or stale data.

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 63c10f465e

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread Plain Craft Launcher 2/Modules/Minecraft/ModLocalComp.cs Outdated
@pcl-ce-automation pcl-ce-automation Bot added size: XXL PR 大小评估:巨型 and removed size: XL PR 大小评估:超大型 labels Jul 25, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 05ed984f7c

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread Plain Craft Launcher 2/Modules/Minecraft/ModJarInJarDependency.cs Outdated
Comment thread Plain Craft Launcher 2/Modules/Minecraft/ModJarInJarDependency.cs Outdated
Comment thread Plain Craft Launcher 2/Modules/Minecraft/ModLocalComp.cs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 473f259986

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread Plain Craft Launcher 2/Modules/Minecraft/McConstraintMatcher.cs Outdated
Comment thread Plain Craft Launcher 2/Modules/Minecraft/ModJarInJarDependency.cs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8d68a6393a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread Plain Craft Launcher 2/Modules/Minecraft/ModLocalComp.cs Outdated
Comment thread Plain Craft Launcher 2/Modules/Minecraft/McConstraintMatcher.cs Outdated
Comment thread Plain Craft Launcher 2/Pages/PageInstance/PageInstanceCompJarInJar.xaml.cs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: fb229015d7

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread Plain Craft Launcher 2/Modules/Minecraft/ModJarInJarDependency.cs Outdated
Comment thread Plain Craft Launcher 2/Modules/Minecraft/ModJarInJarDependency.cs Outdated
Comment thread Plain Craft Launcher 2/Pages/PageInstance/PageInstanceCompJarInJar.xaml.cs Outdated

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 15c15f033a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +1695 to +1696
var seg = headerL.Substring("dependencies.".Length).Trim('"', ' ');
if (seg != ownModIdL && jarModIds.Contains(seg)) continue; // 兄弟 mod 的依赖段

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Include every mod declared by a multi-mod JAR

When a Forge/NeoForge JAR contains multiple [[mods]] entries, this branch explicitly skips every dependencies.<secondModId> section, while the earlier selection retains only the first mod ID. The second mod is therefore neither registered as a provider nor represented as a dependent, so its prerequisites are absent from the relation page and removing one of them produces no cascade warning. Associate every declared mod ID and its dependency sections with the physical host JAR instead of dropping sibling sections.

Useful? React with 👍 / 👎.

Comment on lines +94 to +96
Raw = g.Select(r => r.Raw).FirstOrDefault(v => v is not null),
Optional = g.All(r => r.Optional),
Loader = g.Select(r => r.Loader).FirstOrDefault(l => l is not null)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Retain every same-ID dependency requirement

When the host and multiple embedded mods require the same ID with different ranges or loader dialects, this keeps only the first non-null range and first loader, silently discarding the other requirements. For example, if the retained row requires >=2 but is already unsatisfied while another embedded row requires >=1 from an installed 1.5 provider, FindAffected sees no active provider for the retained row and emits no cascade when 1.5 is removed, even though the second embedded mod loses a working prerequisite. Keep the rows separate or evaluate all requirements independently.

Useful? React with 👍 / 👎.

Comment on lines +125 to +127
var ver = providerVersion.Trim();
if (ver.Length == 0 || !char.IsDigit(ver[0])) return true;
if (McConstraintMatcher.Satisfies(dep.Raw, dep.Loader, ver)) return true;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Compare v-prefixed provider versions

When a provider reports a common version such as v2.0, this digit gate treats it as unknown and immediately accepts it against every constraint; for example, v2.0 satisfies <2.0. This bypasses McConstraintMatcher, even though that matcher explicitly strips a leading v, causing incompatible providers to be reported as installed and suppressing missing-version and cascade warnings. Strip the supported prefix before this gate or let the matcher evaluate it.

Useful? React with 👍 / 👎.

if (aPre < 0 && bPre < 0) return 0;
if (aPre < 0) return 1; // a 无预发布 > b(有预发布)
if (bPre < 0) return -1; // a 有预发布 < b(正式版)
return McVersionComparer.CompareVersion(a.Substring(aPre + 1), b.Substring(bPre + 1));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply SemVer ordering within prereleases

Although the new branches distinguish stable versions from prereleases, prerelease identifiers are still delegated to McVersionComparer, which orders numeric tokens above alphabetic tokens. SemVer requires numeric identifiers to have lower precedence, so a Fabric constraint such as >=1.0.0-alpha incorrectly accepts provider 1.0.0-1, marking an incompatible provider installed and affecting cascade calculations. Compare dot-separated prerelease identifiers using SemVer's numeric/non-numeric rules.

Useful? React with 👍 / 👎.

Comment on lines +71 to +73
if (!string.IsNullOrEmpty(m.ModId)) _AddProvider(m.ModId, m, m.Version);
foreach (var n in nodes.Where(n => !string.IsNullOrEmpty(n.ModId)))
_AddProvider(n.ModId, m, n.Version);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Register Fabric provided aliases

When a Fabric mod declares an alias through provides and another mod depends on that alias, only the provider's primary ModId is added here because the metadata parser never retains the provided IDs. The relation page consequently reports the alias dependency as missing, and disabling the provider produces no cascade warning. Register all Fabric provides IDs as providers of the same host and version.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 10f669e338

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

{
if (_SelfBundleSatisfies(mod, dep)) return JijDepStatus.Bundled;
_providers.TryGetValue(_Norm(dep.DepId), out var provs);
var others = provs?.Where(p => p.Mod != mod).ToList() ?? new List<(CompFile Mod, string Version)>();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Treat same-JAR aliases as self-provided

When a Forge/NeoForge JAR declares sibling mods A and B and A requires B, B is now registered through ProvidedIds, but its provider is associated with the same CompFile as A and this filter removes it. Since _SelfBundleSatisfies only considers embedded nodes, the relations page reports B as missing even though both mods are loaded from the same JAR. The fresh evidence beyond the earlier multi-mod issue is that sibling IDs are registered now, but are discarded specifically during analysis.

Useful? React with 👍 / 👎.

Comment on lines 1488 to +1495
if (fabricObject.ContainsKey("depends"))
foreach (var dep in (JsonObject)fabricObject["depends"])
AddDependency(dep.Key, dep.Value.ToString());
{
var ver = dep.Value is JsonArray arr
? string.Join(" || ",
arr.Select(x => x?.ToString()).Where(x => !string.IsNullOrEmpty(x)))
: dep.Value?.ToString();
AddDependency(dep.Key, string.IsNullOrEmpty(ver) ? null : ver);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Parse Fabric optional dependency maps

When a Fabric mod declares optional relationships through the standard recommends or suggests maps, this parser traverses only depends, so those entries never reach DependencyRaw or OptionalDependencies. The new relations page consequently omits Fabric optional dependencies entirely, despite displaying optional Forge dependencies; parse these maps with optional: true as well.

Useful? React with 👍 / 👎.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c13a6af279

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +145 to +147
var missing = probed.Where(x => x.status == JijDepStatus.Missing).Select(x => x.d.DepId).ToList();
var mismatch = probed.Where(x => x.status == JijDepStatus.VersionMismatch)
.Select(x => x.d.Raw is null ? x.d.DepId : x.d.DepId + " " + x.d.Raw).ToList();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Warn when every satisfying provider is disabled

When an enabled mod's required dependency has a version-compatible provider whose file is disabled, Analyze returns JijDepStatus.Disabled, but this warning filter includes only Missing and VersionMismatch. The dependency is unavailable at runtime, yet the warnings section remains empty, so include the disabled state among the missing-prerequisite warnings.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size: XXL PR 大小评估:巨型 🛠️ 等待审查 Pull Request 已完善,等待维护者或负责人进行代码审查

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[C#]: 为模组管理增加模组关系解析及级联处理

1 participant