NeoForge P1 - 了解基础
一、NeoForge 的本质
实际上,它并不是一个工具,而是三层东西结合在一起的:
Java Mod 代码 -> NeoForge API 注册实体、监听事件、网络包等等 -> FML / ModLauncher 注入MinecraftFML(Forge Mod Loader) 是底层,负责在 Minecraft 启动时扫描 neoforge.mods.toml,用 ModLauncher 把模组的类和资源注入 JVM。
NeoForge API 是中间层,提供你在代码里用的所有东西:@Mod、DeferredRegister、事件总线、IItemHandler、网络系统等等。
模组就是最上层:一个被 @Mod 注解的类,通过构造函数接收 IEventBus,然后往上面注册实体、物品、事件监听器。
二、核心概念即 「注册 + 事件」
// 声明一个注册器,绑定到 ENTITY_TYPE 注册表public static final DeferredRegister<EntityType<?>> ENTITIES = DeferredRegister.create(Registries.ENTITY_TYPE, "mymod");
// 注册一个实体public static final DeferredHolder<EntityType<?>, EntityType<MyEntity>> MY_ENTITY = ENTITIES.register("my_entity", () -> EntityType.Builder.of(MyEntity::new, MobCategory.CREATURE) .sized(0.6f, 1.8f) .build("mymod:my_entity"));
// 构造函数里挂到 MOD 总线public MyMod(IEventBus modEventBus) { ENTITIES.register(modEventBus);}DeferredRegister 是一个「延迟注册容器」——你在静态初始化时往里放东西,但真正写入 Registries 是在相应的注册事件触发时。不会出现注册顺序问题。
同理,物品、方块、创造模式标签页、粒子类型、附魔……全部走同一套 DeferredRegister 模式。
三、事件
Minecraft 运行过程中会在特定时机「发事件」,模组「监听」事件来做自己的逻辑。NeoForge 有两条独立总线:
MOD 总线:
获取方式:构造函数参数 IEventBus modEventBus注册阶段:实体属性、模型层、能力注册典型事件为:EntityAttributeCreationEvent、RegisterCapabilitiesEventNeoForge 总线:
获取方式:NeoForge.EVENT_BUS游戏运行阶段:生物生成、玩家交互、世界 Tick典型事件为:LivingDeathEvent、PlayerInteractEvent、ServerTickEvent四、官方文档
NeoForge 相关文档链接:
主文档:https://docs.neoforged.net/社区 Wiki:https://github.com/neoforged/neoforge/wikiJavadoc:https://neoforged.net/javadoc/
学习项目(车万女仆 By.酒石酸菌):https://github.com/TartaricAcid/TouhouLittleMaid五、解耦
学习了dalao的开源项目,仔细发现它们的各个功能模块与注册都是解耦的。
在软件工程中,耦合(Coupling) 指的是两个模块之间的依赖程度。当两个模块紧密依赖时,修改其中一个往往会影响另一个,这种牵一发而动全身的关系就叫“高耦合”。
解耦(Decoupling) 就是通过某种方式降低这种依赖,让各个模块可以独立变化、独立理解、独立测试。
那么,在我们先前的代码开发中,不难发现把所有功能都集合在了 HelloWorld.java 中,把所有功能全部在此进行注册与功能实现,如果所有逻辑都写在 HelloWorld.java 里:主类、事件监听、注册逻辑……全部挤在一起。一旦要修改事件监听逻辑,就得打开这个庞大的文件,在众多代码中找到那一小块。
所以我们需要对项目进行重构,从开源学习项目中不难发现,dalao在软件包下将诸多功能模块分别置于不同软件包下,仅在主类里完成注册。
那么,就让我们开始吧。
5.1 分析项目结构
解耦前:
com.helloworld/ - HelloWorld.java - ModEvents.java由于所有的注册都写在了 HelloWorld.java 中,这显然是不行的,所以即刻重构!
预计解耦后:
com.helloworld/ - HelloWorld.java - com.helloworld.event/ - ModEvents.java - com.helloworld.init/ - InitItems.java - InitTab.java仅在主类里进行注册操作,把所有的功能模块写在其它软件包下(如event写事件,init写注册逻辑等),那么就开始对先前的代码进行改进吧!
5.2 实现重构
@Mod(HelloWorld.MODID)public class HelloWorld { public static final String MODID = "helloworld";
public HelloWorld(IEventBus modEventBus) { // 注册物品 InitItems.register(modEventBus);
// 注册物品栏 InitTab.register(modEventBus); }}@EventBusSubscriber(modid = HelloWorld.MODID)public class ModEvents { @SubscribeEvent public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) { event.getEntity().sendSystemMessage(Component.literal("Hello World!")); }}public class InitItems { public static final DeferredRegister<Item> ITEM = DeferredRegister.create(Registries.ITEM, HelloWorld.MODID);
public static void register(IEventBus modEventBus) { ITEM.register(modEventBus); }}public class InitTab { public static final DeferredRegister<CreativeModeTab> CREATIVE_TABS = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, HelloWorld.MODID);
public static final DeferredHolder<CreativeModeTab, CreativeModeTab> EXAMPLE_TAB = CREATIVE_TABS.register("example_tab", () -> CreativeModeTab.builder() .title(Component.translatable("springtsuki.tab")) .icon(() -> new ItemStack(Items.DIAMOND)) .displayItems((parameters, output) -> { output.accept(Items.COBBLESTONE); output.accept(Items.OAK_PLANKS); }) .build());
public static void register(IEventBus modEventBus) { CREATIVE_TABS.register(modEventBus); }}这样,对先前的代解耦就完成了。每一个功能模块的尾部预留一个注册函数,便于主入口调用。而具体的功能则可以编写在各个模块之间,实现解耦。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!













