From 1dab50ca003abc59e4115a329bcb46f76c105e15 Mon Sep 17 00:00:00 2001 From: LexManos Date: Mon, 10 Aug 2026 21:56:47 -0700 Subject: [PATCH] Fix mod packets being processed after vanilla packets when received in the same tick. --- .../net/minecraftforge/common/ForgeHooks.java | 31 +++++++++++++++++++ .../common/util/LogicalSidedProvider.java | 2 ++ .../network/simple/SimpleFlow.java | 2 +- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/main/java/net/minecraftforge/common/ForgeHooks.java b/src/main/java/net/minecraftforge/common/ForgeHooks.java index e44f8c4a8c..0a988ce2c3 100644 --- a/src/main/java/net/minecraftforge/common/ForgeHooks.java +++ b/src/main/java/net/minecraftforge/common/ForgeHooks.java @@ -15,6 +15,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.function.BiConsumer; import java.util.function.Function; import java.util.function.Predicate; import java.util.regex.Matcher; @@ -54,6 +55,7 @@ import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.registries.Registries; import net.minecraft.network.Connection; import net.minecraft.network.FriendlyByteBuf; +import net.minecraft.network.PacketListener; import net.minecraft.network.RegistryFriendlyByteBuf; import net.minecraft.network.VarInt; import net.minecraft.network.chat.ClickEvent; @@ -107,6 +109,7 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.ListTag; import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.PacketFlow; +import net.minecraft.network.protocol.PacketType; import net.minecraft.network.protocol.common.custom.CustomPacketPayload; import net.minecraft.network.protocol.common.custom.DiscardedPayload; import net.minecraft.network.syncher.EntityDataSerializer; @@ -1305,4 +1308,32 @@ public final class ForgeHooks { return Optional.of(ret); } + + // This is similar to PacketUtils.ensureRunningOnSameThread, we can't use the normal LogicalSidedProvider.WORKQUEUE because it is processed after packets. + // So any vanilla packets that are received after this packet, will be processed before our enqueued packet. + public static void enqueuePacket(BiConsumer handler, MSG packet, CustomPayloadEvent.Context context) { + var processor = LogicalSidedProvider.PACKETS.get(context.isClientSide()); + if (!processor.isSameThread()) + processor.scheduleIfPossible(context.getConnection().getPacketListener(), new DummyPacket<>(handler, packet, context)); + else + handler.accept(packet, context); + } + + private static final record DummyPacket( + BiConsumer handler, + MSG packet, + CustomPayloadEvent.Context context + ) implements Packet { + private static final PacketType> TYPE = new PacketType<>(PacketFlow.CLIENTBOUND, Identifier.fromNamespaceAndPath("forge", "dummy_for_schedualing")); + + @Override + public PacketType> type() { + return TYPE; + } + + @Override + public void handle(PacketListener listener) { + handler.accept(packet, context); + } + } } \ No newline at end of file diff --git a/src/main/java/net/minecraftforge/common/util/LogicalSidedProvider.java b/src/main/java/net/minecraftforge/common/util/LogicalSidedProvider.java index 8020174ac0..02a725ac34 100644 --- a/src/main/java/net/minecraftforge/common/util/LogicalSidedProvider.java +++ b/src/main/java/net/minecraftforge/common/util/LogicalSidedProvider.java @@ -6,6 +6,7 @@ package net.minecraftforge.common.util; import net.minecraft.client.Minecraft; +import net.minecraft.network.PacketProcessor; import net.minecraft.server.MinecraftServer; import net.minecraft.server.TickTask; import net.minecraft.util.thread.BlockableEventLoop; @@ -21,6 +22,7 @@ import org.jetbrains.annotations.ApiStatus; public class LogicalSidedProvider { public static final LogicalSidedProvider> WORKQUEUE = new LogicalSidedProvider<>(Supplier::get, Supplier::get); public static final LogicalSidedProvider> CLIENTWORLD = new LogicalSidedProvider<>((c)-> Optional.of(c.get().level), (s)->Optional.empty()); + public static final LogicalSidedProvider PACKETS = new LogicalSidedProvider<>(client -> client.get().packetProcessor(), server -> server.get().packetProcessor()); public T get(LogicalSide side) { return side == LogicalSide.CLIENT ? clientSide.apply(client) : serverSide.apply(server); diff --git a/src/main/java/net/minecraftforge/network/simple/SimpleFlow.java b/src/main/java/net/minecraftforge/network/simple/SimpleFlow.java index bd9f3a5b1e..c1906d1633 100644 --- a/src/main/java/net/minecraftforge/network/simple/SimpleFlow.java +++ b/src/main/java/net/minecraftforge/network/simple/SimpleFlow.java @@ -28,7 +28,7 @@ public interface SimpleFlow extends SimplePro */ default SimpleFlow addMain(Class type, StreamCodec codec, BiConsumer handler) { return add(type, codec, (msg, ctx) -> { - ctx.enqueueWork(() -> handler.accept(msg, ctx)); + net.minecraftforge.common.ForgeHooks.enqueuePacket(handler, msg, ctx); ctx.setPacketHandled(true); }); }