You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Minecraft 1.16.5 Fabric模组温度同步报IllegalReferenceCountException问题

问题:Fabric模组温度同步触发Netty IllegalReferenceCountException异常

我正在为Minecraft 1.16.5开发模组,实现温度系统,使用Fabric Networking API同步两个float类型的温度数据(环境温度ambient、生物群系温度biome)。核心数据类代码如下:

// Temperature.java
private float ambient;
private float biome;

public PacketByteBuf createBuffer() {
    PacketByteBuf buf = PacketByteBufs.create();
    buf.writeFloat(ambient);
    buf.writeFloat(biome);
    return buf;
}

public void loadBuffer(PacketByteBuf buf) {
    this.ambient = buf.readFloat();
    this.biome = buf.readFloat();
}

public void copyFieldsTo(Temperature that) {
    Objects.requireNonNull(that, "that");
    that.ambient = this.ambient;
    that.biome = this.biome;
}

目前遇到的问题:在loadBuffer方法执行时,Netty持续抛出io.netty.util.IllegalReferenceCountException: refCnt: 0异常,数据包能被客户端接收,但数据无法完成同步。数据包发送与接收的代码如下:

// In a mixin to ServerPlayerEntity
@Unique
private Temperature rmc$temperature;
@Unique
private Temperature rmc$syncedTemperature;
@Inject(method = "playerTick", at = @At(value = "INVOKE",
            target = "Lnet/minecraft/server/network/ServerPlayerEntity;getHealth()F",
            shift = At.Shift.BEFORE, ordinal = 0))
private void syncRMCData(CallbackInfo ci) {
    TemperatureUtil.update(rmc$temperature, self);//Update temperature data
    if (!rmc$temperature.equals(rmc$syncedTemperature)) {
         ServerPlayNetworking.send(self, TemperatureUtil.PACKET_ID, rmc$temperature.createBuffer());
         rmc$temperature.copyFieldsTo(rmc$syncedTemperature);
    }
}
// In client mod initializer
ClientPlayNetworking.registerGlobalReceiver(TemperatureUtil.PACKET_ID,
        (client, handler, buf, responseSender) -> client.execute(
                () -> ((TemperatureAccess) client.player).getTemperature()
                        .loadBuffer(buf)));
// TemperatureAccess is an interface implemented by a mixin to ClientPlayerEntity

异常堆栈跟踪信息:

[13:22:34] [main/FATAL] (Minecraft) Error executing task on Client
io.netty.util.IllegalReferenceCountException: refCnt: 0
    at io.netty.buffer.AbstractByteBuf.ensureAccessible(AbstractByteBuf.java:1417) ~[netty-all-4.1.25.Final.jar:4.1.25.Final]
    at io.netty.buffer.AbstractByteBuf.checkReadableBytes0(AbstractByteBuf.java:1403) ~[netty-all-4.1.25.Final.jar:4.1.25.Final]
    at io.netty.buffer.AbstractByteBuf.readByte(AbstractByteBuf.java:707) ~[netty-all-4.1.25.Final.jar:4.1.25.Final]
    at net.minecraft.network.PacketByteBuf.readByte(PacketByteBuf.java:1260) ~[minecraft-1.16.5-mapped-net.fabricmc.yarn-1.16.5+build.10-v2.jar:?]
    at net.minecraft.network.PacketByteBuf.readVarInt(PacketByteBuf.java:483) ~[minecraft-1.16.5-mapped-net.fabricmc.yarn-1.16.5+build.10-v2.jar:?]
    at io.github.asablock.rmc.temperature.Temperature.loadBuffer(Temperature.java:50) ~[main/:?]
    at io.github.asablock.rmc.client.ClientReasonableMCMod.lambda$onInitializeClient$2(ClientReasonableMCMod.java:57) ~[main/:?]
    at net.minecraft.util.thread.ThreadExecutor.executeTask(ThreadExecutor.java:136) [minecraft-1.16.5-mapped-net.fabricmc.yarn-1.16.5+build.10-v2.jar:?]
    at net.minecraft.util.thread.ReentrantThreadExecutor.executeTask(ReentrantThreadExecutor.java:22) [minecraft-1.16.5-mapped-net.fabricmc.yarn-1.16.5+build.10-v2.jar:?]
    at net.minecraft.util.thread.ThreadExecutor.runTask(ThreadExecutor.java:109) [minecraft-1.16.5-mapped-net.fabricmc.yarn-1.16.5+build.10-v2.jar:?]
    at net.minecraft.util.thread.ThreadExecutor.runTasks(ThreadExecutor.java:97) [minecraft-1.16.5-mapped-net.fabricmc.yarn-1.16.5+build.10-v2.jar:?]
    at net.minecraft.client.MinecraftClient.render(MinecraftClient.java:1047) [minecraft-1.16.5-mapped-net.fabricmc.yarn-1.16.5+build.10-v2.jar:?]
    at net.minecraft.client.MinecraftClient.run(MinecraftClient.java:685) [minecraft-1.16.5-mapped-net.fabricmc.yarn-1.16.5+build.10-v2.jar:?]
    at net.minecraft.client.main.Main.main(Main.java:187) [minecraft-1.16.5-mapped-net.fabricmc.yarn-1.16.5+build.10-v2.jar:?]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:?]
    at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:78) ~[?:?]
    at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[?:?]
    at java.lang.reflect.Method.invoke(Method.java:567) ~[?:?]
    at net.fabricmc.loader.game.MinecraftGameProvider.launch(MinecraftGameProvider.java:234) [fabric-loader-0.11.6.jar:?]
    at net.fabricmc.loader.launch.knot.Knot.launch(Knot.java:153) [fabric-loader-0.11.6.jar:?]
    at net.fabricmc.loader.launch.knot.KnotClient.main(KnotClient.java:28) [fabric-loader-0.11.6.jar:?]
    at net.fabricmc.devlaunchinjector.Main.main(Main.java:86) [dev-launch-injector-0.2.1+build.8.jar:?]

原因分析

  • Fabric Networking API的回调方法执行完毕后,Netty会自动释放PacketByteBuf的引用计数(将refCnt设为0)
  • 你将loadBuffer(buf)通过client.execute()延迟到主线程执行,此时原Buf已经被Netty回收,再次读取就会触发引用计数异常

解决方案

方案1:提前读取数据(推荐)

在网络回调线程中先读取Buf中的温度数据,再将数据传递到主线程更新,避免直接操作已释放的Buf:

// 修改后的客户端接收代码
ClientPlayNetworking.registerGlobalReceiver(TemperatureUtil.PACKET_ID,
        (client, handler, buf, responseSender) -> {
            // 在网络线程读取数据,此时Buf仍有效
            float ambientTemp = buf.readFloat();
            float biomeTemp = buf.readFloat();
            // 将数据传到主线程更新温度对象
            client.execute(() -> {
                Temperature temp = ((TemperatureAccess) client.player).getTemperature();
                temp.ambient = ambientTemp;
                temp.biome = biomeTemp;
            });
        });

方案2:复制Buf并手动管理引用

如果要保留loadBuffer方法,可以复制原Buf,在主线程使用后手动释放:

// 修改后的客户端接收代码
ClientPlayNetworking.registerGlobalReceiver(TemperatureUtil.PACKET_ID,
        (client, handler, buf, responseSender) -> {
            // 复制Buf,生成独立的引用计数实例
            PacketByteBuf copiedBuf = buf.copy();
            client.execute(() -> {
                try {
                    ((TemperatureAccess) client.player).getTemperature().loadBuffer(copiedBuf);
                } finally {
                    // 手动释放复制的Buf,避免内存泄漏
                    copiedBuf.release();
                }
            });
        });

额外优化

检查Temperature类的equals()方法是否正确实现,确保只有当温度数据真的变化时才发送数据包,减少不必要的网络传输和潜在的异常触发概率。

内容的提问来源于stack exchange,提问作者asablock

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 09:35:15