Mixin 加载阶段

基本信息

属性 值
类型 特殊机制(Mixin 挂载与目标判定)
Mixin 清单 com.caedis.freecam.mixins.Mixins(enum,实现 IMixins)
目标 mod 判定 com.caedis.freecam.mixins.TargetedMod(enum,实现 ITargetMod)
早期加载器 core/FreecamCore.java——IFMLLoadingPlugin + IEarlyMixinLoader
后期加载器 mixins/LateMixinsLoader.java——@LateMixin + ILateMixinLoader
coremod 声明 gradle.properties 中 coreModClass = core.FreecamCore
Mixin 基础设施 GTNHLib 风格的 IMixins / MixinBuilder / TargetModBuilder,refmap mixins.freecam-gtnh.refmap.json

三个 Mixin 配置文件

配置文件 声明位置 加载者
mixins.freecam-gtnh.early.json FreecamCore.getMixinConfig() 早期阶段
mixins.freecam-gtnh.late.json LateMixinsLoader.getMixinConfig() 后期阶段
mixins.freecam-gtnh.json — 无任何代码引用(grep "mixins.freecam-gtnh.json" src/main/java 无结果)

三者共同字段:required: true、minVersion: "0.8.3-GTNH"、refmap: "mixins.freecam-gtnh.refmap.json"、 target: "@env(DEFAULT)"、compatibilityLevel: "JAVA_8"。

early / late 两个配置额外带 package(分别是 com.caedis.freecam.mixins.early 与 com.caedis.freecam.mixins.late)与 mixinextras: {minVersion: "0.5.0"}。 只有 early 配置带 "injectors": {"maxShiftBy": 2},允许注入点在 2 字节内做位移调整。

mixins.freecam-gtnh.json 没有 package、也没有 mixins 数组,是 GTNH 构建脚本生成的 基础配置,仅由上面两个配置的 refmap 与 minVersion 约束共享。

早期 vs 后期

维度 早期(EARLY) 后期(LATE)
清单项 Mixins.FREECAM Mixins.WAILA、Mixins.THAUMCRAFT、Mixins.HOLOINVENTORY
加载类 FreecamCore LateMixinsLoader(@LateMixin 注解)
接口 IEarlyMixinLoader ILateMixinLoader
取混合的方法 IMixins.getEarlyMixins(Mixins.class, loadedCoreMods) IMixins.getLateMixins(Mixins.class, loadedMods)
目标 原版 Minecraft 客户端类 第三方 mod 的类
条件 无 每个清单项都 addRequiredMod(TargetedMod.X)
remap 默认(需要 SRG 重映射) 全部 remap = false
目标包 com.caedis.freecam.mixins.early.minecraft com.caedis.freecam.mixins.late.*

阶段划分的可观察依据:早期清单项不带 addRequiredMod 且只针对原版类,任何游戏实例都必须加载; 后期清单项每一项都带 addRequiredMod,且目标类带 remap = false(第三方类名未混淆, 不存在 SRG 映射可重映射)。源码中未直接写出阶段划分的文字理由,上述差异均为从代码读出的事实。

TargetedMod:目标 mod 判定

TargetedMod(TargetedMod.java)三个取值全部把 coreModClass 传成 null, 即只按 modId 判定,不按核心类判定:

枚举值 coreModClass modId
WAILA null Waila
THAUMCRAFT null Thaumcraft
HOLOINVENTORY null holoinventory

⚠️ modId 字面量大小写不统一:Waila、Thaumcraft 用首字母大写,holoinventory 全小写。 这直接写进了 setModId(modId),没有做任何规范化。

注意这套机制与 兼容模组 里的 Mods 枚举是两套彼此独立的第三方 mod 判定方式: Mods 走 Loader.isModLoaded + class 文件存在性探测,用于运行时行为分支; TargetedMod 走 TargetModBuilder 传进 MixinBuilder.addRequiredMod,用于加载期决定是否应用 Mixin。

全部 Mixin 枚举项

枚举项 阶段 目标类(@Mixin) 注入点数量 门控条件
FREECAM EARLY Minecraft、EntityRenderer、EntityLivingBase、MovementInputFromOptions 1×@Redirect + 4×@Inject(Minecraft)、1×@Redirect + 1×@Redirect + 1×@Inject + 1×@ModifyExpressionValue + 1×@Inject(EntityRenderer)、1×@Inject(EntityLivingBase)、1×@Inject(MovementInputFromOptions) 无条件
WAILA LATE mcp.mobius.waila.overlay.OverlayRenderer 1×@ModifyReturnValue TargetedMod.WAILA
THAUMCRAFT LATE thaumcraft.client.lib.RenderEventHandler 4×@Inject TargetedMod.THAUMCRAFT
HOLOINVENTORY LATE net.dries007.holoInventory.client.Renderer 1×@ModifyExpressionValue TargetedMod.HOLOINVENTORY

各 Mixin 具体打的是什么补丁,见下表。

Mixin 逐条枚举

Mixin 类 目标方法 注入器 补丁内容
minecraft.MixinMinecraft Minecraft.runTick @Redirect 字段 PUTFIELD GameSettings.thirdPersonView 已激活时强制写回 0,屏蔽 F5 视角切换
minecraft.MixinMinecraft func_147115_a @Inject HEAD + cancellable 取消(开发者命名为 cancelLeftClick)
minecraft.MixinMinecraft func_147116_af @Inject HEAD + cancellable 取消(cancelMiddleClick)
minecraft.MixinMinecraft func_147121_ag @Inject HEAD + cancellable 取消(cancelRightClick)
minecraft.MixinMinecraft func_147112_ai @Inject HEAD + cancellable 取消(cancelPickBlock)
minecraft.MixinMinecraft @Unique freecam$shouldCancel() 私有辅助方法 isActive() && !isPlayerControlled() 的统一判定
minecraft.MixinEntityRenderer EntityRenderer.updateCameraAndRender @Redirect INVOKE EntityClientPlayerMP.setAngles(FF)V 鼠标 yaw/pitch 改写到 renderViewEntity 而非玩家
minecraft.MixinEntityRenderer EntityRenderer.setupFog @Redirect INVOKE ActiveRenderInfo.getBlockAtEntityViewpoint 配合 禁用浸没雾,水/岩浆返回 Blocks.air
minecraft.MixinEntityRenderer EntityRenderer.getMouseOver @Inject HEAD + cancellable 玩家操控时改用 thePlayer.rayTrace(reach, partialTicks) 覆写 objectMouseOver
minecraft.MixinEntityRenderer EntityRenderer.updateLightmap @ModifyExpressionValue 字段 GETFIELD GameSettings.gammaSetting 配合 全亮度 返回 100.0F
minecraft.MixinEntityRenderer EntityRenderer.renderHand @Inject HEAD + cancellable 配合 显示手部 取消手部渲染
minecraft.MixinEntityLivingBase EntityLivingBase.setHealth @Inject HEAD 配合 受伤时禁用 排程退出
minecraft.MixinMovementInputFromOptions updatePlayerMoveState @Inject RETURN 清零 moveStrafe/moveForward/jump/sneak
waila.MixinOverlayRenderer OverlayRenderer.isOverlayVisible @ModifyReturnValue RETURN original && !RevealGate.shouldHide()
thaumcraft.MixinRenderEventHandler drawTagsOnContainer @Inject HEAD + cancellable 取消护目镜容器标签
thaumcraft.MixinRenderEventHandler drawTextInAir @Inject HEAD + cancellable 取消护目镜悬浮文字
thaumcraft.MixinRenderEventHandler showScannedBlocks @Inject HEAD + cancellable 取消魔能检测器扫描脉冲
thaumcraft.MixinRenderEventHandler renderMarkedBlocks @Inject HEAD + cancellable 取消傀儡放置/铃标记
holoinventory.MixinRenderer Renderer.renderEvent @ModifyExpressionValue 字段 GETFIELD Renderer.enabled:Z original && !RevealGate.shouldHide()

三个后期 Mixin 的目标类全部带 remap = false。holoinventory 那条的源码注释说明了为何用 @ModifyExpressionValue 而不是写字段:enabled 由 KeyManager 拥有并会持久化到配置, 改写读值才不会与用户的开关按键冲突。

访问变换器

META-INF/freecam_at.cfg 只有一行: public net.minecraft.client.audio.SoundManager$SoundSystemStarterThread。 这让 Mixin 补丁后的相机在移动时会正常触发音效线程(相机继承 EntityPlayer, 原版 Entity.stepSound 会调用 playStepSound,SoundSystemStarterThread 需要 public 才能被跨线程启动)。

相关条目