From 6498d625add48ad35c289bf420f9744998da33ad Mon Sep 17 00:00:00 2001 From: johni0702 Date: Sat, 24 Dec 2016 11:22:01 +0100 Subject: [PATCH] Fix server game data snapshot being restored in singleplayer (fixes #44) --- .../recording/handler/FMLHandshakeFilter.java | 25 +++++++++++++++++ .../mixin/MixinNetworkDispatcher.java | 27 ++++++++++++++++--- 2 files changed, 48 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/replaymod/recording/handler/FMLHandshakeFilter.java diff --git a/src/main/java/com/replaymod/recording/handler/FMLHandshakeFilter.java b/src/main/java/com/replaymod/recording/handler/FMLHandshakeFilter.java new file mode 100644 index 00000000..79553b3b --- /dev/null +++ b/src/main/java/com/replaymod/recording/handler/FMLHandshakeFilter.java @@ -0,0 +1,25 @@ +package com.replaymod.recording.handler; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import net.minecraftforge.fml.common.network.handshake.FMLHandshakeMessage; + +/** + * Filters out all handshake packets that were sent for recording but must + * not actually be handled. + * This handler is only present when connected to the integrated server as + * otherwise all packets must be handled. + * + * When in single player, the game state packets must never be handled + * otherwise wired bugs related to semi-singletons can occur. + * See https://bugs.replaymod.com/show_bug.cgi?id=44 + */ +public class FMLHandshakeFilter extends SimpleChannelInboundHandler { + @Override + protected void channelRead0(ChannelHandlerContext ctx, FMLHandshakeMessage msg) throws Exception { + if (!(msg instanceof FMLHandshakeMessage.RegistryData)) { + // Pass on everything but RegistryData messages + ctx.fireChannelRead(msg); + } + } +} diff --git a/src/main/java/com/replaymod/recording/mixin/MixinNetworkDispatcher.java b/src/main/java/com/replaymod/recording/mixin/MixinNetworkDispatcher.java index 9ef0b6da..c6e2e4d5 100644 --- a/src/main/java/com/replaymod/recording/mixin/MixinNetworkDispatcher.java +++ b/src/main/java/com/replaymod/recording/mixin/MixinNetworkDispatcher.java @@ -1,7 +1,11 @@ package com.replaymod.recording.mixin; +import com.replaymod.recording.handler.FMLHandshakeFilter; +import io.netty.channel.ChannelPipeline; import io.netty.channel.embedded.EmbeddedChannel; +import net.minecraftforge.fml.common.network.handshake.FMLHandshakeCodec; import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher; +import net.minecraftforge.fml.relauncher.Side; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -11,17 +15,32 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(value = NetworkDispatcher.class, remap = false) public abstract class MixinNetworkDispatcher { + @Shadow + private Side side; + @Shadow private EmbeddedChannel handshakeChannel; /** - * Always sets fml:isLocal to false. + * Always sets fml:isLocal to false on the server side. * This effectively removes the difference in the FML handshake between SP and MP * and forces the block/item ids, etc. to always be send. - * This might have undesired side effects but at least it works at all. + * Injects a {@link FMLHandshakeFilter} on the client side to filter out + * those extra, unexpected packets. */ @Inject(method = "insertIntoChannel", at=@At("HEAD")) - public void replayModRecording_forceIsLocalToFalse(CallbackInfo cb) { - handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).set(false); + public void replayModRecording_setupForLocalRecording(CallbackInfo cb) { + // If we're in multiplayer, everything is fine as is + if (!handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).get()) return; + + if (side == Side.SERVER) { + // On the server side, force all packets to be sent + handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).set(false); + } else { + // On the client side, discard additional packets + ChannelPipeline pipeline = handshakeChannel.pipeline(); + pipeline.addAfter(pipeline.context(FMLHandshakeCodec.class).name(), + "replaymod_filter", new FMLHandshakeFilter()); + } } }