Fix server game data snapshot being restored in singleplayer (fixes #44)

This commit is contained in:
johni0702
2016-12-24 11:22:01 +01:00
parent 2c898a8192
commit 6498d625ad
2 changed files with 48 additions and 4 deletions

View File

@@ -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<FMLHandshakeMessage> {
@Override
protected void channelRead0(ChannelHandlerContext ctx, FMLHandshakeMessage msg) throws Exception {
if (!(msg instanceof FMLHandshakeMessage.RegistryData)) {
// Pass on everything but RegistryData messages
ctx.fireChannelRead(msg);
}
}
}

View File

@@ -1,7 +1,11 @@
package com.replaymod.recording.mixin; package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.FMLHandshakeFilter;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.embedded.EmbeddedChannel; 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.common.network.handshake.NetworkDispatcher;
import net.minecraftforge.fml.relauncher.Side;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; 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) @Mixin(value = NetworkDispatcher.class, remap = false)
public abstract class MixinNetworkDispatcher { public abstract class MixinNetworkDispatcher {
@Shadow
private Side side;
@Shadow @Shadow
private EmbeddedChannel handshakeChannel; 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 * This effectively removes the difference in the FML handshake between SP and MP
* and forces the block/item ids, etc. to always be send. * 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")) @Inject(method = "insertIntoChannel", at=@At("HEAD"))
public void replayModRecording_forceIsLocalToFalse(CallbackInfo cb) { 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); 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());
}
} }
} }