minecraft-multiloader

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Minecraft Multiloader Skill (Architectury)

Minecraft 多加载器 Skill(Architectury)

What Is Architectury?

什么是 Architectury?

Architectury is a framework that lets you write one mod codebase that compiles to both NeoForge and Fabric JARs. The common subproject has a shared API; platform subprojects implement platform-specific behavior behind the
@ExpectPlatform
abstraction.
Architectury 是一个框架,可让您只编写一套模组代码库,即可编译为 NeoForge 和 Fabric JAR。common 子项目提供共享 API;平台子项目通过
@ExpectPlatform
抽象实现平台特定行为。

Routing Boundaries

适用边界

  • Use when
    : one shared codebase must build and ship both NeoForge and Fabric artifacts.
  • Do not use when
    : the project is single-loader only (
    minecraft-modding
    for NeoForge/Fabric, not both).
  • Do not use when
    : the task is Paper/Bukkit plugin development (
    minecraft-plugin-dev
    ).
ComponentPurpose
architectury-loom
Gradle plugin — extends Fabric Loom for multiloader support
architectury-api
Runtime library — abstractions over both platforms
@ExpectPlatform
Annotation marking methods with platform-specific implementations
common/
Shared code (no loader-specific APIs)
fabric/
Fabric-specific code + entrypoint
neoforge/
NeoForge-specific code + entrypoint

  • Use when
    :必须使用一套共享代码库来构建并发布 NeoForge 和 Fabric 构件时。
  • Do not use when
    :项目仅支持单一加载器时(对于 NeoForge/Fabric,使用
    minecraft-modding
    ,而不是本 Skill)。
  • Do not use when
    :任务是 Paper/Bukkit 插件开发时(
    minecraft-plugin-dev
    )。
组件用途
architectury-loom
Gradle 插件——扩展 Fabric Loom 以支持多加载器
architectury-api
运行时库——为两个平台提供抽象
@ExpectPlatform
标注具有平台特定实现的方法的注解
common/
共享代码(不包含加载器特定 API)
fabric/
Fabric 特定代码和入口点
neoforge/
NeoForge 特定代码和入口点

Versions (Minecraft 1.21.11)

版本(Minecraft 1.21.11)

properties
undefined
properties
undefined

gradle.properties property names used by this skill's static helper.

gradle.properties property names used by this skill's static helper.

Get every tool version from the exact generated or known-working project.

Get every tool version from the exact generated or known-working project.

mod_version=1.0.0 minecraft_version=1.21.11 enabled_platforms=fabric,neoforge
architectury_version=<project pin> fabric_loader_version=<project pin> fabric_api_version=<project pin ending in +1.21.11> neoforge_version=<project pin in the 21.11.x family> loom_version=<project pin>

Pin `architectury_version`, the Architectury plugin version, and `loom_version`
from the same generated or known-working project line. This skill deliberately
does not publish a copyable dependency matrix: its static helper cannot resolve
whether a particular set of versions is compatible.

For Minecraft 26.x / Java 25, use the official
[Architectury Template Generator](https://generate.architectury.dev/) only when
its version selector offers the exact target. Generate a **Multiplatform**
project with Fabric and NeoForge, then preserve the generated Gradle layout and
pins as one set. If the generator does not offer the target, begin with an
already working project on that exact line and inspect its resolved build; do
not relabel a 1.21.11 template as 26.x. The published template downloads are
not a substitute for an exact-current scaffold.

Do not mechanically change only `minecraft_version` in the retained example:
26.x is unobfuscated and its Loom/remapping setup differs from 1.21.11.
mod_version=1.0.0 minecraft_version=1.21.11 enabled_platforms=fabric,neoforge
architectury_version=<project pin> fabric_loader_version=<project pin> fabric_api_version=<project pin ending in +1.21.11> neoforge_version=<project pin in the 21.11.x family> loom_version=<project pin>

将 `architectury_version`、Architectury 插件版本和 `loom_version` 固定到同一个生成项目或已知可用项目系列中的版本。本 Skill 有意不提供可直接复制的依赖版本矩阵,因为其静态辅助程序无法解析特定版本组合是否兼容。

对于 Minecraft 26.x / Java 25,仅当官方 [Architectury Template Generator](https://generate.architectury.dev/) 的版本选择器提供精确的目标版本时才使用它。生成一个同时包含 Fabric 和 NeoForge 的 **Multiplatform** 项目,然后将生成的 Gradle 布局及版本固定项作为一套整体保留。如果生成器不提供目标版本,请从该精确版本系列中一个已经可用的项目开始,并检查其解析后的构建配置;不要将 1.21.11 模板重新标记为 26.x。已发布的模板下载不能替代与当前精确版本匹配的脚手架。

不要在保留的示例中只机械地修改 `minecraft_version`:26.x 未混淆,且其 Loom/remapping 设置与 1.21.11 不同。

Bundled References And Helpers

内置参考资料和辅助工具

  • Version alignment reference:
    references/architectury-reference.md
  • Sanity checker:
    ./scripts/check-version-sanity.sh --root <project>
Run the sanity checker after editing
gradle.properties
. It is a static syntax-and-version-family preflight: it catches missing keys, snapshot pins, missing
fabric
/
neoforge
platforms, and obvious version-family drift. It does not resolve dependencies, prove loader compatibility, or replace the project's Fabric and NeoForge build and smoke tests.

  • 版本对齐参考:
    references/architectury-reference.md
  • 健全性检查器:
    ./scripts/check-version-sanity.sh --root <project>
编辑
gradle.properties
后运行健全性检查器。它是一项静态语法和版本系列预检:能够发现缺失的键、快照版本固定、缺失的
fabric
/
neoforge
平台,以及明显的版本系列偏移。它不能解析依赖关系、证明加载器兼容性,也不能替代项目的 Fabric 和 NeoForge 构建及冒烟测试。

Root Project Layout

根项目布局

my-mod/
├── build.gradle           ← root build (shared config)
├── settings.gradle
├── gradle.properties
├── common/
│   ├── build.gradle
│   └── src/main/java/com/example/mymod/
│       ├── MyMod.java               ← shared init
│       ├── registry/
│       │   └── ModItems.java        ← shared registry declarations
│       └── platform/
│           └── PlatformHelper.java  ← @ExpectPlatform methods
├── fabric/
│   ├── build.gradle
│   └── src/main/
│       ├── java/com/example/mymod/fabric/
│       │   ├── MyModFabric.java          ← Fabric entrypoint
│       ├── java/com/example/mymod/platform/
│       │   └── PlatformHelperImpl.java   ← Fabric @ExpectPlatform implementation
│       └── resources/
│           ├── fabric.mod.json
│           └── assets/...
└── neoforge/
    ├── build.gradle
    └── src/main/
        ├── java/com/example/mymod/neoforge/
        │   ├── MyModNeoForge.java        ← NeoForge @Mod entry
        ├── java/com/example/mymod/platform/
        │   └── PlatformHelperImpl.java   ← NeoForge @ExpectPlatform implementation
        └── resources/
            ├── META-INF/neoforge.mods.toml
            └── assets/...

my-mod/
├── build.gradle           ← root build (shared config)
├── settings.gradle
├── gradle.properties
├── common/
│   ├── build.gradle
│   └── src/main/java/com/example/mymod/
│       ├── MyMod.java               ← shared init
│       ├── registry/
│       │   └── ModItems.java        ← shared registry declarations
│       └── platform/
│           └── PlatformHelper.java  ← @ExpectPlatform methods
├── fabric/
│   ├── build.gradle
│   └── src/main/
│       ├── java/com/example/mymod/fabric/
│       │   ├── MyModFabric.java          ← Fabric entrypoint
│       ├── java/com/example/mymod/platform/
│       │   └── PlatformHelperImpl.java   ← Fabric @ExpectPlatform implementation
│       └── resources/
│           ├── fabric.mod.json
│           └── assets/...
└── neoforge/
    ├── build.gradle
    └── src/main/
        ├── java/com/example/mymod/neoforge/
        │   ├── MyModNeoForge.java        ← NeoForge @Mod entry
        ├── java/com/example/mymod/platform/
        │   └── PlatformHelperImpl.java   ← NeoForge @ExpectPlatform implementation
        └── resources/
            ├── META-INF/neoforge.mods.toml
            └── assets/...

Legacy Build Template

旧版构建模板

The old fixed Gradle scripts were a 1.21.11 snapshot and are intentionally not presented as a current scaffold. For either supported version, read
references/legacy-1.21.11-template.md
or
references/architectury-reference.md
before changing generated build files. They preserve version anchors and source-set boundaries without encouraging a partial build script to be copied into a different Minecraft line.

旧的固定 Gradle 脚本基于 1.21.11 快照,因此有意不将其作为当前脚手架展示。对于任一受支持版本,在修改生成的构建文件之前,请阅读
references/legacy-1.21.11-template.md
或
references/architectury-reference.md
。这些参考会保留版本锚点和 source set 边界,同时避免将部分构建脚本复制到其他 Minecraft 版本系列。

Shared Common Code

共享通用代码

common/.../MyMod.java

common/.../MyMod.java

java
package com.example.mymod;

import dev.architectury.registry.registries.DeferredRegister;
import dev.architectury.registry.registries.RegistrySupplier;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.item.Item;

public class MyMod {
    public static final String MOD_ID = "mymod";

    // Architectury's DeferredRegister — works on both platforms
    public static final DeferredRegister<Item> ITEMS =
        DeferredRegister.create(MOD_ID, Registries.ITEM);

    public static final RegistrySupplier<Item> MY_ITEM =
        ITEMS.register("my_item", () -> new Item(new Item.Properties().setId(
            ResourceKey.create(Registries.ITEM,
                Identifier.fromNamespaceAndPath(MOD_ID, "my_item"))
        )));

    public static void init() {
        ITEMS.register(); // registers with both platforms
    }
}
java
package com.example.mymod;

import dev.architectury.registry.registries.DeferredRegister;
import dev.architectury.registry.registries.RegistrySupplier;
import net.minecraft.core.registries.Registries;
import net.minecraft.resources.Identifier;
import net.minecraft.resources.ResourceKey;
import net.minecraft.world.item.Item;

public class MyMod {
    public static final String MOD_ID = "mymod";

    // Architectury's DeferredRegister — works on both platforms
    public static final DeferredRegister<Item> ITEMS =
        DeferredRegister.create(MOD_ID, Registries.ITEM);

    public static final RegistrySupplier<Item> MY_ITEM =
        ITEMS.register("my_item", () -> new Item(new Item.Properties().setId(
            ResourceKey.create(Registries.ITEM,
                Identifier.fromNamespaceAndPath(MOD_ID, "my_item"))
        )));

    public static void init() {
        ITEMS.register(); // registers with both platforms
    }
}

@ExpectPlatform
— platform-specific methods

@ExpectPlatform
——平台特定方法

Define the contract in
common/
:
java
package com.example.mymod.platform;

import dev.architectury.injectables.annotations.ExpectPlatform;
import net.minecraft.world.level.material.Fluid;

public class PlatformHelper {

    @ExpectPlatform
    public static boolean isModLoaded(String modId) {
        // This body is replaced at compile time by the platform implementation
        throw new AssertionError("ExpectPlatform implementation not found");
    }

    @ExpectPlatform
    public static boolean isClient() {
        throw new AssertionError();
    }
}
Keep each platform implementation in the same Java package as the common
@ExpectPlatform
class. Only the source set changes between
common/
,
fabric/
, and
neoforge/
.
Implement in
fabric/.../platform/PlatformHelperImpl.java
:
java
package com.example.mymod.platform;

import net.fabricmc.loader.api.FabricLoader;

// Class name must match: <common class name>Impl
public class PlatformHelperImpl {

    public static boolean isModLoaded(String modId) {
        return FabricLoader.getInstance().isModLoaded(modId);
    }

    public static boolean isClient() {
        return FabricLoader.getInstance().getEnvironmentType() ==
            net.fabricmc.api.EnvType.CLIENT;
    }
}
Implement in
neoforge/.../platform/PlatformHelperImpl.java
:
java
package com.example.mymod.platform;

import net.neoforged.fml.ModList;
import net.neoforged.fml.loading.FMLEnvironment;

public class PlatformHelperImpl {

    public static boolean isModLoaded(String modId) {
        return ModList.get().isLoaded(modId);
    }

    public static boolean isClient() {
        return FMLEnvironment.dist.isClient();
    }
}

在
common/
中定义契约:
java
package com.example.mymod.platform;

import dev.architectury.injectables.annotations.ExpectPlatform;
import net.minecraft.world.level.material.Fluid;

public class PlatformHelper {

    @ExpectPlatform
    public static boolean isModLoaded(String modId) {
        // This body is replaced at compile time by the platform implementation
        throw new AssertionError("ExpectPlatform implementation not found");
    }

    @ExpectPlatform
    public static boolean isClient() {
        throw new AssertionError();
    }
}
确保每个平台的实现与 common 中
@ExpectPlatform
类位于同一个 Java 包中。只有 source set 会因
common/
、
fabric/
和
neoforge/
而异。
在
fabric/.../platform/PlatformHelperImpl.java
中实现:
java
package com.example.mymod.platform;

import net.fabricmc.loader.api.FabricLoader;

// Class name must match: <common class name>Impl
public class PlatformHelperImpl {

    public static boolean isModLoaded(String modId) {
        return FabricLoader.getInstance().isModLoaded(modId);
    }

    public static boolean isClient() {
        return FabricLoader.getInstance().getEnvironmentType() ==
            net.fabricmc.api.EnvType.CLIENT;
    }
}
在
neoforge/.../platform/PlatformHelperImpl.java
中实现:
java
package com.example.mymod.platform;

import net.neoforged.fml.ModList;
import net.neoforged.fml.loading.FMLEnvironment;

public class PlatformHelperImpl {

    public static boolean isModLoaded(String modId) {
        return ModList.get().isLoaded(modId);
    }

    public static boolean isClient() {
        return FMLEnvironment.dist.isClient();
    }
}

Fabric Entrypoint

Fabric 入口点

fabric/.../MyModFabric.java

fabric/.../MyModFabric.java

java
package com.example.mymod.fabric;

import com.example.mymod.MyMod;
import net.fabricmc.api.ModInitializer;

public class MyModFabric implements ModInitializer {
    @Override
    public void onInitialize() {
        MyMod.init();
    }
}
java
package com.example.mymod.fabric;

import com.example.mymod.MyMod;
import net.fabricmc.api.ModInitializer;

public class MyModFabric implements ModInitializer {
    @Override
    public void onInitialize() {
        MyMod.init();
    }
}

fabric/.../resources/fabric.mod.json

fabric/.../resources/fabric.mod.json

This retained-1.21.11 metadata example follows the minimum dependencies in the upstream Architectury 1.21.11 branch. Keep the generated project's exact ranges when they are stricter.
json
{
  "schemaVersion": 1,
  "id": "mymod",
  "version": "${version}",
  "name": "My Mod",
  "description": "A multiloader example mod",
  "license": "MIT",
  "environment": "*",
  "entrypoints": {
    "main": ["com.example.mymod.fabric.MyModFabric"]
  },
  "depends": {
    "fabricloader": ">=0.18.2",
    "fabric-api": ">=0.139.4+1.21.11",
    "architectury": ">=19.0",
    "minecraft": "~1.21.11"
  }
}

此保留的 1.21.11 元数据示例遵循 上游 Architectury 1.21.11 分支 中的最低依赖项。如果生成项目中的版本范围更严格,请保留其精确范围。
json
{
  "schemaVersion": 1,
  "id": "mymod",
  "version": "${version}",
  "name": "My Mod",
  "description": "A multiloader example mod",
  "license": "MIT",
  "environment": "*",
  "entrypoints": {
    "main": ["com.example.mymod.fabric.MyModFabric"]
  },
  "depends": {
    "fabricloader": ">=0.18.2",
    "fabric-api": ">=0.139.4+1.21.11",
    "architectury": ">=19.0",
    "minecraft": "~1.21.11"
  }
}

NeoForge Entrypoint

NeoForge 入口点

neoforge/.../MyModNeoForge.java

neoforge/.../MyModNeoForge.java

java
package com.example.mymod.neoforge;

import com.example.mymod.MyMod;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.common.Mod;

@Mod(MyMod.MOD_ID)
public class MyModNeoForge {
    public MyModNeoForge(IEventBus modEventBus) {
        MyMod.init();
    }
}
java
package com.example.mymod.neoforge;

import com.example.mymod.MyMod;
import net.neoforged.bus.api.IEventBus;
import net.neoforged.fml.common.Mod;

@Mod(MyMod.MOD_ID)
public class MyModNeoForge {
    public MyModNeoForge(IEventBus modEventBus) {
        MyMod.init();
    }
}

neoforge/.../resources/META-INF/neoforge.mods.toml

neoforge/.../resources/META-INF/neoforge.mods.toml

toml
modLoader = "javafml"
loaderVersion = "[1,)"
license = "MIT"

[[mods]]
modId = "mymod"
version = "${file.jarVersion}"
displayName = "My Mod"
description = "A multiloader example mod"

[[dependencies.mymod]]
modId = "neoforge"
type = "required"
versionRange = "[21.11,)"
ordering = "NONE"
side = "BOTH"

[[dependencies.mymod]]
modId = "minecraft"
type = "required"
versionRange = "[1.21.11,1.22)"
ordering = "NONE"
side = "BOTH"

toml
modLoader = "javafml"
loaderVersion = "[1,)"
license = "MIT"

[[mods]]
modId = "mymod"
version = "${file.jarVersion}"
displayName = "My Mod"
description = "A multiloader example mod"

[[dependencies.mymod]]
modId = "neoforge"
type = "required"
versionRange = "[21.11,)"
ordering = "NONE"
side = "BOTH"

[[dependencies.mymod]]
modId = "minecraft"
type = "required"
versionRange = "[1.21.11,1.22)"
ordering = "NONE"
side = "BOTH"

Build Commands

构建命令

bash
undefined
bash
undefined

Build both JARs simultaneously

Build both JARs simultaneously

./gradlew build
./gradlew build

Inspect the project's actual outputs. Generated templates choose their own

Inspect the project's actual outputs. Generated templates choose their own

archive base name and version convention:

archive base name and version convention:

find fabric/build/libs neoforge/build/libs -maxdepth 1 -type f -name '.jar'
! -name '
-sources.jar' ! -name '-dev.jar' ! -name '-javadoc.jar'
find fabric/build/libs neoforge/build/libs -maxdepth 1 -type f -name '.jar'
! -name '
-sources.jar' ! -name '-dev.jar' ! -name '-javadoc.jar'

Run in dev environment

Run in dev environment

./gradlew :fabric:runClient ./gradlew :neoforge:runClient ./gradlew :neoforge:runServer
./gradlew :fabric:runClient ./gradlew :neoforge:runClient ./gradlew :neoforge:runServer

Datagen (if applicable)

Datagen (if applicable)

./gradlew :neoforge:runData

---
./gradlew :neoforge:runData

---

Common Pitfalls

常见问题

PitfallSolution
Using
net.neoforged.*
/
net.fabricmc.*
in
common/
Only use vanilla MC and Architectury APIs in common
Direct field access on
DeferredRegister
(NeoForge style) in common
Use Architectury's
DeferredRegister
Constructing a 26.x item without a registry keyCreate its
ResourceKey<Item>
and call
Item.Properties#setId
before
new Item
Forgetting
@ExpectPlatform
throws
AssertionError
at runtime
Both
fabric/
and
neoforge/
must have matching same-package
*Impl
classes
Assets duplicated in fabric/ and neoforge/Keep assets in
common/src/main/resources/assets/
A common Mixin imports a loader API or targets one loader's sidePut it in that platform subproject; loader-neutral Mixins may be common when both generated platform configurations include them
Accessing world/registry on mod init threadUse
mod bus
events for setup; never access world on init

常见问题解决方案
在
common/
中使用
net.neoforged.*
/
net.fabricmc.*
在 common 中仅使用 Minecraft 原版 API 和 Architectury API
在 common 中以 NeoForge 风格直接访问
DeferredRegister
字段
使用 Architectury 的
DeferredRegister
创建 26.x 物品时未提供 registry key创建其
ResourceKey<Item>
,并在
new Item
前调用
Item.Properties#setId
忘记
@ExpectPlatform
导致运行时抛出
AssertionError
fabric/
和
neoforge/
都必须包含位于同一包中的匹配
*Impl
类
在
fabric/
和
neoforge/
中重复放置 assets
将 assets 放在
common/src/main/resources/assets/
中
common Mixin 导入加载器 API 或仅针对某一个加载器将其放入相应平台子项目;如果两个生成平台配置都包含该 Mixin,则与加载器无关的 Mixin 可以放在 common 中
在 mod init 线程访问 world/registry使用 mod bus 事件进行 setup;初始化时绝不访问 world

References

参考资料