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 |
绘制经典条 |