类型系统与扩展
基本信息
| 属性 | 值 |
|---|---|
脚本可见类(@ZenClass) |
89(api/ 68 + mods/ 20 + runtime/ 1) |
类型扩展(@ZenExpansion) |
16 处声明,落到 13 个不同类型名 |
实例方法(@ZenMethod) |
205(api/ 140 + mods/ 54 + 其余 11) |
属性读取(@ZenGetter) |
220 |
属性写入(@ZenSetter) |
25 |
| 成员读取 / 写入 | @ZenMemberGetter 2 / @ZenMemberSetter 1 |
类型转换(@ZenCaster) |
27(全部无参) |
运算符重载(@ZenOperator) |
32 |
括号处理器(@BracketHandler) |
4 |
门控注解(@ModOnly) |
23 |
| 统计口径 | grep -rE '^\s*@Xxx\b' src/main/java --include=*.java | wc -l,行首锚定以排除 javadoc 里的文字提及 |
注解本身来自外部依赖 com.github.GTNewHorizons:ZenScript(dependencies.gradle:2),本仓 src/main/java 下没有 stanhebben 包;仅 buildSrc/src/main/java/stanhebben/zenscript/annotations/ 存有一份注解源码副本,供构建期字节码扫描与文档生成使用。
三种进入命名空间的路径
MineTweakerAPI.registerClass(Class)(MineTweakerAPI.java:304-326)按注解分派:
| 注解 | 动作 | 源码行 |
|---|---|---|
@ZenExpansion |
GlobalRegistry.registerExpansion —— 合并进已有 TypeExpansion |
MineTweakerAPI.java:309-311 |
@ZenClass |
GlobalRegistry.registerNativeClass —— root.put(name, ...) |
MineTweakerAPI.java:312-314 |
@BracketHandler |
newInstance() 后 registerBracketHandler |
MineTweakerAPI.java:315-324 |
关键差异:
- 扩展是累加的。
GlobalRegistry.registerExpansion只在键不存在时新建TypeExpansion,之后一律expansions.get(name).expand(cls, types)往同一个对象上继续加成员(GlobalRegistry.java:62-77)。多个类声明同一扩展名是设计如此,不是冲突。 - 类是覆盖的。
registerNativeClass直接root.put(type.getName(), new SymbolType(type), errors)(GlobalRegistry.java:94),不检查键是否已存在;重复名静默后写覆盖先写。而registerGlobal反倒会抛IllegalArgumentException(GlobalRegistry.java:55-57)—— 两种注册路径的重复名行为不一致。
扩展:13 个类型名由 16 处声明合成
| 扩展类型名 | 声明处 | 门控 |
|---|---|---|
any[] |
expand/ExpandAnyArray.java |
无 |
any[any] |
expand/ExpandAnyDict.java |
无 |
bool |
expand/ExpandBool.java |
无 |
byte |
expand/ExpandByte.java |
无 |
short |
expand/ExpandShort.java |
无 |
int |
expand/ExpandInt.java |
无 |
long |
expand/ExpandLong.java |
无 |
float |
expand/ExpandFloat.java |
无 |
double |
expand/ExpandDouble.java |
无 |
string |
expand/ExpandString.java |
无 |
minetweaker.item.IItemStack |
expand/ExpandItemStack.java |
无 |
minecraft.item.IItemStack |
mods/ic2/expand/ItemExpansion.java:22 |
@ModOnly("IC2") |
minetweaker.item.IIngredient |
4 处合成(见下) | 1 处带 @ModOnly("IC2") |
minetweaker.item.IIngredient 由 4 个类分别贡献成员,属累加设计:
| 声明文件 | 行 | 门控 |
|---|---|---|
api/item/IngredientTransform.java |
:11 |
无 |
api/item/IngredientCondition.java |
:12 |
无 |
api/tooltip/IngredientTooltips.java |
:15 |
无 |
mods/ic2/expand/IngredientExpansion.java |
:21 |
@ModOnly("IC2") |
minecraft.item.IItemStack 扩展(IC2 的 @ModOnly,ItemExpansion.java:21-22)只在装了 IC2 时生效,为原生 ItemStack 补 getChargeLevel() 等成员。
ExpandItemStack 等 11 个 expand/ 类同时带 @ZenCaster(expand/ExpandByte.java:20 等),让脚本能把原生 byte / int / ItemStack 等隐式转成脚本类型。
缺陷:WeightedLiquidStack 标注了错误的脚本名
minetweaker/api/liquid/WeightedLiquidStack.java:12 写的是:
@ZenClass("minetweaker.item.WeightedItemStack")
与 minetweaker/api/item/WeightedItemStack.java:12 完全同名(grep -rn 'minetweaker.item.WeightedItemStack' 命中且仅命中这两个文件)。后果:
- 脚本里没有
minetweaker.liquid.WeightedLiquidStack这个名字。 - 两个类抢同一个
root键,而registerNativeClass不做重复检查(GlobalRegistry.java:94),静默后注册者胜出。 - 谁先谁后由构建期扫描顺序决定:
RegisterZenClassesTask.iterate用dir.listFiles()(buildSrc/src/main/java/minetweaker/tasks/RegisterZenClassesTask.java:106),Java 规范不保证该数组顺序。因此该名到底绑到物品版还是流体版加权栈,在不同机器/不同构建上可能不同。
缺陷:Java 类名与脚本名不一致的 4 处
脚本作者按 ZenScript 名字写代码时容易踩:
| Java 类 | 脚本名 | 位置 |
|---|---|---|
PlayerCraftedEvent |
minetweaker.event.PlayerCraftingEvent |
api/event/PlayerCraftedEvent.java:11-12 |
IEventHandle |
minetweaker.event.IEventHandler |
api/event/IEventHandle.java:12 |
WeightedLiquidStack |
minetweaker.item.WeightedItemStack |
见上节 |
IClient |
minetweaker.api.IClient |
api/client/IClient.java:11 |
其中 IClient 的前缀风格也与其余 60 余个 minetweaker.<子包>.<接口> 命名不同(多了一层 api.)。另 vanilla.* 4 个类型(IVanilla、ILootRegistry、ISeedRegistry、LootEntry)完全不带 minetweaker. 前缀。
未注册的接口(12 个)
api/ 下有 12 个接口没有 @ZenClass,因此类型不进命名空间:
| 接口 | 影响的全局对象 |
|---|---|
api/event/IEventManager.java |
events |
api/formatting/IFormatter.java |
format |
api/compat/IJEIRecipeRegistry.java |
NEI/IEI 配方查询 |
api/mods/IMod.java |
loadedMods 元素类型 |
api/recipes/IFurnaceRecipe.java |
furnace.all 元素类型 |
api/recipes/IMTRecipe.java |
— |
api/data/IDataConverter.java |
NBT 转换 |
api/entity/IEntityItem.java |
掉落物实体 |
api/damage/IDamageSource.java |
— |
api/resource/IResourceManager.java |
— |
api/vanilla/IVillagerRegistry.java |
— |
api/world/IBiome.java |
game.biomes 元素类型 |
IEventManager 行首锚定注解数为 0(grep -cE '^\s*@' api/event/IEventManager.java → 0),而它 20 个 on* 方法也全部没有 @ZenMethod(api/event/IEventManager.java:15-50)。同类中 IEventHandle 有 @ZenClass + @ZenMethod close(),CommandValidators 有 @ZenClass + @ZenGetter isOp(),说明省略是逐个决定的而非全接口风格。
无法核实:
events.onPlayerCrafted(...)、format.color(...)这类调用在脚本中究竟是否可用,取决于外部 ZenScript 引擎对未注册 Java 类型的方法解析策略。ZenScript 1.0.2-GTNH 的源码不在本仓(仅buildSrc有注解与文档生成器的副本),我无法在本仓内确认其行为。已核实的是本仓侧的注册事实:这些接口未被registerClass处理。