1. 注册表
Minecraft 是一个处处充满了注册的游戏,因为…… 这个游戏里充满了各种各样奇奇怪怪的对象。
1.1. 事件驱动的注册
Forge 引入了一个注册表系统以接管原版的注册表系统。Forge 的系统与原版的系统相比多了 ID 自动分配、映射存储、映射重定义等好用的功能。其中映射存储更是使得跨整合的存档分享成为了可能。此前带 Mod 的存档的分享往往需要连带整合一起发布,而现在的话只需要给出必须的 Mod 列表就可以了。
// 注意这里的泛型参数。它的边界是 T extends IForgeRegistryEntry<T>,
// 意味着所有注册项都可以用这个方法来完成注册
//
// 泛型事件的使用决定了你需要对每一种对象的注册都写一个订阅。你不能向方块的注册表里
// 注册物品,反之亦然。
@SubscribeEvent
public static void onRegistry(RegistryEvent.Register<Block> regBlock) {
regBlock.getRegistry().register(myBlock);
}
1.2. 注册项?
刚才说到了“注册项”这个概念。 那么我们来总结一下吧,需要通过标准化的注册表进行注册的东西有:
- 物品(Item)
- 方块(Block)
- 附魔(Enchantment)
- 药水(Potion)
- 生物群系 (Biome)
- 声音事件 (SoundEvent)
- 村民的职业(VillagerProfession)
- 实体(以 EntityEntry 的形式出现)
原版工作台合成(IRecipe)这些东西会在后面的章节中一一讲到。
原版工作台合成的确也是受注册表控制的,但大多数时候你不应该通过这个方式注册原版工作台合成,而是写 JSON。相关细节在第二十章中有详细阐述。
1.3. 结束了?
非也。还有一个十分有趣的注解: @GameRegistry.ObjectHodler
。
import javax.annotation.Nonnull;
// 本范例改编自 ForgeDocs 的对应案例
// 标记在主类上的 ObjectHolder 指定了默认的 namespace,即 ResourceLocation 中
// 构造器的第一个参数。
@ObjectHolder("minecraft")
public class MyStuffHolder {
/*
* 被注入的 Field 要求是:
* public,静态,<T extends IForgeRegistryEntry.Impl<T>>。
* final 可有可无。若使用 final,可以初始化为 Blocks.AIR 以避开 IDE 的 null 检查。
*/
@ObjectHolder("beacon")
public static final Block VANILLA_BEACON = Blocks.AIR;
/*
* 这样也可以,前提是:
* 1. 所在类上有 ObjectHolder 注解,比如这个类。类的注解中的参数会用作 namespace。
* 2. 字段名会用作 path,所以必须与某个物品匹配。
* 查询时会统一转化为小写字母,所以可以放心使用传统的大写下划线常量命名。
* 这样得到的物品便是 minecraft:iron_hoe。
*/
public static final Item IRON_HOE = Items.AIR;
//或者你还可以这样覆盖标记在类上的domain
//依旧是参考 ResourceLocation 的构造器,这次参考一个 String 参数的构造器
@ObjectHolder("forestry:grafter")
public static final Item FORESTRY_GRAFTER = Items.AIR;
}
//或者你可以不在类上加@ObjectHolder:
public class MyStuffHolder2 {
//直接这样也可以
@ObjectHolder("theoneprobe:probe")
public static final Item THEONEPROBE_PROBE = null;
}
虽然注册事件的触发顺序是未定义的,但有一个例外:根据 ForgeDocs,方块注册总是在物品注册之前。目前 MinecraftForge 本身所有的 test 都可以印证此约定。
1.4. 复用注册表
所以你也可以使用 IForgeRegistry
来让其他 Mod 注册你自定义的注册项。
// 待补