事件处理器

PlayerEventHandler(core/event/PlayerEventHandler.java,149 行)是本 mod 唯一不依赖 mixin 的功能入口,通过 MinecraftForge.EVENT_BUS.register(new PlayerEventHandler()) 注册于 ProxyCommon.init(core/proxy/ProxyCommon.java:56)。它包含 3 个 @SubscribeEvent 处理器,其中两处实现了未在物品描述或研究中体现的隐藏机制。

1. onPlayerInteract(PlayerInteractEvent)

:37-81,三段互不相干的逻辑:

1a. 徒手拆隐形方块

未戴 IRevealer 护目镜时右键 Metaphysical Brick / Metaphysical Rose:

if (event.action == RIGHT_CLICK_BLOCK && !(BlockMetaphysical.hasGoggles(player))) {
    Vec3 vec = BlockHelper.getCentralCoords(event.x, event.y, event.z, event.face);
    if (blockAt(vec) instanceof BlockMetaphysical && player.getHeldItem() != null) {
        worldObj.setBlock(..., Blocks.air);          // 直接删方块
        player.setItemInUse(heldItem, heldItem.getMaxItemUseDuration());
    }
}

这是绕过「戴眼镜才可见」限制的后门:准隐形方块可以被直接右键删除,且无任何物品掉落。

1b. 深红研究解锁(有意设计)

右键时手持 ConfigItems.itemEldritchObject 且 getItemDamage() == 1,若玩家未完成 @CRIMSON_PHILOSOPHY:

PacketHandler.INSTANCE.sendTo(new PacketResearchComplete(rname), (EntityPlayerMP) player);
Thaumcraft.proxy.getResearchManager().completeResearch(player, rname);

这不是后门。CRIMSON_PHILOSOPHY 在 ResearchHelper.loadCrimsonResearch() 中注册时带 setSpecial() + setStub() + setHidden()(research/ResearchHelper.java:263-273),即刻意做成不可见、不可手动完成的事件型研究,本处理器就是它唯一的完成途径。详见 研究树。

1c. 法杖右键 Flux 预扫描

RIGHT_CLICK_BLOCK 且手持 ItemWandCasting 时,扫描点击点周围 5×5×5 的 BlockFluxGas / BlockFluxGoo,命中即 player.setItemInUse(stack, Integer.MAX_VALUE)。这是 Taint Capping 的触发前提 —— 让 useItem 状态持续足够长,PlayerUseItemEvent.Tick 才会反复执行。

2. onUseItem(PlayerUseItemEvent.Tick)

:82-124。Taint Capping 的全部实现,详见 Taint Capping。5×5×5 扫描,随机要素注入(0–99),约 2/50 概率清除 Flux 方块。

3. onAttackEntity(AttackEntityEvent)—— 隐藏的 Vis 近战

:127-148。未被任何 Augmentation NBT 触发,纯靠法杖自身的 Vis 存量判定:

if (!(event.target instanceof EntityLivingBase target)) return;
if (stack == null || !(stack.getItem() instanceof ItemWandCasting wand)) return;
int aspectCount = 0;
for (Aspect aspect : wand.getAllVis(stack))
    if (aspectList.getAmount(aspect) > 15) ++aspectCount;   // 存量 > 15 才计数
if (aspectCount >= 6) {
    target.attackEntityFrom(DamageSource.magic, 4.0f);
    for (Aspect aspect : aspectList.getAspects())
        wand.consumeVis(stack, player, aspect, 15, false);
    worldObj.playSoundAtEntity(player, "thaumcraft:zap", 1.0f, 1.1f);
}

法杖敲生物:若法杖上有 ≥ 6 个要素各自存量 > 15,每次攻击造成 4 点 DamageSource.magic 伤害,并从每一个要素扣 15 点 Vis,播放 thaumcraft:zap 音效(音调 1.1)。

数值

处理器 关键数值
onPlayerInteract 1a 护目镜判定 = hasGoggles;方块直接置 Blocks.air;无掉落
onPlayerInteract 1b 物品 = itemEldritchObject meta 1;研究 = @CRIMSON_PHILOSOPHY
onPlayerInteract 1c 扫描 5×5×5(i/j/k ∈ [-2,2]);setItemInUse(stack, Integer.MAX_VALUE)
onUseItem 5×5×5;Vis 0–99;Entropy 约 21%;无效约 11%;清方块约 2/50
onAttackEntity 要素阈值 6;单要素存量阈值 > 15;伤害 4.0F(DamageSource.magic);每要素扣 15;音效 thaumcraft:zap 1.0f / 1.1f

源码缺陷

  • onAttackEntity 在客户端也会执行伤害计算。AttackEntityEvent 双端触发,但代码只判 event.target instanceof EntityLivingBase,未做 worldObj.isRemote 检查。客户端会重复调用 consumeVis 与 attackEntityFrom(客户端 attackEntityFrom 通常不生效,但 consumeVis 会改本地 NBT,服务端包随后覆盖)。
  • 1a 的隐形方块后门:准隐形机制(见 Metaphysical Brick)的整个意义是「看不见就摸不着」,但本处理器允许无眼镜直接右键删除,且要求 player.getHeldItem() != null(不能空手删)—— 这是未文档化的绕过路径。
  • 1a 只删方块不给物品:worldObj.setBlock(..., Blocks.air) 不走 dropBlockAsItem,两个 metaphysical 方块无掉落。
  • 1b 缺服务端二次校验:仅 !player.worldObj.isRemote 一道判断,物品 meta 判定在双端都做,客户端会重复构造 PacketResearchComplete。
  • onAttackEntity 的 getAllVis 调用两次(:135 与 :140),每次都新建 AspectList;getAspectsWithRoom 语义未用,可能允许扣成负值。
  • onAttackEntity 与 Contact Discharge 名称高度相关但无代码关联 —— 后者是无实现的空改装,二者易被误认为同一机制。

相关条目