WailaHandler 插件与提示栏组装
基本信息
| 属性 | 值 |
|---|---|
| 类型 | WAILA 数据提供者(IWailaDataProvider) |
| 注册类 | squeek.wailaharvestability.WailaHandler |
| 注册方式 | 通过 FML IMC 注册,未直接依赖 WAILA |
| IMC 消息 | FMLInterModComms.sendMessage("Waila", "register", "squeek.wailaharvestability.WailaHandler.callbackRegister") |
| 注册时机 | FMLInitializationEvent(@SideOnly(Side.CLIENT)) |
| 服务端 NBT | 不采集(getNBTData 原样返回 tag) |
| 源码 | WailaHandler.java(321 行) |
概述
本模组是纯粹的 WAILA 插件:不注册任何方块、物品、实体,唯一的内容就是在玩家注视方块时往 WAILA 提示栏的 Body 区域追加采集信息行。
因为 getNBTData 不写入任何数据,所有判定都在客户端每次渲染提示栏时实时重算,服务端不参与。
回调逐项清单
IWailaDataProvider 的全部回调,以及模组额外实现的静态注册回调:
| 回调 | 返回/行为 | 对提示栏的贡献 |
|---|---|---|
getWailaStack |
返回 null |
不覆盖 WAILA 显示的物品栈,保持原样 |
getWailaHead |
原样返回 toolTip |
无 |
getWailaBody |
追加最多 3 行采集信息 | 唯一有输出的回调 |
getWailaTail |
原样返回 toolTip |
无 |
getNBTData |
原样返回 tag |
不采集任何服务端数据 |
callbackRegister(IWailaRegistrar)(static) |
播种 16 个配置默认值 → addConfig 注册 → registerBodyProvider(instance, Block.class) |
把自身挂到所有 Block 的 Body 上 |
registerBodyProvider 的作用域
registrar.registerBodyProvider(instance, Block.class) 针对所有方块注册——没有黑名单、没有 mod 白名单。因此每一个被 WAILA 显示的方块都会走一遍 getWailaBody。唯一的整体跳过条件是 CreativeBlocks,见 兼容代理。
生命周期
| 阶段 | 事件 | 动作 | 端 |
|---|---|---|---|
| preInit | FMLPreInitializationEvent |
Config.init(event.getSuggestedConfigurationFile());向 VersionChecker 发 addVersionCheck IMC |
仅客户端 |
| init | FMLInitializationEvent |
MissingHarvestInfo.init();向 Waila 发 register IMC |
仅客户端 |
| postInit | FMLPostInitializationEvent |
若加载了 IguanaTweaksTConstruct → 置 hasIguanaTweaks 并 ProxyIguanaTweaks.init();ToolHelper.init() |
仅客户端 |
三个事件处理器全部标注 @SideOnly(Side.CLIENT)——本模组没有服务端逻辑。
getWailaBody 流程
- 取
block = accessor.getBlock()、meta = accessor.getMetadata()。 - CreativeBlocks 早退:
ProxyCreativeBlocks.isCreativeBlock(block, meta)为真则立即原样返回。 - 取
player = accessor.getPlayer(),设capMeta = true。 - 取
gtBlockType = ProxyGregTech.getGTBlockType(block)。 - 显示态与实际态的选择——当 WAILA 显示的
itemStack是ItemBlock时:- 若
gtBlockType不是Casing也不是Machine,则改用Block.getBlockFromItem(itemStack.getItem())与itemStack.getItemDamage()作为判定对象。源码注释说明理由:方块可能在"伪装"自己是什么,此时应按它声称的身份判定采集等级。 - GT 的
Casing与Machine相反,保留实际方块,因为所需的挖掘等级由真实方块决定。
- 若
- 若
gtBlockType == Ore则capMeta = false——GT 矿石的 metadata 会超过 16,不能按原版上限截断。 - 读
harvestability.minimal(读取点显式传默认值false)。 - 调
getHarvestability(...)填stringParts;非空时,最小模式用RESET + MINIMAL_SEPARATOR_STRING拼接成单行,否则逐行addAll。
getHarvestability 判定顺序
- 读 8 个布尔量(3 个主开关 + 3 个潜行开关 +
unharvestableonly+oresonly+toolrequiredonly)。潜行类开关的判定式统一为开关 && (!潜行开关 || player.isSneaking())。 - 总门控:三个主开关全为
false→ 直接返回。 - 矿石过滤:
oresonly && !OreHelper.isBlockAnOre(block, meta)→ 返回。 - 不可破坏/冒险模式:若
!player.isCurrentToolAdventureModeExempt(x,y,z)或BlockHelper.isBlockUnbreakable(...)→ 输出一行"暗红✘+harvestable后缀"并返回。 - metadata 截断:
if (meta >= 16 && capMeta) meta = 0;。源码注释说明是为了避免生物刷怪笼等 meta ≥ 16 的方块触发数组越界——getHarvestLevel/getHarvestTool数组只有 16 个元素。 - 取
harvestLevel = block.getHarvestLevel(meta)、effectiveTool = BlockHelper.getEffectiveToolOf(...)。若effectiveTool != null && harvestLevel < 0则把harvestLevel抬为0;blockHasEffectiveTools = harvestLevel >= 0 && effectiveTool != null。 - 预先算好
shearability与silkTouchability两个字符串。 toolrequiredonly过滤:材质isToolNotRequired且!blockHasEffectiveTools且两个字符串都为空 → 返回。- 手持物分析(
itemHeld != null时):isHoldingTinkersTool = ToolHelper.hasToolTag(itemHeld)(NBT 含InfiTool)isHoldingGTTool = ProxyGregTech.isGTTool(itemHeld)(NBT 含GT.ToolStats)isAboveMinHarvestLevel=(currentlyharvestable 或 harvestlevel 开启) && ToolHelper.canToolHarvestLevel(...)isEffective=effectivetool 开启 && ToolHelper.isToolEffectiveAgainst(...)canHarvest:GT 工具走itemHeld.func_150998_b(block)(源码注释:GT 工具不看Material#isToolNotRequired);否则ToolHelper.canToolHarvestBlock(...) || (!isHoldingTinkersTool && block.canHarvestBlock(player, meta))
isCurrentlyHarvestable = (canHarvest && isAboveMinHarvestLevel) || (!isHoldingTinkersTool && ForgeHooks.canHarvestBlock(block, player, meta))unharvestableonly:hideWhileHarvestable && isCurrentlyHarvestable→ 返回。- 按顺序输出最多 3 行。
输出行构成
第 1 行:状态 + 剪刀 + 精准
[颜色]✔ 或 ✘[文字后缀] [颜色]✓ [颜色]✂
- 三个字段中至少有一个非空才输出这一行。
- 字段间的间隔是硬编码的单个空格
" "(separator = (!shearability.isEmpty() || !silkTouchability.isEmpty() ? " " : "")),只在前两者之后加。 - 最小模式下
✔/✘不带文字后缀;✓与✂任何模式下都没有后缀。
第 2 行:所需工具
- 条件:
harvestLevel != -1 && showEffectiveTool && effectiveTool != null。 - 非最小模式前缀
wailaharvestability.effectivetool(en_USEffective Tool :/ zh_CN所需工具 :)。 - 工具名优先取
wailaharvestability.toolclass.<类别>的翻译;取不到则把类别字符串首字母大写(Wrench、Cutter这类回退值)。 - 颜色:
getBooleanColor(isEffective && (!isHoldingTinkersTool || canHarvest), isHoldingTinkersTool && isEffective && !canHarvest)。modified参数专门用于 Tinkers 工具——手持 Tinkers 工具时工具本身"对得上",但还没到挖掘等级要求,这种情况显示成更亮的绿色。
第 3 行:挖掘等级
- 条件:
harvestLevel >= 1 && (showHarvestLevel || showHarvestLevelNum)。 - 名称先经
stripFormatting去掉颜色码再比较。 - 名称与数字相同时自动隐藏数字:
showHarvestLevelNum = showHarvestLevelNum && (!showHarvestLevel || !harvestLevelName.equals(harvestLevelNum))。 - 三种渲染:
名称 (N)(名称+数字)、名称(仅名称)、N(仅数字)。 - 颜色:
getBooleanColor(isAboveMinHarvestLevel && canHarvest)。
数值
| 数值 | 值 |
|---|---|
| 最多输出行数 | 3(非最小模式) |
| 最小模式输出行数 | 1 |
IWailaDataProvider 回调总数 |
5(其中 4 个无输出) |
| 读取的 WAILA 配置键数 | 11(getHarvestability)+ 2(getShearabilityString)+ 2(getSilkTouchabilityString)+ 1(getWailaBody 的 minimal)= 16 |
configOptions 注册项数 |
16(与读取点一一对应,无孤立项) |
源码现状提示
@Mod的dependencies是after:TConstruct;after:ExtraTiC;after:TSteelworks;after:Mariculture,没有after:Waila。本模组靠 IMC 注册,WAILA 缺席时这条 IMC 被静默丢弃,不报错也不加载——但模组仍会正常"装上"。getSilkTouchabilityString与getShearabilityString的判定依赖玩家当前手持物:不手持剪刀时✂显示为暗红,手持剪刀但该方块不可剪时才显示亮红。精准采集同理。- 剪刀判定
getShearabilityString的条件分支包含Blocks.deadbush与Blocks.double_plant(掉落实体为null时),但函数体里的((IShearable) block)是无条件强制转换,没有被instanceof IShearable保护。源码层面这是一处未被守护的强转。 getHarvestability接受player、position、capMeta等参数,是public实例方法,可被外部调用,但模组内部只有getWailaBody一个调用点。- 冒险模式判定(
isCurrentToolAdventureModeExempt)为假时必然输出✘——玩家在冒险模式下无法破坏方块,这是设计意图而非 bug。
相关条目
- WAILA 采集选项 - 上述 16 个开关的完整默认值表
- Forge 配置项 - 4 个符号字符与最小模式分隔符
- 工具类别与有效工具判定 -
harvestLevel/effectiveTool/canHarvest的来源 - 采集等级名称解析 - 第 3 行名称的三级回退链
- 矿石判定 -
oresonly过滤的实现 - 兼容代理 - CreativeBlocks 早退与 GT 方块类型
- 缺失采集信息修正 -
getWailaBody之外的唯一启动期副作用 - 显示样式与配色 - 上述各行颜色的取值表