Mixin 与网络

基本信息

属性 值
注册真源 mixins/Mixins.java(枚举 Mixins implements IMixins)
Mixin 组数 2
Mixin 类数 8(7 early + 1 late)
清单文件 mixins.infohud.json / .early.json / .late.json
加载器 mixins/EarlyMixinsLoader.java、mixins/LateMixinsLoader.java
网络 不用 SimpleNetworkWrapper,走 C17 自定义载荷 + OSL 握手
网络通道名 InfoHUD.NETWORK_MODID + "HUD" = IHHUD

2 个 Mixin 组

mixins/Mixins.java:10-17:

MINECRAFT_LATE(new MixinBuilder("Minecraft Late").addClientMixins("ArmorBarRenderMixin")
    .setPhase(Phase.LATE)),

MINECRAFT_EARLY(new MixinBuilder("Minecraft Early")
    .addClientMixins(
        "InventoryEffectRendererMixin",
        "NetHandlerPlayerClientMixin",
        "GuiIngameMixin",
        "MinecraftAccessor")
    .addServerMixins("MinecraftServerMixin", "NetHandlerPlayServerMixin", "NetHandlerPlayServerDisconnectMixin")
    .setPhase(Phase.EARLY));
组 阶段 客户端 服务端 小计
MINECRAFT_EARLY EARLY 4 3 7
MINECRAFT_LATE LATE 1 0 1

两个组都没有 addRequiredMod / setApplyIf——全部无条件应用。

8 个 Mixin 逐条

Mixin 目标 侧 阶段 注入 行
GuiIngameMixin GuiIngameForge 客户端 EARLY renderHUDText(@Inject) 36、46-47
InventoryEffectRendererMixin InventoryEffectRenderer 客户端 EARLY func_147044_g(@Inject HEAD,cancellable) 15-21
MinecraftAccessor Minecraft 客户端 EARLY @Accessor(非注入) —
NetHandlerPlayerClientMixin NetHandlerPlayClient 客户端 EARLY 3 个 @Inject 30、36、47
MinecraftServerMixin MinecraftServer 服务端 EARLY tick(@Inject TAIL) 12、15
NetHandlerPlayServerMixin NetHandlerPlayServer 服务端 EARLY processVanilla250Packet(@Inject HEAD,cancellable) 17、20
NetHandlerPlayServerDisconnectMixin NetHandlerPlayServer 服务端 EARLY onDisconnect(@Inject HEAD) 13、16
ArmorBarRenderMixin ⚠️ locusway.overloadedarmorbar.overlay.ArmorBarRenderer 客户端 LATE onRenderGameOverlayEvent(@Inject HEAD,cancellable,remap = false) 15-23

GuiIngameMixin —— HUD 的唯一渲染入口

@Mixin(value = GuiIngameForge.class)

(mixins/early/GuiIngameMixin.java:36)

目标不是原版 GuiIngame,而是 Forge 改过的 GuiIngameForge—— 这是 GTNH/Forge 环境下画 HUD 的正确入口。

它在 renderHUDText 注入(GuiIngameMixin.java:46-47), 并在 244 行处理 Backhand 副手护甲:

if (Mods.BACKHAND.isLoaded() && BackhandHandler.getBackHandItemStack(mc.thePlayer) != null) {

MinecraftAccessor —— 读 FPS 的唯一手段

Minecraft 没有公开的 FPS 字段,本 mod 用 Mixin @Accessor 注入 getter, 供 InfoFPS 使用:

return tr("info_fps", getPlayer().getDisplayName(), ((MinecraftAccessor) mc).getFps());

(hud/core/infolines/InfoFPS.java:15)

这是 8 个 Mixin 里唯一一个纯 accessor 工具类(无 @Inject)。

NetHandlerPlayerClientMixin —— 3 个注入点

注入方法 行 用途
handleJoinGame(TAIL) 30 进服后握手
handlePlayerListItem(HEAD) 36 玩家列表变化(更新 ping)
handleCustomPayload(HEAD) 47 接收服务端数据

另有一个 @Shadow(NetHandlerPlayerClientMixin.java:26)。

⚠️ 严重缺陷:ArmorBarRenderMixin 缺 addRequiredMod

@Mixin(value = ArmorBarRenderer.class, remap = false)
public class ArmorBarRenderMixin {
    @Inject(method = "onRenderGameOverlayEvent", at = @At("HEAD"), cancellable = true, remap = false)
    private void cancelArmorBarRender(RenderGameOverlayEvent.Pre event, CallbackInfo ci) {
        if (ClassicBarConfig.ClassicBarEnable) {
            ci.cancel();
        }
    }
}

(mixins/late/ArmorBarRenderMixin.java:15-23, import locusway.overloadedarmorbar.overlay.ArmorBarRenderer;,12)

# 事实 证据
1 目标是外部 mod Overloaded Armor Bar 的类,不是 Minecraft 的类 ArmorBarRenderMixin.java:12、15
2 该依赖是 compileOnly,即运行时可选 dependencies.gradle:compileOnly("com.github.GTNewHorizons:OverloadedArmorBar:1.1.1:dev")
3 没有 addRequiredMod 门控 Mixins.java:10-11 的 MINECRAFT_LATE 只有 addClientMixins + setPhase
4 清单 "required": true src/main/resources/mixins.infohud.late.json
5 该依赖不在 Mods 枚举里 Mods.java:9-16 只有 6 个常量,无 Overloaded Armor Bar
6 也不在 @Mod(dependencies) InfoHUD.java:13-18 无 dependencies 字段

后果:未安装 Overloaded Armor Bar 时,目标类不存在, required: true 的 late 清单在应用该 Mixin 时会失败 (GTNH Mixins 通常直接抛异常)⇒ 启动即崩。

📌 组名与实际目标不符:MINECRAFT_LATE / "Minecraft Late" 这个名字暗示它打的是 Minecraft 类,实际打的是第三方 mod。 对比 MINECRAFT_EARLY 的 7 个确实都是 Minecraft / Forge 类。

本 mod 自己的护甲条不依赖它:ClassicBarRenderEvent 用 classicbar/core/PlayerStats.java 读护甲(getArmor()), 与 Overloaded Armor Bar 无关。这个 Mixin 的唯一作用是 开着经典条时把 Overloaded Armor Bar 的护甲条关掉(避免两条重叠)。

网络:C17 载荷 + OSL 握手

本 mod 不用 SimpleNetworkWrapper,而是借 OSL(Open Source Launcher / 西服客户端约定) 的自定义载荷协议。

通道名

public static final String NETWORK_MODID = "IH";
public static final String CHANNEL = InfoHUD.NETWORK_MODID + "HUD";

(InfoHUD.java:22、hud/network/OSLHandshakePayload.java:16)

⇒ 通道名 = "IH" + "HUD" = "IHHUD"。

3 个子通道

channels.add(CHANNEL + "|TPS");
channels.add(CHANNEL + "|Mem");
channels.add(CHANNEL + "|Seed");

(OSLHandshakePayload.java:23-25)

子通道 承载数据 对应 HUD 行
**`IHHUD TPS`** TPS + MSPT
**`IHHUD Mem`** 服务端内存
**`IHHUD Seed`** 世界种子

|Seed 服务于史莱姆区块判定—— HudUtils.isSlimeChunk(long seed, ...)(HudUtils.java:81)用种子做 RANDOM.setSeed(seed ^ ...) 复现原版史莱姆判定(HudUtils.java:84-88)。

握手的实现

public static C17PacketCustomPayload getHandshake() {
    PacketBuffer buffer = new PacketBuffer(Unpooled.buffer());
    List<String> channels = new ArrayList<>();
    channels.add(CHANNEL + "|TPS");
    channels.add(CHANNEL + "|Mem");
    channels.add(CHANNEL + "|Seed");
    for (String channel : channels) {
        try {
            buffer.writeStringToBuffer(channel);
        } catch (IOException ignored) {}
    }
    return new C17PacketCustomPayload("OSL|Handshake", buffer);
}

(OSLHandshakePayload.java:18-34)

载荷通道名是 "OSL|Handshake"(33)——这是 OSL 协议的约定载荷名, 不是 InfoHUD 自己的通道名。

服务端侧读取

hud/network/ServerPacketHandler.java:14:

public static List<String> readHandshakeChannels(C17PacketCustomPayload packet)

NetHandlerPlayServerMixin 在 processVanilla250Packet 的 HEAD (NetHandlerPlayServerMixin.java:20,cancellable = true)拦截自定义载荷, 识别 "OSL|Handshake" 后取出通道列表。

数据落点

客户端侧统一进 hud/core/DataStorage.java: DataStorage.tps、DataStorage.mspt、DataStorage.serverMemUsed / serverMemMax、DataStorage.getPlayerPing(uuid)。 详见 HUD 信息行。

事件处理器

类 位置 作用
TickListener hud/event/TickListener.java FMLCommonHandler.bus()(Hud.java:40-42)
JoinWorldEvent hud/event/JoinWorldEvent.java 同时注册到两个总线(Hud.java:44、47)
BloodMagicEvent hud/event/BloodMagicEvent.java 仅 AWWayofTime 加载时(Hud.java:49-51)
DelayedGuiDisplayTicker hud/event/DelayedGuiDisplayTicker.java 延迟打开配置 GUI(/hud 用)
OverlayEventHandler lightoverlay/OverlayEventHandler.java 光照覆盖层的渲染事件
VanillaRenderEvent classicbar/event/VanillaRenderEvent.java 取消原版 HUD
ClassicBarRenderEvent classicbar/event/ClassicBarRenderEvent.java 绘制经典条

关联