diff --git a/src/main/java/com/replaymod/core/versions/MCVer.java b/src/main/java/com/replaymod/core/versions/MCVer.java index 80c165f5..d62db95d 100644 --- a/src/main/java/com/replaymod/core/versions/MCVer.java +++ b/src/main/java/com/replaymod/core/versions/MCVer.java @@ -394,6 +394,14 @@ public class MCVer { //#endif } + public static void scheduleOnMainThread(Runnable runnable) { + //#if MC>=11400 + getMinecraft().send(runnable); + //#else + //$$ getMinecraft().addScheduledTask(runnable); + //#endif + } + //#if MC>=11400 public static Window getWindow(MinecraftClient mc) { //#if MC>=11500 diff --git a/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java b/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java index 997bdb11..497a18ec 100644 --- a/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java +++ b/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java @@ -57,6 +57,7 @@ public abstract class MixinNetHandlerPlayClient { //$$ @Inject(method = "handlePlayerListItem", at=@At("HEAD")) //#endif public void recordOwnJoin(PlayerListS2CPacket packet, CallbackInfo ci) { + if (!MCVer.isOnMainThread()) return; if (mcStatic.player == null) return; RecordingEventHandler handler = getRecordingEventHandler(); diff --git a/src/main/java/com/replaymod/recording/packet/PacketListener.java b/src/main/java/com/replaymod/recording/packet/PacketListener.java index 2fb03ed8..9bcd7d17 100644 --- a/src/main/java/com/replaymod/recording/packet/PacketListener.java +++ b/src/main/java/com/replaymod/recording/packet/PacketListener.java @@ -149,6 +149,14 @@ public class PacketListener extends ChannelInboundHandlerAdapter { } public void save(Packet packet) { + // If we're not on the main thread (i.e. we're on the netty thread), then we need to schedule the saving + // to happen on the main thread so we can guarantee correct ordering of inbound and inject packets. + // Otherwise, injected packets may end up further down the packet stream than they were supposed to and other + // inbound packets which may rely on the injected packet would behave incorrectly when played back. + if (!MCVer.isOnMainThread()) { + MCVer.scheduleOnMainThread(() -> save(packet)); + return; + } try { if(packet instanceof PlayerSpawnS2CPacket) { //#if MC>=10800