变形(Resizing)缩放的同步与相机修复

基本信息

这是本 mod 最大的一组补丁:5 个 Mixin + 1 个自有数据包 + 2 个事件处理器, 专门修 Witchery 的 PotionResizing 药水(缩小体型)在客户端的表现。

Witchery 的 Resizing 药水由 com.emoniph.witchery.brewing.potions.PotionResizing 实现, Witchery 不在本 wiki 收录范围(见 分类页),本页只记本 mod 的补丁。

Mixin 组 目标 阶段 作用
FIX_POSITON_RESIZING_EYE_HEIGHT witchery.PotionResizingMixin LATE 缩放时眼高定位
WITCHERY_OFFSET_CAPTURE witchery.PlayerRenderMixin(仅客户端) LATE 跳过原版视角偏移逻辑
FIX_SHAPESHIFT_CAMERA minecraft.EntityRendererMixin(仅客户端) EARLY 相机偏移
FIX_SHAPESHIFT_SYNC minecraft.NetHandlerPlayClientMixin(仅客户端) EARLY 同步位置偏移
FIX_SHAPESHIFT_STANCE_VALIDATION minecraft.NetHandlerPlayServerMixin EARLY 站姿上限校验
FIX_IS_ENTITY_INSIDE_OPAQUE_BLOCK minecraft.EntityMixin EARLY 窒息判定

组定义见 mixins/Mixins.java:13-30;文件见 mixins/early/minecraft/ 与 mixins/late/witchery/。

Y 偏移的计算(本 mod 自己的公式)

mixins/hooks/EntitySizeManager.java:26-37:

public static float getTargetYOffset(EntityPlayer player) {
    EntitySizeInfo size = new EntitySizeInfo(player);
    PotionEffect shrunk = Witchery.Potions.RESIZING != null
            ? player.getActivePotionEffect(Witchery.Potions.RESIZING)
            : null;
    if (shrunk == null && size.isDefault) {
        return 0;
    }
    float amp = shrunk != null ? PotionResizing.getScaleFactor(shrunk.getAmplifier()) : 1.0F;
    float scale = size.defaultHeight / 1.8F * amp;
    return 1.8F * (1.0F - scale);
}
步骤 含义
size.isDefault && shrunk == null 未变形且无缩放药水 → 偏移 0
amp 缩放因子,来自 PotionResizing.getScaleFactor(放大等级);无药水时 1.0F
scale 目标身高 / 1.8F(标准玩家身高)× amp
返回值 1.8F × (1 − scale)

未在源码中调用 setter 的数值一律不要写进 wiki——defaultHeight 来自 Witchery 的 EntitySizeInfo,本 mod 读而不设。

扩展属性与存档

OffsetContents implements IExtendedEntityProperties (EntitySizeManager.java:39-74):

  • 属性键:OFFSET_PROPERTY = "ShapeShiftOffset"(EntitySizeManager.java:39)
  • 字段:currentOffset / targetOffset,均以 float 存 NBT (键名同为 currentOffset / targetOffset,EntitySizeManager.java:47-56)
  • init(Entity, World) 是空实现(EntitySizeManager.java:59)

3 个事件处理器

全部在 EntitySizeManager 内(EntitySizeManager.java:76-101):

事件 行 行为
EntityEvent.EntityConstructing 76-81 玩家实体构造时 registerExtendedProperties("ShapeShiftOffset", new OffsetContents())
EntityJoinWorldEvent 83-90 服务端且为 EntityPlayerMP 时:发本 mod 的 EntitySizeSyncPacket(offset, true),并连发两次 Witchery.packetPipeline.sendToAll(new PacketSyncEntitySize(player))
PlayerEvent.Clone 92-101 event.wasDeath 为 true 时直接返回(死亡不继承);否则把 currentOffset / targetOffset 从原实体复制过来

外加 handlers/ServerSizeHandler.java(TickEvent.PlayerTickEvent,PHASE 非 END 时 return) 与 handlers/ClientSizeHandler.java:在 contents.targetOffset != getTargetYOffset(player) 时更新并 NETWORK.sendTo(new EntitySizeSyncPacket(tmp), player)。 ClientSizeHandler 在 proxy/ClientProxy.java 的 init 中注册。

网络

  • 通道:NetworkRegistry.INSTANCE.newSimpleChannel(MODID),MODID = "WitcheryExtras" (core/WitcheryExtras.java:43)
  • 自有包:1 个 —— network/EntitySizeSyncPacket.java (构造重载:(float) 与 (float, boolean))

📌 onEntityJoinWorld 里 PacketSyncEntitySize 被 sendToAll 连续发送两次 (EntitySizeManager.java:87-88,两行完全相同,仅第二行多传了 player)。 这是源码原样,未做合并。

EARLY 阶段 4 个补丁的注入点

Mixin 注入 改动的常量 / 目标
EntityMixin.java:14 @ModifyExpressionValue on isEntityInsideOpaqueBlock CONSTANT floatValue=0.1F
EntityRendererMixin.java:20 @ModifyVariable on orientCamera,@At("STORE", ordinal = 0),ordinal = 1 第 2 个被存入的局部变量
NetHandlerPlayClientMixin.java:16-20 @ModifyExpressionValue on handlePlayerPosLook INVOKE S08PacketPlayerPosLook.func_148928_d()D
NetHandlerPlayServerMixin.java:20 @ModifyExpressionValue on processPlayer CONSTANT doubleValue=1.65D

PotionResizingMixin.java:15 走 MixinExtras 专有注入点: @ModifyExpressionValue(method = "onLivingUpdate", at = @At("MIXINEXTRAS:EXPRESSION")) ——不指定目标,靠 MixinExtras 的表达式匹配生效,比常规 @At 锚点更脆弱。 这也是为什么 mixins.WitcheryExtras.late.json 声明了 "mixinextras": {"minVersion": "0.5.0"}。

PlayerRenderMixin.java:13-15 在 onRenderTick 里对 @At(value = "NEW", target = "com/emoniph/witchery/util/EntitySizeInfo") 注入。

关联