Skip to content

安装 Forge 与 NeoForge ​

Forge 和 NeoForge 我们不自己解析版本 JSON,而是跑官方的安装器。安装器负责算出正确的 classpath、处理器(processors)和补丁后的客户端 jar——这套逻辑太复杂,重写没有意义。

代价是必须把环境准备到官方安装器能接受的样子,这一页讲的就是这些前置条件。

构建列表从哪来 ​

Forge 的构建列表由两个来源合成。

一是全量构建,来自 maven 元数据:

https://maven.minecraftforge.net/net/minecraftforge/forge/maven-metadata.xml

这是标准 Maven 元数据,<version> 标签里是构建号,形如 1.20.1-47.4.28。我们按 <mcVersion>- 前缀过滤,再排序。

二是推荐/最新标记,来自:

https://files.minecraftforge.net/net/minecraftforge/forge/promotions_slim.json

它是一个扁平的 promos 字典:

json
{
  "promos": {
    "1.20.1-recommended": "47.4.28",
    "1.20.1-latest": "47.4.28",
    "1.8-recommended": "11.14.4.1563",
    "1.8-latest": "11.14.4.1577"
  }
}

注意 recommended 和 latest 经常不是同一个。1.8 就是:最新是 11.14.4.1577,推荐是 11.14.4.1563。推荐的含义是「Forge 团队认为稳的那个」,最新可能刚推出来。所以界面上我们把它们标成两个不同的记号,推荐卡优先取 推荐,没有才退回 最新:

csharp
private static LoaderBuild? RecommendedOf(LoaderKind kind, List<LoaderBuild> builds) => kind switch
{
    LoaderKind.Forge => builds.FirstOrDefault(b => b.Note == "推荐")
                     ?? builds.FirstOrDefault(b => b.Note == "最新"),
    _ => builds.FirstOrDefault(),
};

构建号必须按点号判定

早先我们用 int.TryParse 判断构建号是否合法,结果现代构建号 47.4.5 解析失败,整个列表恒为空。

正确的判法是「首位是数字,其余字符只能是数字/字母/点/连字符」:

csharp
private static bool IsForgeBuildSuffix(string s)
{
    if (s.Length == 0 || !char.IsAsciiDigit(s[0])) return false;
    foreach (var c in s)
        if (!char.IsAsciiDigit(c) && !char.IsAsciiLetter(c) && c != '.' && c != '-') return false;
    return true;
}

这样现代的 47.4.5 和旧的 10.13.4.1614-1.7.10(带 -1.7.10 后缀)都能通过。

排序也不能用字符串比较。CompareForgeBuilds 忽略 - 之后的后缀,逐段按数字比,段数多的更新:

csharp
static int[] Key(string v) =>
    v.Split('-')[0].Split('.').Select(p => int.TryParse(p, out var n) ? n : 0).ToArray();

注意 maven-metadata.xml 里的顺序不保证按时间,必须自己排。

NeoForge 的构件位置 ​

NeoForge 的构建列表走另一套 API:

https://maven.neoforged.net/api/maven/versions/releases/net/neoforged/{artifact}

artifact 是 forge 还是 neoforge,取决于 MC 版本:

  • 1.20.1 的产物发布在 legacy 的 forge 构件下,版本号形如 1.20.1-47.1.106
  • 其余版本在 neoforge 构件下,版本号形如 21.1.77(1.x 时代)或 26.2.0.57(新命名)

前缀规则:

csharp
private static string? NeoForgePrefix(string mcVersion)
{
    if (mcVersion == "1.20.1") return "1.20.1-47.1.";
    var seg = mcVersion.StartsWith("1.", StringComparison.Ordinal) ? mcVersion[2..] : mcVersion;
    ...
    return $"{seg}.";
}

1.x 时代构件版本号以 MC 版本号开头(1.21 → 21.0.、1.20.4 → 20.4.),要剥掉前缀;2026 年起 NeoForge 直接以 MC 版本作为构件版本前缀(26.2 → 26.2.0.57),不用剥。剥的长度在 ListNeoForgeBuildsAsync 里算:

csharp
var skip = legacy ? mcVersion.Length + 1 : oldNaming ? prefix!.Length : 0;

安装器直链同样分两种:

csharp
return mcVersion == "1.20.1"
    ? $"https://maven.neoforged.net/releases/net/neoforged/forge/{mcVersion}-{ver}/forge-{mcVersion}-{ver}-installer.jar"
    : $"https://maven.neoforged.net/releases/net/neoforged/neoforge/{ver}/neoforge-{ver}-installer.jar";

build 参数如果带了 MC 前缀会被自动剥离,所以界面上传什么都能拼对。beta 版标成 Beta,第一个非 beta 标成 最新稳定版。

跑安装器 ​

下载到临时目录后执行:

csharp
psi.ArgumentList.Add("-jar");
psi.ArgumentList.Add(installerPath);
psi.ArgumentList.Add("--installClient");
psi.ArgumentList.Add(settings.DotMinecraft);

--installClient 会把游戏目录作为参数传进去,安装器在里面写入 versions/<id>-forge-<build>/(含版本 JSON、处理器产物、库文件)和 libraries/ 下的依赖。

Java 大版本从原版版本 JSON 的 javaVersion.majorVersion 取(1.20.1 是 17,1.16.5 是 8)。本地没有满足的运行时时,EnsureJavaAsync 会自动从 Adoptium 下一个。

安装器的 stdout 逐行读出来当进度显示,stderr 留着在失败时拼进异常信息:

csharp
if (p.ExitCode != 0)
{
    var err = await stdErrTask;
    throw new InvalidOperationException(
        $"安装器执行失败(exit {p.ExitCode}):{(err.Length > 0 ? err : lastLine)}");
}

必须先预置 launcher_profiles.json

Forge 官方安装器启动后第一件事是读游戏目录里的 launcher_profiles.json。文件不存在就直接报

There is no minecraft launcher profile...

然后退出码 1 退出。

PCL 和 HMCL 都在跑安装器前自动补这个文件,我们也这么做:

csharp
public static bool EnsureLauncherProfiles(string dotMinecraft)
{
    var path = Path.Combine(dotMinecraft, "launcher_profiles.json");
    if (File.Exists(path)) return false;   // 只缺时才建,不覆盖用户已有的配置
    ...
    File.WriteAllText(path, json, new System.Text.UTF8Encoding(false));

必须是 UTF-8 无 BOM。Forge 用 Gson 解析,带 BOM 会直接解析失败——这是另一个更隐蔽的失败模式:不报「没有 profile」,而是抛一个 JSON 解析异常。

内容用最小合法结构即可,官方启动器读得懂,空 profiles 不影响启动:

json
{
  "profiles": {},
  "settings": {
    "enableSnapshots": false,
    "keepLauncherOpen": false,
    "showGameLog": false,
    "soundOn": false
  },
  "version": 3
}

别忘了原版必须先装

Forge 安装器要读原版的版本 JSON 才能算出完整的 classpath。所以流程永远是「先装原版,再装 Forge」。我们把这条约束写进了 UI 的安装流程:原版装完才走加载器分支。

Forge 装出来的实例目录名是 <mc>-forge-<build>(NeoForge 是 <mc>-neoforge-<build>),和原版目录并列,不会覆盖原版。

相关链接:加载器版本怎么选、安装原版、安装 Fabric 与 Quilt

基于 MIT 许可证(有修改)开源