插件注册 API(RecipeManager)

基本信息

属性 值
入口类 api/RecipeManager.java(514 行)
底层实现 core/util/WitcheryRecipeHandlerInternal.java
插件基类 api/plugin/base/BasePluginWitchery.java(100 行)
插件接口 api/plugin/interfaces/WitcheryPlugin.java(12 行)
示例 api/plugin/ExamplePlugin.java(70 行)
嵌套类 9 个(8 个注册表 + 1 个 PluginManager)
本 mod 自用 仅 Oven 一组(OvenRecipes.generateDefaultOvenRecipes() 不走本 API)

这是本 mod 暴露给其它 mod 的接口层,用来增删 Witchery 的配方与仪式。

⚠️ 它操作的 8 类注册表全部属于 Witchery 本体, Witchery 不在本 wiki 收录范围(见 分类页), 本页只列本 mod 暴露的方法签名,不复述各注册表的内部结构。

8 个注册表

嵌套类 RecipeManager.java 行 方法数 增 删 查
WitchesOven 106 5 addRecipe(ItemStack, String, int, ...) removeRecipe(OvenRecipe) getOvenResult / findRecipeForOutput / findRecipeUsingIngredient
SpinningWheel 166 6 addRecipe(ItemStack fibre, ItemStack output, ItemStack[] modifiers) removeRecipe × 2 重载 getRecipe / findRecipeFor / findRecipeUsing / findRecipeUsingFibre
Distillery 244 5 addRecipe(input1, input2, jars, output1, ...) removeRecipe(DistilleryRecipe) getDistillingResult / findRecipeForOutput / findRecipeUsingIngredient
Kettle 306 4 addRecipe(output, hatBonus, familiarType, ...) removeRecipe(ItemStack mOutput) findRecipeWithSomeInputsAndAnOutput / findRecipesFor
CreaturePowers 355 4 add(CreaturePower) remove(CreaturePower) getCreaturePower(EntityLiving) / getCreaturePower(int entityID)
RitesAndRituals 392 3 add(ritualID, bookIndex, Rite, Sacrifice, ...) remove(int ritualID) getLastUsedRitualID()
Predictions 431 4 add(Prediction) remove(Prediction) getPredictions() / getPrediction(int aID)
Infusions 467 5 add(Infusion) remove(Infusion) getInfusion(int) / getInfusionOnPlayer(EntityPlayer) / getLastUsedInfusionID()

拼写照抄源码:RitesAndRituals 的查询方法叫 getLastUsedRitualID (Ritual,少一个 i),而 Infusions 的是 getLastUsedInfusionID(Infusion,有 i)。

并发标注不一致:SpinningWheel.addRecipe 与 SpinningWheel.removeRecipe(ItemStack, ItemStack, ItemStack...) 没有 synchronized(RecipeManager.java 的 SpinningWheel 段), 而该类其余方法以及其它 7 个类的所有 add / remove 都是 public static synchronized。见下方「源码缺陷」。

PluginManager(RecipeManager.java:64)

3 个方法,把插件挂到本 mod 的三个生命周期阶段:

方法 行 对应本 mod 阶段
loadPluginForPreInit(BasePluginWitchery) RecipeManager.java:64 段 WitcheryExtras.java:59-61
loadPluginForInit(BasePluginWitchery) 同上 WitcheryExtras.java:74-76
loadPluginForPostInit(BasePluginWitchery) 同上 WitcheryExtras.java:86-88

本 mod 侧的三个静态列表(WitcheryExtras.java:46-48):

private static final List<BasePluginWitchery> mPreInitEvents  = new ArrayList<>();
private static final List<BasePluginWitchery> mInitEvents     = new ArrayList<>();
private static final List<BasePluginWitchery> mPostInitEvents = new ArrayList<>();

以及 3 个公开注册入口(WitcheryExtras.java:116-126): addEventPreInit / addEventInit / addEventPostInit。

⚠️ 三个列表都是普通 ArrayList,没有同步保护; 遍历时若其它线程调用 addEventXxx 会抛 ConcurrentModificationException。

插件生命周期

BasePluginWitchery 提供 preInit() / init() / postInit() (WitcheryExtras.java:60、75、87 分别调用), WitcheryPlugin(12 行)是标记接口。

ExamplePlugin 演示的三件事(api/plugin/ExamplePlugin.java):

  1. 注册一条纺车配方(约 ExamplePlugin.java:40-46,用 ItemStack[] 传输入)
  2. 注册一条烤炉配方
  3. 移除一条既有蒸馏器配方: RecipeManager.Distillery.removeRecipe(RecipeManager.Distillery.findRecipeUsingIngredient(new ItemStack(Items.diamond))) (ExamplePlugin.java:61)

⚠️ ExamplePlugin 位于主源码集 src/main/java/alkalus/main/api/plugin/, 不是 src/test 或独立示例工程。它会被打进正式 jar。 它的类名以 Example 开头,不代表它是生效的插件—— 本仓库没有任何代码调用 loadPluginForXxx 传入它, 因此实际不会执行。

底层 WitcheryRecipeHandlerInternal

core/util/WitcheryRecipeHandlerInternal.java 提供 8 对 add/remove:

方法 行
addOvenRecipe / removeOvenRecipe 36 / 41
addDistilleryRecipe / removeDistilleryRecipe 64 / 69
addKettleRecipe / removeKettleRecipe 91 / 97
addNewCreaturePower / removeCreaturePower 120 / 130
addNewRiteToRiteRegistry / removeRiteFromRiteRegistry 157 / 170
addNewPrediction / removePrediction 205 / 214
addNewInfusion / removeInfusion 244 / 253
addSpinningWheelRecipe / removeSpinningWheelRecipe / removeSpinningRecipe 285 / 294 / 313

只有 addOvenRecipe / addNewRiteToRiteRegistry / addNewInfusion 等 部分方法带 synchronized。

9 个 core/types/Witchery_*.java 门面

core/types/ 下 9 个类是薄门面,只转发查询,不含注册逻辑:

文件 行数 转发到
Witchery_Cauldron.java 68 WitcheryRecipeHandlerInternal + 反射
Witchery_CreaturePower.java 67 反射读 CreaturePower 注册表
Witchery_Distillery.java 35 Witchery 静态表
Witchery_Infusion.java 63 反射
Witchery_Kettle.java 50 Witchery 静态表
Witchery_Oven.java 21 本 mod 的 OvenRecipes(唯一有本地实现的)
Witchery_Predictions.java 26 反射(见 预言权重重写)
Witchery_Rite.java 27 反射
Witchery_SpinningWheel.java 25 Witchery 静态表

⚠️ 源码缺陷

# 位置 问题
1 Witchery_Predictions.java:22 catch (Throwable t) {} 空 catch。反射失败被吞掉,调用方拿到空 Hashtable,无法与「真的没有预言」区分
2 OvenRecipes.java:19-21 getRecipeMap() 直接返回私有可变 ArrayList 引用,未做防御性拷贝。外部可任意增删(本意是开放 API,但同时使 mRecipeMap 无法保证完整性)
3 WitcheryExtras.java:46-48 3 个插件列表无同步保护
4 RecipeManager.java 的 SpinningWheel 段 addRecipe / removeRecipe 缺 synchronized,与同类其余方法不一致
5 ExamplePlugin.java 示例类打进正式 jar,且无人加载

关联