注册表
注册是获取模组中的对象(物品,方块,声音等)并使其为游戏所知的过程。注册事物非常重要,因为没有注册,游戏就不会知道在mod中有这些对象,并且会表现出大量无法解释的行为(并且可能会崩溃)。需要注册的一些东西的例子有Block,Item,Biome。
大多数需要在游戏中注册的东西都由Forge注册表管理。注册表是一个简单的对象,类似于为键分配值的映射。此外,它们会自动为值分配整数ID。Forge使用带有ResourceLocation键的注册表来注册对象。这允许ResourceLocation表现得像对象的“注册表名称”。可以使用 getRegistryName/setRegistryName 访问对象的注册表名称。setRegistryName只能被调用一次,并且调用它两次会导致异常。每种类型的可注册对象都有自己的注册表,两个不同注册表中的名称不会发生冲突。(例如,Block的注册表和Item的注册表中,一个 Block 和一个 Item 可以使用相同的名称mod:example来注册而不会发生冲突。但是,如果有两个Block使用该名称注册,则会抛出异常。)
注册事物
注册事物的推荐方法是通过RegistryEvent。这些事件在预初始化之后立即触发,注册表应该在RegistryEvent.NewRegistry被创建。之后,RegistryEvent.Register为每一个注册过的注册表运行一次。因为Register是一般事件,所以事件处理程序应将type参数设置为要注册的对象的类型。该事件将包含注册表以向(getRegistry)注册,并且可以在注册表中使用register(或registerAll)注册。这是一个注册方块的事件处理程序的示例:
@SubscribeEvent
public void registerBlocks(RegistryEvent.Register<Block> event) {
event.getRegistry().registerAll(block1, block2, ...);
}
订单RegistryEvent.Register事件中的注册次序是按字母顺序,也就是说Block将始终首先被注册,并且Item会一直第二个被注册,紧跟Block。在Register<Block>事件触发之后,所有的ObjectHolder记号被刷新,并且在Register<Item>触发后他们会再次被刷新。在所有其他Register事件触发后,它们会第三次刷新。
RegistryEvents的目前支持以下几种类型:Block,Item,Potion,Biome,SoundEvent,PotionType,Enchantment,IRecipe,VillagerProfession,EntityEntry。
另一种将对象注册到注册表的旧方法是 GameRegistry.register。当有东西建议使用此方法的时候,则这个东西应该替换为适当的注册表事件的事件处理程序。此方法只是使用 IForgeRegistryEntry::getRegistryType 找到与 IForgeRegistryEntry 对应的注册表,然后将该对象注册到注册表。还有一个方便的的重载,它需要一个 IForgeRegistryEntry 和一个 ResourceLocation(作为参数),相当于调用 IForgeRegistryEntry::setRegistryName紧接着一个 GameRegistry.register 调用(译者注:原句断句略复杂,此处根据Forge API Javadoc确定)。
信息: 注册一个实体可能有点令人困惑,因为它不使用Entity类,而是一个EntityEntry。这些都是通过利用EntityEntryBuilder来创造的。 EntityEntryBuilder#id()相当于 IForgeRegistryEntry 里的 setRegistryName() 方法 ,区别在于它也需要一个模组内部int ID。在注册时,一个的简单计数器(counter)就足够了,因为此ID仅用于网络(译者注:没有理解这句和其他几句的关系)。
创建注册表
有一个全局注册表,其中存储了所有其他注册表。通过使用一个注册表用来存储的Class(译者注:原文这里用了和其它翻译作“类”的部分的不同的颜色,可能是新的概念,故这里不做翻译)或者这个Class的ResourceLocation名称,可以从此注册表中检索注册表。例如,可以使用GameRegistry.findRegistry(Block.class)获取方块的注册表。任何模组都可以创建自己的注册表,任何模组都可以注册到来自任何其他模组的注册表。通过使用RegistryEvent.NewRegistry事件处理程序中的RegistryBuilder来创建注册表。此类为将生成的注册表获取特定参数,例如名称,它的值对应的Class以及更改注册表时的多种回调。在调用RegistryBuilder::create时注册表被构建,注册到metaregistry,并返回给调用者。
为了使类具有注册表,它需要实现 IForgeRegistryEntry。该接口定义 getRegistryName(ResourceLocation),setRegistryName(ResourceLocation) 和 getRegistryType()。getRegistryType是Class对象要注册到的注册表的基础。建议扩展默认 IForgeRegistryEntry.Impl 类而不是 IForgeRegistryEntry 直接实现。该类还提供了setRegistryName的两种便利实现:一种是参数是单个字符串,另一种是有两个字符串参数。采用单个字符串的重载检查输入是否包含一个冒号(即它检查传入的字符串 ResourceLocation 是否具有命名空间),如果不包含,则使用当前 modid 作为资源命名空间。而两个参数的重载只是使用modID作为命名空间和name路径构造注册表名称。
将注册表值注入字段
可以将注册表中的Forge注入值放入public static final类的字段中。这是通过使用标记类和拥有@ObjectHolder的字段来完成的。如果一个类有这个注释,那么其中的所有public static final字段也会被认为是对象持有者,并且注释的值是持有者的命名空间(即每个字段使用它作为要注入的对象的注册表名称的默认命名空间)。如果某个字段具有此标记,并且该值不包含命名空间,则从周围类的@ObjectHolder标记中选择该命名空间。如果在此情况下未对类进行标记,则会忽略该字段并显示警告。如果它确实包含命名空间,则注入该字段的对象是具有该名称的对象。如果类有标记而其中一个public static final字段没有,那么对象名称的资源路径将被视为字段的名称。注册表的类型取自字段的类型。
注意: 如果找不到对象,或者因为对象本身尚未注册,或者因为注册表不存在,则会记录调试消息并保持字段不变。
由于这些规则相当复杂,以下是一些示例:
@ObjectHolder("minecraft") // Resource namespace "minecraft"
class AnnotatedHolder {
public static final Block diamond_block = null; // public static final is required.
// Type Block means that the Block registry will be queried.
// diamond_block is the field name, and as the field is not annotated it is taken to be the resource path.
// As there is no explicit namespace, "minecraft" is inherited from the class.
// Object to be injected: "minecraft:diamond_block" from the Block registry.
@ObjectHolder("ender_eye")
public static final Item eye_of_ender = null; // Type Item means that the Item registry will be queried.
// As the annotation has the value "ender_eye", that overrides the field's name.
// As the namespace is not explicit, "minecraft" is inherited from the class.
// Object to be injected: "minecraft:ender_eye" from the Item registry.
@ObjectHolder("neomagicae:coffeinum")
public static final ManaType coffeinum = null; // Type ManaType means that the ManaType registry will be queried. This is obviously a registry made by a mod.
// As the annotation has the value "neomagicae:coffeinum", that overrides the field's name.
// The namespace is explicit, and is "neomagicae", overriding the class's "minecraft" default.
// Object to be injected: "neomagicae:coffeinum" from the ManaType registry.
public static final Item ENDER_PEARL = null; // Note that the actual name is "minecraft:ender_pearl", not "minecraft:ENDER_PEARL".
// However, since constructing a ResourceLocation lowercases the value, this will work.
}
class UnannotatedHolder { // Note lack of annotation on this class.
@ObjectHolder("minecraft:flame")
public static final Enchantment flame = null; // No annotation on the class means that there is no preset namespace to inherit.
// Field annotation supplies all the information for the object.
// Object to be injected: "minecraft:flame" from the Enchantment registry.
public static final Biome ice_flat = null; // No annotation on the class or the field.
// Therefore this just gets ignored.
@ObjectHolder("levitation")
public static final Potion levitation = null; // No resource namespace in annotation, and no default specified by class annotation.
// Therefore, THIS WILL FAIL. The field annotation needs a namespace, or the class needs an annotation.
}