模组 API 与扩展点

基本信息

OpenBlocks 暴露三层扩展点:FlinFlam 注册表(运行时可查可写)、6 个 RPC 接口(客户端↔服务端)、以及若干纯数据接口(供其它 mod 实现以被识别)。

层次 入口 位置
静态门面 openblocks.api.OpenBlocksApi api/OpenBlocksApi.java
API 提供者装配 openblocks.ApiSetup ApiSetup.java
编译期注入 openblocks.api.ApiHolder api/ApiHolder.java
RPC 注册 RpcCallDispatcher.INSTANCE.startRegistration() OpenBlocks.java:619-622

API 提供者机制

OpenBlocksApi 只有一个静态 provider 字段与两个方法(OpenBlocksApi.java:3-35):

成员 行 行为
interface ApiProvider :5-10 getApi(Class<T>) / isApiPresent(Class<T>)
init(ApiProvider) :17-20 重复调用抛 IllegalStateException("API already initialized")(:18)
getApi(Class<T>) :26-29 @Deprecated,注释指向 ApiHolder(:22-25);provider 为 null 时抛 IllegalStateException("API not initialized")
isApiPresent(Class<T>) :31-34 同上

装配逻辑在 ApiSetup(58 行):

  1. registry = new ApiProviderRegistry<IApiInterface>(IApiInterface.class)(:23-24)
  2. setupApis() 注册唯一实现 registry.registerInstance(FlimFlamRegistry.instance) 然后 registry.freeze()(:28-31)——冻结后不可再注册
  3. installHolderAccess(ASMDataTable) 走 ASM 注入 ApiFactory.instance.createApi(ApiHolder.class, IApiInterface.class, table, registry)(:33-35)
  4. injectProvider() 调 OpenBlocksApi.init(new ApiProviderAdapter(registry)),失败时重新抛出 IllegalStateException 并附上 API 来源(:37-46)
  5. getApiSource() 失败时 Log.severe 并返回 "<unknown, see logs>"(:48-56)——这是不吞异常的 catch (Throwable) 写法

private final ApiSetup apiSetup = new ApiSetup();(OpenBlocks.java:249)

ApiHolder 的 javadoc 里提到「OpenPeripheralAddons」——那是上游复制粘贴留下的痕迹(api/ApiHolder.java),不影响功能。

唯一已注册的 API 实现

FlimFlamRegistry.instance(ApiSetup.java:29)。对外接口是 IFlimFlamRegistry(api/IFlimFlamRegistry.java):

public interface IFlimFlamRegistry extends IApiInterface {
    List<String> getAllFlimFlamsNames();
    IFlimFlamDescription getFlimFlamByName(String name);
    List<IFlimFlamDescription> getFlimFlams();
    void registerFlimFlam(String name, IFlimFlamDescription meta);
    FlimFlamDescriptionSimple registerFlimFlam(String name, int cost, int weight, IFlimFlamAction effect);
}

注意:registry.freeze()(ApiSetup.java:30)在 postInit 之前执行,因此外部 mod 无法再通过 API 新增 FlimFlam 效果——只有 postInit 里 OpenBlocks.java:760-781 那 17 个是活的。

配套的 3 个 FlimFlam 接口:

接口 作用
IFlimFlamDescription 效果元数据:name() / weight() / cost() / isSafe() / isSilent() / action() / canApply(int luck)
FlimFlamDescriptionSimple 上者的默认实现,构造器 (name, cost, weight, effect),链式 markSafe() / markSilent() / setRange(a, b)
IFlimFlamAction 效果的实际执行体

FlimFlamDescriptionSimple 的 cost 实际是幸运值区间上界(负值才生效),细节与「不作为缺陷」的论证见附魔页。

6 个 RPC 接口

RpcCallDispatcher.INSTANCE.startRegistration()(OpenBlocks.java:619-622)注册:

顺序 接口 文件
1 IRotatable rpc/IRotatable.java
2 IStencilCrafter rpc/IStencilCrafter.java
3 IColorChanger rpc/IColorChanger.java
4 ILevelChanger rpc/ILevelChanger.java
5 ITriggerable rpc/ITriggerable.java
6 IGuideAnimationTrigger rpc/IGuideAnimationTrigger.java

rpc/ 包共 6 个文件,与注册的 6 个接口一一对应,无多余文件。

IGuideAnimationTrigger 的实际使用方是 TileEntityBuilderGuide(common/tileentity/TileEntityBuilderGuide.java:21 实现该接口),用于在放置方块后从服务端触发客户端动画。IStencilCrafter 由 TileEntityDrawingTable 实现(common/tileentity/TileEntityDrawingTable.java:21)。

供其它 mod 实现的标记接口

这些接口不由 OpenBlocks 调用,而是 OpenBlocks 检查实现者来决定行为:

接口 用途 行
IElevatorBlock 电梯配对查询:getColor(World,x,y,z) / getRotation(...) 与 PlayerRotation 枚举 api/IElevatorBlock.java
IPointable 指针类物品的选中回调:onPointingStart / onPointingEnd api/IPointable.java
IPaintableBlock 24 位 RGB 版的 Block.recolourBlock api/IPaintableBlock.java
IMagnetAware 磁铁是否可释放:canRelease() api/IMagnetAware.java
IApiInterface 所有 API 接口的根标记 api/IApiInterface.java

IElevatorBlock 有两个内部实现,都在 OpenBlocks 自己包里:

  • BlockElevator(common/block/BlockElevator.java:23)——颜色存在方块 meta(getColor 返回 world.getBlockMetadata,:72-74)
  • BlockElevatorRotating(common/block/BlockElevatorRotating.java:16)——颜色存在 TileEntity(getColor 走 getColorMeta,:65-67),并额外实现 getRotation(:70-85)

IPaintableBlock 由 BlockCanvas 实现(common/block/BlockCanvas.java:23),把 recolourBlockRGB 委派给 TileEntityCanvas.applyPaint(:101-105)。

ElevatorActionHandler 判定电梯配对时用 instanceof IElevatorBlock,共 4 处::63(当前电梯,是强制转换 (IElevatorBlock) world.getBlock(...))、:75-76(候选电梯)、:171(事件入口前置检查)、:208。所以外部 mod 实现的 IElevatorBlock 也能参与电梯配对——这是 OpenBlocks 对外开放最实质的扩展点之一。

事件

events/ 包只有 2 个事件类:

事件 方向 行
ElevatorActionEvent C2S events/ElevatorActionEvent.java:13(@NetworkEventMeta(direction = EventDirection.C2S))
PlayerActionEvent — events/PlayerActionEvent.java

另在 api/ 包里有 4 个可取消的事件:GraveDropsEvent、GraveSpawnEvent、InventoryEvent、SleepingBagUseEvent。

数值

数值名 来源 值
已注册 API 实现数 ApiSetup.java:29 1(仅 FlimFlamRegistry)
已注册 RPC 接口数 OpenBlocks.java:619-622 6
rpc/ 包文件数 目录 6(与注册数一致)
已注册的 FlimFlam 效果数 OpenBlocks.java:760-781 17
供实现的标记接口数 api/ 包 5
网络事件数 OpenBlocks.java:614-617 6
api/ 包 .java 文件数 目录 16(含 1 个 package-info.java)
api/ 包事件类数 目录 4

相关条目