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 流程

  1. 取 block = accessor.getBlock()、meta = accessor.getMetadata()。
  2. CreativeBlocks 早退:ProxyCreativeBlocks.isCreativeBlock(block, meta) 为真则立即原样返回。
  3. 取 player = accessor.getPlayer(),设 capMeta = true。
  4. 取 gtBlockType = ProxyGregTech.getGTBlockType(block)。
  5. 显示态与实际态的选择——当 WAILA 显示的 itemStack 是 ItemBlock 时:
    • 若 gtBlockType 不是 Casing 也不是 Machine,则改用 Block.getBlockFromItem(itemStack.getItem()) 与 itemStack.getItemDamage() 作为判定对象。源码注释说明理由:方块可能在"伪装"自己是什么,此时应按它声称的身份判定采集等级。
    • GT 的 Casing 与 Machine 相反,保留实际方块,因为所需的挖掘等级由真实方块决定。
  6. 若 gtBlockType == Ore 则 capMeta = false——GT 矿石的 metadata 会超过 16,不能按原版上限截断。
  7. 读 harvestability.minimal(读取点显式传默认值 false)。
  8. 调 getHarvestability(...) 填 stringParts;非空时,最小模式用 RESET + MINIMAL_SEPARATOR_STRING 拼接成单行,否则逐行 addAll。

getHarvestability 判定顺序

  1. 读 8 个布尔量(3 个主开关 + 3 个潜行开关 + unharvestableonly + oresonly + toolrequiredonly)。潜行类开关的判定式统一为 开关 && (!潜行开关 || player.isSneaking())。
  2. 总门控:三个主开关全为 false → 直接返回。
  3. 矿石过滤:oresonly && !OreHelper.isBlockAnOre(block, meta) → 返回。
  4. 不可破坏/冒险模式:若 !player.isCurrentToolAdventureModeExempt(x,y,z) 或 BlockHelper.isBlockUnbreakable(...) → 输出一行"暗红 ✘ + harvestable 后缀"并返回。
  5. metadata 截断:if (meta >= 16 && capMeta) meta = 0;。源码注释说明是为了避免生物刷怪笼等 meta ≥ 16 的方块触发数组越界——getHarvestLevel/getHarvestTool 数组只有 16 个元素。
  6. 取 harvestLevel = block.getHarvestLevel(meta)、effectiveTool = BlockHelper.getEffectiveToolOf(...)。若 effectiveTool != null && harvestLevel < 0 则把 harvestLevel 抬为 0;blockHasEffectiveTools = harvestLevel >= 0 && effectiveTool != null。
  7. 预先算好 shearability 与 silkTouchability 两个字符串。
  8. toolrequiredonly 过滤:材质 isToolNotRequired 且 !blockHasEffectiveTools 且两个字符串都为空 → 返回。
  9. 手持物分析(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))
  10. isCurrentlyHarvestable = (canHarvest && isAboveMinHarvestLevel) || (!isHoldingTinkersTool && ForgeHooks.canHarvestBlock(block, player, meta))
  11. unharvestableonly:hideWhileHarvestable && isCurrentlyHarvestable → 返回。
  12. 按顺序输出最多 3 行。

输出行构成

第 1 行:状态 + 剪刀 + 精准

[颜色]✔ 或 ✘[文字后缀]  [颜色]✓  [颜色]✂
  • 三个字段中至少有一个非空才输出这一行。
  • 字段间的间隔是硬编码的单个空格 " "(separator = (!shearability.isEmpty() || !silkTouchability.isEmpty() ? " " : "")),只在前两者之后加。
  • 最小模式下 ✔/✘ 不带文字后缀;✓ 与 ✂ 任何模式下都没有后缀。

第 2 行:所需工具

  • 条件:harvestLevel != -1 && showEffectiveTool && effectiveTool != null。
  • 非最小模式前缀 wailaharvestability.effectivetool(en_US Effective 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。

相关条目