跳转至

17. 依赖拉取(Dependency Resolution)

这一章讲「怎么把别人的模组/库弄进自己的工程」。 从最常用的模组平台、到 GitHub、到本地 jar、再到自建 Maven,四种来源全覆盖,最后给排错手册和实战案例。

本章示例环境:MC 1.20.1 / Forge。不同构建插件(ForgeGradle、ModDevGradle、Fabric Loom)的写法有差异,文中会点明。


0. 先把概念理清

Gradle 拉依赖只需要两样东西:

  1. 仓库(repository):去哪里找 —— repositories { ... }
  2. 坐标(coordinate):找什么 —— group:artifact:version,例如 maven.modrinth:fpsmatch:1.2.5
repositories {                 ← 去哪找
    maven { url = "https://api.modrinth.com/maven" }
}
dependencies {                 ← 找什么 + 什么时候用
    modImplementation "maven.modrinth:fpsmatch:1.2.5"
}

0.1 依赖配置(configuration)速查

配置 作用 备注
implementation 编译期 + 运行期依赖 普通 Java 库最常用
api implementation,且把依赖暴露给下游 只在做库/多模块时用
compileOnly 只在编译期,不进运行期 可选兼容模组常用
runtimeOnly 只在运行期 需要它在游戏中存在、但代码里不直接引用
annotationProcessor 注解处理器 mixin 注解处理器、Lombok 等
modImplementation 模组依赖(编译 + 运行,会被当 mod 处理) NeoForge ModDevGradle / Fabric Loom 提供
modCompileOnly / modRuntimeOnly 模组的编译期/运行期版 同上
fg.deobf(...) ForgeGradle:把模组依赖反混淆 ForgeGradle 专用包装
jarJar(...) 把依赖内嵌进你的 jar 需要 neoforged jarjar 插件(NeoForge 自带)
files(...) / fileTree(...) 本地文件当依赖 见第 3 章

⚠️ 最容易踩的坑modImplementationFabric Loom / NeoForge ModDevGradle 的写法。 在旧版 ForgeGradlenet.minecraftforge.gradle)工程里写它,会直接报 Could not find method modImplementation()。老工程要用 implementation fg.deobf("...")

0.2 modImplementation 到底谁能用?(版本对照)

关键结论:modImplementation 跟 MC/Forge 版本无关,跟「构建插件」绑定

构建插件(plugin id) mod* 配置? 支持的 MC / 加载器
net.neoforged.moddev.legacyforge MinecraftForge / 原版 1.17 ~ 1.20.1(1.20.1 就是它的上限)
net.neoforged.moddev NeoForge 版本线
net.minecraftforge.gradle(ForgeGradle,含 5.x/6.x) ❌ 永远没有 全部 Forge 版本,只能用 fg.deobf(...)
fabric-loom Fabric 全系(不适用 Forge)
architectury-loom Fabric + Forge 多加载器项目

要在 MC 1.20.1 Forge 上写 modImplementation,唯一正解是换用 legacyforge 插件:

plugins {
    id 'net.neoforged.moddev.legacyforge' version '2.0.140'
}

legacyForge {
    version = "1.20.1-47.4.10"          // 写法固定为 <mc>-<forge>
    runs {
        client { client() }
        server { server(); programArgument '--nogui' }
    }
    mods { "yourmodid" { sourceSet sourceSets.main } }
}

dependencies {
    modImplementation('net.ptcrys:FPSMatch') { transitive = false }
    modCompileOnly "maven.modrinth:tacz-tweaks:2.11.2"
    modRuntimeOnly "maven.modrinth:kotlin-for-forge:4.11.0"
}

插件版本怎么选

  • 该插件与普通 ModDevGradle 同版本号发布,走 2.0.x 系列:插件门户上可见最早 2.0.50-beta,官方 LEGACY.md 示例用 2.0.28-beta,最新到 2.0.147; 方块攻势 / FPSMatch 1.3.0 用的是 2.0.140(稳妥选择)。
  • 它是「addon 插件」:普通 net.neoforged.moddev 的 API 在同一个工程里也能一起用。
  • 环境要求:Gradle 8.x + JDK 17

换过去之后必须知道的三件事

  1. mod* 配置不传递依赖(官方明确说明):FPSMatch 要的 lodlib2 / ModernUI / KotlinForForge 必须在自己的工程里再声明一遍
  2. 生产打包会自动重混淆回 SRG(开发用 Mojang 官方名,发布用 SRG)——上传平台时用 reobfJar 的产物,未重混淆的原始 jar 会被放到 build/devlibs
  3. 用了 legacyforge 就不要再包 fg.deobf(...):没有那个扩展,会报 Could not find method fg.deobf()(反过来也一样,ForgeGradle 工程里没有 modImplementation)。

0.3 四种来源总览

来源 适合什么 稳定性 典型坐标
模组平台(Modrinth / CurseForge) 正式发布过的模组 maven.modrinth:<slug>:<ver>
GitHub 快照版、只有源码的项目、开发者自用 com.github.用户:仓库:tag(JitPack)或本地 jar
本地 jar(libs/ 私有/未发布/平台拿不到 最高(离线可用) files('libs/x.jar')
自建 Maven 团队/服务器内部共享、私有制品 看运维 com.你:你的库:1.0.0

1. 模组平台拉取

1.1 Modrinth Maven

  • 仓库地址:https://api.modrinth.com/maven
  • 坐标:maven.modrinth:<项目 slug>:<版本号>
repositories {
    maven {
        name = "Modrinth"
        url = "https://api.modrinth.com/maven"
        content { includeGroup "maven.modrinth" }   // 只让这个仓库处理 maven.modrinth 组,避免拖慢解析
    }
}

dependencies {
    // 正式版
    modImplementation "maven.modrinth:fpsmatch:1.2.5"
    // 可选依赖(只在编译期,玩家装不装都行)
    modCompileOnly "maven.modrinth:tacz-tweaks:2.11.2"
    // 只在运行期(测试用)
    modRuntimeOnly "maven.modrinth:kotlin-for-forge:4.11.0"
}

要点

  • <slug> 是项目网址末段modrinth.com/mod/fpsmatch),不是项目 ID。
  • <版本号> 是 Modrinth 页面上的 Version number不一定等于 jar 文件名里的版本。拿不准就去查:
# 列出某项目在 1.20.1 / forge 下的全部版本
curl -s "https://api.modrinth.com/v2/project/fpsmatch/version?loaders=%5B%22forge%22%5D&game_versions=%5B%221.20.1%22%5D" \
  | python3 -c "import json,sys; [print(v['version_number'], v['date_published']) for v in json.load(sys.stdin)]"
  • 有些项目在 Modrinth 上没有上传(例如 FPSMatch 1.3.0 至今只有 GitHub 快照)——那就得走第 2 章。

1.2 CurseForge(CurseMaven)

  • 仓库地址:https://www.cursemaven.com
  • 坐标:curse.maven:<项目ID>:<文件ID>(带名字的写法也行:curse.maven:fpsmatch-1331710:7109977
repositories {
    maven {
        name = "CurseMaven"
        url = "https://www.cursemaven.com"
        content { includeGroup "curse.maven" }
    }
}
dependencies {
    implementation fg.deobf("curse.maven:fpsmatch-1331710:7109977")   // ForgeGradle 写法
}

怎么拿两个 ID

要找 在哪看
项目 ID CurseForge 项目页 → 右侧 About 里的 Project ID(如 FPSMatch = 1331710
文件 ID 进入具体文件页,URL 末尾或右侧 File ID(如 1.2.5 = 7109977

注意

  • CurseMaven 是第三方镜像服务,偶尔抽风(503 / 解析慢);能给 Modrinth 就用 Modrinth。
  • 只有公开文件ID 能用;被作者设为私有/未审核的文件拉不到。
  • 文件名里带 + 之类的字符时,Gradle 可能解析异常,优先用数字 ID 写法。

1.3 作者/官方自建 Maven(公开可读的那种)

不少模组作者会挂自己的 Maven(常是 Reposilite / Nexus),比如本文档反复用到的:

repositories {
    maven {
        name = "PTCrysReleases"
        url = uri("https://maven.ptcrys.net/releases")
    }
}
dependencies {
    implementation("com.lowdragmc:ldlib2-forge:2.2.27+1.20.1")
}

怎么判断能不能匿名用

# 目录列表 200 → 匿名可读;401/403 → 需要账号
curl -s -o /dev/null -w "%{http_code}\n" https://maven.ptcrys.net/releases/com/lowdragmc/ldlib2-forge/maven-metadata.xml
# 200 → 能直接拉
  • 登录才发现:401 + {"status":401,"message":"Missing authorization credentials"}(Reposilite 的典型响应)。
  • 有些仓库匿名能读、但匿名不能发(发布要 token)——这很正常。

1.4 平台选择建议

  1. 有正式发布要求(玩家会装)→ 走 Modrinth / CurseForge,版本语义清楚;
  2. 只有快照(Snapshot)→ GitHub Releases(第 2 章);
  3. 私有/平台没有 → 本地 jar 或自建 Maven(第 3、4 章);
  4. 编译期用能拿到的最稳定来源,运行期让玩家用同一份 jar,避免版本漂移。

2. GitHub 拉取

GitHub 的"依赖"分四种截然不同的拿法,别混。

2.1 GitHub Releases:下 jar(最通用)

绝大多数模组的快照都发在 Releases,下载直链有稳定规律:

https://github.com/<用户>/<仓库>/releases/download/<tag>/<文件名>
https://github.com/<用户>/<仓库>/releases/latest/download/<文件名>   # 永远取最新

实例(FPSMatch 1.3.0 快照):

https://github.com/PhasetransCrystal/FPSMatch/releases/download/fpsmatch-1.20.1-1.3.0-forge-snapshot-20260910-0945/fpsmatch-1.20.1-1.3.0-forge-snapshot.jar

查最新 Release(脚本化必备)

curl -s https://api.github.com/repos/PhasetransCrystal/FPSMatch/releases/latest \
  | python3 -c "import json,sys; r=json.load(sys.stdin); print(r['tag_name']); [print(' ', a['name'], a['browser_download_url']) for a in r['assets']]"

下下来怎么用

dependencies {
    // ForgeGradle 老工程:包一层 fg.deobf 反混淆
    implementation fg.deobf(files("libs/fpsmatch-1.20.1-1.3.0-forge-snapshot.jar"))
    // ModDevGradle / Loom:直接当模组依赖
    // modImplementation files("libs/fpsmatch-1.20.1-1.3.0-forge-snapshot.jar")
}

国内下载小技巧(网络不稳时)

  • curl -L --retry 3 -o 而不是浏览器点;断点续传 -C -
  • 把 jar 下载一次存到自己服务器,团队分发走自己的静态目录(见 4.4)。
  • 第三方 GitHub 加速代理能不碰就不碰(不知道中间有没有改包),要用必须校验 sha256
sha256sum libs/xxx.jar     # 与 Release 页面/API 的 digest 对比

2.2 JitPack:把 GitHub 仓库当场 maven

JitPack 会去拉你的 GitHub 仓库、现场构建、再把产物当 maven 制品发出来。

repositories {
    maven { url = "https://jitpack.io" }
}
dependencies {
    implementation "com.github.<用户>:<仓库>:<tag>"        // 稳定版,用 tag 或 commit
    implementation "com.github.<用户>:<仓库>:main-SNAPSHOT" // 分支最新(缓存敏感,不建议生产用)
}

要点与限制

  • 项目必须是 Gradle 工程且 JitPack 能构建成功;构建失败就去看 https://jitpack.io/com/github/<用户>/<仓库>/<tag>/build.log
  • 首次请求会触发构建,第一次特别慢,可能要等几分钟到十几分钟。
  • Minecraft 模组基本不适用:模组工程依赖 MC/Forge,JitPack 上没有这套环境,且模组需要 SRG/反混淆处理。JitPack 更适合纯 Java 工具库

2.3 源码联调:submodule + composite build(模组开发最推荐)

当你要改别人的模组源码、或者受不了发布 jar 在 dev 环境的各种坑(典型:mixin 写死 SRG 名、refmap 不全),就把源码拉进工程一起编译。

① 拉源码(git submodule 或直接 clone)

# 方式一:作为子模块(推荐,版本可锁定,队友一条命令同步)
git submodule add https://github.com/PhasetransCrystal/FPSMatch.git FPSMatch

# 方式二:直接 clone 一份(不打算推回上游时更省事)
git clone --depth 1 https://github.com/PhasetransCrystal/FPSMatch.git FPSMatch
rm -rf FPSMatch/.git

② settings.gradle:把源码目录变成一个"复合构建"

includeBuild('FPSMatch') {
    dependencySubstitution {
        // 把坐标 net.ptcrys:FPSMatch 替换成这个源码工程
        substitute module('net.ptcrys:FPSMatch') using project(':')
    }
}

③ build.gradle:照常写坐标即可

dependencies {
    modImplementation('net.ptcrys:FPSMatch') { transitive = false }
}

三个必须知道的特性

  1. dev 环境不再加载生产 jar,mixin 用可读名编译 → 从根上绕开 SRG/refmap 崩溃;
  2. composite build 不传递对方的运行时依赖!FPSMatch 自己要的 ldlib2modernuikotlinforforge 你得在自己工程里再声明一遍,否则 runClientMissing or mandatory dependencies
  3. 源码工程自己也可能跑 gradle(首次同步慢),改它的代码即改即生效,很适合边调边改。

2.4 GitHub Packages

GitHub 自带的 Maven 仓库,适合私有库:

repositories {
    maven {
        url = uri("https://maven.pkg.github.com/<所有者>/<仓库>")
        credentials {
            username = project.findProperty("gpr.user") ?: System.getenv("GITHUB_ACTOR")
            password = project.findProperty("gpr.key")  ?: System.getenv("GITHUB_TOKEN")  // 需要 read:packages 权限
        }
    }
}

限制:必须有 PAT;对 MC 模组同样存在构建环境问题,主要用于自己写的 Java 库。

2.5 附:把 GitHub 当普通文件源

不是 jar、而是资源文件(配置、数据包、脚本)时,可以直接当静态文件拉:

https://raw.githubusercontent.com/<用户>/<仓库>/<分支>/<路径>
https://cdn.jsdelivr.net/gh/<用户>/<仓库>@<tag>/<路径>       # CDN 加速版
https://github.com/<用户>/<仓库>/archive/refs/tags/<tag>.zip  # 整包 zip

3. 本地拉取(本地 jar)

3.1 三种写法

dependencies {
    // ① 点名单个 jar(最常用,明确可控)
    implementation files("libs/ldlib2-forge-1.20.1-2.2.27-devfix.jar")

    // ② 整个目录的 jar 全进来
    implementation fileTree(dir: "libs", include: ["*.jar"])

    // ③ 用 flatDir 仓库 + 按名字引用(不推荐)
}
repositories {
    flatDir { dirs "libs" }      // ③ 的配套
}
// implementation name: "ldlib2-forge-1.20.1-2.2.27-all"   ← 无元数据、无传递依赖,容易翻车

推荐 ①:哪个 jar 干什么一目了然,出问题好排查;fileTree 适合"一堆小库塞进来"的场景。

3.2 要不要 fg.deobf

构建插件 写法
ForgeGradle(旧,net.minecraftforge.gradle implementation fg.deobf(files("libs/x.jar"))必须,否则引用不到 MC 的类名
ModDevGradle / legacyforge(1.3.0 起的 FPSMatch 官方模板) modImplementation files("libs/x.jar") — 插件自己处理映射,不要再包 fg.deobf

写错的表现:fg.deobf 在 ModDevGradle 下找不到方法;或者反过来,FORGEGradle 下不反混淆 → 编译期「找不到符号」。

3.3 本地 jar 的四个常见坑

  1. dev 环境 mixin 崩(非常常见): 形如 Mixin apply failed xxx.mixins.json:SomeMixin ... @Shadow method m_6702_ was not located —— 作者把 SRG 混淆名写进了 mixin 源码、refmap 又不全,生产正常、dev 必崩。 解法:① 换 dev 修补版 jar;② 改用源码 composite build(2.3);③ 把 mixin 列表清空做 dev 专用 jar;④ 给上游提 issue。
  2. 没有传递依赖:本地 jar 不会自动带上它依赖的东西,得手动补齐(LDlib2 就常要配 modernui / kotlinforforge)。
  3. 重复模组:同一个 modId 出现两份(比如 libs/ 一份 + maven 一份)→ 启动报 Found duplicate mods / 版本冲突。启动日志搜索 Found 2 mods for first modid 就能确认。
  4. 版本/变体搞混-all(含依赖的 fat jar)、-sources(源码)、-dev/-api(开发用变体)不能互相替代;拿不准就看一眼 jar 里的元数据:
unzip -p libs/xxx.jar META-INF/mods.toml | head -30     # modId / version / 依赖范围
unzip -l libs/xxx.jar | grep -E "mixins.json|refmap|mods.toml"
unzip -p libs/xxx.jar xxx.mixins.json                   # 看 mixin 配置

3.4 jarJar:把依赖"内嵌"进你的 jar

发布时想省得玩家自己装前置,可以把依赖塞进自己的 jar(NeoForge/ModDevGradle 支持):

dependencies {
    implementation(jarJar("io.github.llamalad7:mixinextras-forge:0.5.3"))
}

注意:内嵌会增大 jar、可能和玩家自装版本冲突,只包你必须的小库,别包大型玩法模组。


4. 自建方式

4.1 本机 maven(mavenLocal,自用最快)

发布方(你的库工程):

plugins { id 'maven-publish' }

publishing {
    publications {
        register('mavenJava', MavenPublication) {
            from components.java
            groupId = 'com.example'
            artifactId = 'mylib'
            version = '1.0.0'
        }
    }
}
// 一条命令发布到 ~/.m2/repository
// gradlew publishToMavenLocal

使用方:

repositories { mavenLocal() }
dependencies { implementation "com.example:mylib:1.0.0" }

优点:零运维。缺点:只在这台机器有效、会污染本机缓存、团队协作等于没有。适合临时联调。

4.2 自建 Maven 服务(团队/服务器共享)

方案 A:轻量 Reposilite(Java,单文件,最省事)

# 下载 reposilite.jar 后
java -jar reposilite.jar --port 8080 --working-directory ./data
# 默认会创建 releases / snapshots / private 三个仓库;管理面板 /#/

方案 B:Nexus / Artifactory / Archiva — 功能全、偏重,适合大团队。

方案 C:nginx 静态目录模拟 Maven(零依赖,够用)

Maven 布局其实很简单,静态目录就能当仓库:

/maven/
  com/yourname/yourlib/
    1.0.0/
      yourlib-1.0.0.jar
      yourlib-1.0.0.pom
      yourlib-1.0.0.module        ← Gradle 元数据(可选但强烈建议)
    maven-metadata.xml            ← 列出所有版本(可选,帮 IDE 提示)
<!-- maven-metadata.xml 最小示例 -->
<?xml version="1.0" encoding="UTF-8"?>
<metadata>
  <groupId>com.yourname</groupId>
  <artifactId>yourlib</artifactId>
  <versioning>
    <latest>1.0.0</latest>
    <release>1.0.0</release>
    <versions><version>1.0.0</version></versions>
  </versioning>
</metadata>
location /maven/ {
    alias /www/wwwroot/yourhost/maven/;
    autoindex on;                 # 方便浏览器直接看
}
repositories { maven { url = "https://yourhost/maven/" } }

权限

  • 匿名读:按上面的静态目录,谁都拉得到(公开制品);
  • 要认证:nginx 加 auth_basic/token,或 Reposilite/Nexus 的 token 机制;
  • 只读仓库可以放心开匿名,写入一定要凭据

发布脚本(Gradle → 自建仓库)

publishing {
    publications { register('mavenJava', MavenPublication) { from components.java } }
    repositories {
        maven {
            name = "MyRepo"
            url = uri("https://yourhost/maven/")
            credentials {
                username = project.findProperty("repoUser")    // 放 gradle.properties(别提交!)
                password = project.findProperty("repoPass")
            }
        }
    }
}
// gradlew publish

⚠️ 账号密码写进 ~/.gradle/gradle.properties 或环境变量;永远不要提交到仓库

4.3 自建"依赖"的另一种形态:composite build

把自己的另一个工程当依赖(不发布制品也能用):

// settings.gradle
includeBuild('../mylib') {
    dependencySubstitution { substitute module('com.example:mylib') using project(':') }
}

适合"两个仓库一起开发、改完立刻生效"的场景(FPSMatch 联动模组就是这么干的)。

4.4 离线/内网团队的做法(推荐给服务器团队)

  1. 把需要的 jar 统一放一个目录,按 <modid>-<版本>.jar 命名;
  2. 打包成一个 zip,通过自己的网站/网盘分发(下载链接固定);
  3. 工程里 libs/ + files() 引用,写清每个 jar 的用途和来源;
  4. 需要多项目复用时,再升级成 4.2 的静态 Maven。

好处:完全离线可用、版本可控、不受 GitHub/平台抽风影响;代价:更新要手动同步(可写个脚本按 2.1 的直链自动拉)。


5. 语法速查

repositories {
    mavenCentral()                                          // 通用 Java 库
    maven { url = "https://maven.minecraftforge.net/" }     // Forge / MC 本体
    maven { url = "https://api.modrinth.com/maven"; content { includeGroup "maven.modrinth" } }
    maven { url = "https://www.cursemaven.com";     content { includeGroup "curse.maven" } }
    maven { url = "https://jitpack.io" }
    maven { url = "https://maven.izzel.io/releases/" }      // ModernUI 作者仓库
    maven { url = "https://maven.ptcrys.net/releases" }     // 作者自建示例
    flatDir { dirs "libs" }
    mavenLocal()
}

dependencies {
    implementation "g:a:1.0"                              // 普通依赖
    compileOnly    "g:a:1.0"                              // 只编译期
    runtimeOnly    "g:a:1.0"                              // 只运行期
    annotationProcessor "g:a:1.0"                         // 注解处理器
    modImplementation "maven.modrinth:x:1.0"              // ModDevGradle / Loom
    modCompileOnly    "maven.modrinth:x:1.0"
    modRuntimeOnly    "maven.modrinth:x:1.0"
    implementation fg.deobf("curse.maven:x-123:456")       // ForgeGradle
    implementation files("libs/x.jar")                    // 本地 jar
    implementation fileTree(dir: "libs", include: ["*.jar"])
    implementation(jarJar("g:a:1.0"))                     // 内嵌

    // 版本范围 / 强制版本 / 排除
    implementation "g:a:[1.0,2.0)"                        // 范围
    implementation("g:a:1.0") { transitive = false }      // 不带传递依赖
    implementation("g:a:1.0") { exclude group: "g2", module: "a2" }
    constraints { implementation("g:a:2.0") }             // 约束版本
}

常用命令

./gradlew dependencies --configuration runtimeClasspath   # 看依赖树(查冲突神器)
./gradlew dependencies --configuration compileClasspath
./gradlew --refresh-dependencies                          # 强制重新解析(改了仓库/版本没生效时用)
./gradlew build --scan                                    # 构建报告
./gradlew :SubProject:dependencies                        # 看子工程(composite build 的对方)依赖

6. 排错手册

现象 原因 解法
Could not find method modImplementation() ForgeGradle 工程里用了 Fabric/NeoForge 的配置 换成 implementation fg.deobf("..."),或改用 ModDevGradle
Could not resolve com.xxx:yyy:1.0 仓库没加 / 坐标写错 / 该版本不存在 / 仓库要认证 逐个仓库加 content { includeGroup } 排查;浏览器直接访问制品 URL 验证是否 200
Could not find method fg.deobf() 用了 ModDevGradle(没有 fg 扩展) 直接 modImplementation files(...),去掉 fg.deobf
Missing or unsupported mandatory dependencies: 'xxx' 前置模组没进运行期 modRuntimeOnly / implementation(composite build 不传递依赖!)
Mixin apply failed ... @Shadow method m_xxxxx_ was not located 发布 jar 的 mixin 写死 SRG 名、refmap 不全(dev 环境特有) dev 修补 jar / 改用源码 composite build / 见 3.3 第 1 条
Found duplicate mods / Found 2 mods for first modid 同一个 modId 被引入两次(版本不同) ./gradlew dependencies 找到来源,删掉多余的一份
下载极慢/超时 境外仓库网络问题 镜像加速、改用本地 jar、自建仓库(4.4)
改了版本号却没生效 Gradle 缓存 ./gradlew --refresh-dependencies,必要时清 ~/.gradle/caches/modules-2
运行期 NoClassDefFoundError 依赖只在编译期(compileOnly 视需要改成 implementation / modRuntimeOnly
IDE 里红色报错但能构建 IDE 没同步 IDEA → Gradle 面板 → Reload;或 ./gradlew idea

7. 实战:FPSMatch 1.3.0 的依赖拉取现状(2026-09 实测)

依赖 能用的来源 推荐拉法
FPSMatch 1.3.0 GitHub Release 快照(Modrinth/CurseForge 还停在 1.2.5) 源码 composite build(2.3)最稳;或下 jar 放 libs/
LDLib2 2.2.27+1.20.1 作者 Maven https://maven.ptcrys.net/releases匿名可读 implementation("com.lowdragmc:ldlib2-forge:2.2.27+1.20.1")
ModernUI 3.12.0 作者 Maven https://maven.izzel.io/releases/ modImplementation "icyllis.modernui:ModernUI-Forge:1.20.1-3.12.0.1" + Core/Markflow
KotlinForForge 4.11.0 Modrinth Maven modRuntimeOnly "maven.modrinth:kotlin-for-forge:4.11.0"
TaCZ / LR Tactical / CounterStrikeGrenade 等 Modrinth Maven modImplementation "maven.modrinth:<slug>:<版本>"

典型组合(联动模组 build.gradle 骨架)

repositories {
    mavenCentral()
    maven { name = 'PTCrysReleases'; url = uri('https://maven.ptcrys.net/releases') }
    maven { name = 'IzzelAliz';      url = 'https://maven.izzel.io/releases/' }
    maven { name = 'Modrinth';       url = 'https://api.modrinth.com/maven'; content { includeGroup 'maven.modrinth' } }
    maven { name = 'LatvianDev';     url = 'https://maven.latvian.dev/releases'; content { includeGroup 'dev.latvian.mods' } }
}

dependencies {
    // ① FPSMatch(composite build 源码联调)
    modImplementation('net.ptcrys:FPSMatch') { transitive = false }

    // ② FPSMatch 的运行时前置(composite build 不会自动传递,必须自己声明)
    implementation('com.lowdragmc:ldlib2-forge:2.2.27+1.20.1')
    modRuntimeOnly "maven.modrinth:kotlin-for-forge:4.11.0"
    modImplementation "icyllis.modernui:ModernUI-Forge:1.20.1-3.12.0.1"
    // ModernUI-Core / Markflow 走 additionalRuntimeClasspath,见模板 build.gradle

    // ③ 可选玩法/兼容模组
    modCompileOnly "maven.modrinth:tacz-tweaks:2.11.2"
}

📌 更完整的 FPSMatch 1.3.0 引入方式与踩坑,见 16. 联动 FPSMatch


8. 一页纸总结

  1. 优先级:官方/平台发布版 → GitHub 快照 → 源码 composite build → 本地 jar → 自建 Maven。
  2. 坐标 + 仓库是两件事,缺一不可;给仓库加 content { includeGroup } 能显著加速解析。
  3. dev 环境的 mixin/refmap 问题优先用"源码 composite build"绕开,别硬改发布 jar(除不出手时再修补)。
  4. 本地 jar 记得补传递依赖,发布 jar 记得 fg.deobf(ForgeGradle)或 modImplementation(ModDevGradle)。
  5. 自建 Maven 不一定上 Nexus:nginx 静态目录 + 正确布局就能用;内网/服务器团队推荐"统一 libs 目录 + 打包分发"。
  6. 出问题先跑 ./gradlew dependencies --configuration runtimeClasspath,九成能定位。