Get recording and replay working

This commit is contained in:
Jonas Herzig
2019-03-10 12:53:03 +01:00
parent 8705bbad9a
commit f8fc3462e8
22 changed files with 538 additions and 331 deletions

View File

@@ -83,6 +83,12 @@ public class RecordingEventHandler {
}
}
//#if MC>=11300
public void onPacket(Packet<?> packet) {
packetListener.save(packet);
}
//#endif
public void onPlayerJoin() {
try {
//#if MC>=10904

View File

@@ -0,0 +1,45 @@
//#if MC>=11300
package com.replaymod.recording.mixin;
import com.replaymod.core.versions.MCVer;
import com.replaymod.extras.advancedscreenshots.AdvancedScreenshots;
import com.replaymod.replay.ReplayModReplay;
import net.minecraft.client.KeyboardListener;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.client.shader.Framebuffer;
import net.minecraft.util.ScreenShotHelper;
import net.minecraft.util.text.ITextComponent;
import net.minecraftforge.fml.hooks.BasicEventHooks;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.Redirect;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.io.File;
import java.util.function.Consumer;
@Mixin(KeyboardListener.class)
public abstract class MixinKeyboardListener {
@Inject(method = "onKeyEvent", at = @At("HEAD"), cancellable = true)
private void dispatchKeyEvent(long windowPointer, int key, int scanCode, int action, int modifiers, CallbackInfo ci) {
// FIXME this is a hack and will hopefully be done properly by forge before its release (MinecraftForge#5481)
// (and therefore before RM's release), this code will have to be removed then and handling code updated
KeyBinding keyBindChat = MCVer.getMinecraft().gameSettings.keyBindChat;
if (action == 1 && keyBindChat.matchesKey(key, scanCode)) {
keyBindChat.pressTime++;
BasicEventHooks.fireKeyInput();
ci.cancel();
}
}
@Redirect(method = "onKeyEvent", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/ScreenShotHelper;saveScreenshot(Ljava/io/File;IILnet/minecraft/client/shader/Framebuffer;Ljava/util/function/Consumer;)V"))
private void takeScreenshot(File p_148260_0_, int p_148260_1_, int p_148260_2_, Framebuffer p_148260_3_, Consumer<ITextComponent> p_148260_4_) {
if (ReplayModReplay.instance.getReplayHandler() != null) {
AdvancedScreenshots.take();
} else {
ScreenShotHelper.saveScreenshot(p_148260_0_, p_148260_1_, p_148260_2_, p_148260_3_, p_148260_4_);
}
}
}
//#endif

View File

@@ -0,0 +1,41 @@
//#if MC>=11300
package com.replaymod.recording.mixin;
import com.replaymod.replay.InputReplayTimer;
import com.replaymod.replay.ReplayModReplay;
import net.minecraft.client.MouseHelper;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.LocalCapture;
@Mixin(MouseHelper.class)
public abstract class MixinMouseHelper {
//#ifdef DEV_ENV
@Shadow
private boolean mouseGrabbed;
@Inject(method = "grabMouse", at = @At("HEAD"), cancellable = true)
private void noGrab(CallbackInfo ci) {
// Used to be provided by Forge for 1.12.2 and below
if (Boolean.valueOf(System.getProperty("fml.noGrab", "false"))) {
mouseGrabbed = true;
ci.cancel();
}
}
//#endif
@Inject(method = "scrollCallback",
at = @At(value = "INVOKE", target = "Lnet/minecraft/client/entity/EntityPlayerSP;isSpectator()Z"),
locals = LocalCapture.CAPTURE_FAILHARD,
cancellable = true)
private void handleReplayModScroll(long _p0, double _p1, double _p2, CallbackInfo ci, double _l1, double yOffsetAccumulated) {
if (ReplayModReplay.instance.getReplayHandler() != null) {
InputReplayTimer.handleScroll((int) yOffsetAccumulated);
ci.cancel();
}
}
}
//#endif

View File

@@ -9,11 +9,17 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//#if MC>=11300
import com.replaymod.core.versions.MCVer;
import com.replaymod.recording.handler.RecordingEventHandler.RecordingEventSender;
import net.minecraft.network.login.server.SPacketCustomPayloadLogin;
//#else
//#if MC>=10800
// FIXME event not (yet?) in 1.13 import net.minecraftforge.fml.common.network.FMLNetworkEvent;
//$$ import net.minecraftforge.fml.common.network.FMLNetworkEvent;
//#else
//$$ import cpw.mods.fml.common.network.FMLNetworkEvent;
//#endif
//#endif
@Mixin(NetHandlerLoginClient.class)
public abstract class MixinNetHandlerLoginClient {
@@ -27,6 +33,16 @@ public abstract class MixinNetHandlerLoginClient {
//#if MC>=11300
@Inject(method = "func_209521_a", at=@At("HEAD"))
private void replayModRecording_initiateRecording(SPacketCustomPayloadLogin packet, CallbackInfo ci) {
RecordingEventSender eventSender = (RecordingEventSender) MCVer.getMinecraft().renderGlobal;
if (eventSender.getRecordingEventHandler() != null) {
return; // already recording
}
ReplayModRecording.instance.initiateRecording(networkManager);
if (eventSender.getRecordingEventHandler() != null) {
eventSender.getRecordingEventHandler().onPacket(packet);
}
}
//#else
//$$ /**
//$$ * Starts the recording right before switching into PLAY state.
@@ -34,14 +50,14 @@ public abstract class MixinNetHandlerLoginClient {
//$$ * as it only fires after the forge handshake.
//$$ */
//$$ @Inject(method = "handleLoginSuccess", at=@At("HEAD"))
//#endif
public void replayModRecording_initiateRecording(CallbackInfo cb) {
//$$ public void replayModRecording_initiateRecording(CallbackInfo cb) {
//#if MC>=10800
ReplayModRecording.instance.initiateRecording(networkManager);
//$$ ReplayModRecording.instance.initiateRecording(networkManager);
//#else
//$$ ReplayModRecording.instance.initiateRecording(field_147393_d);
//#endif
}
//$$ }
//#endif
//#if MC>=11200
@Inject(method = "handleLoginSuccess", at=@At("RETURN"))

View File

@@ -1,5 +1,6 @@
package com.replaymod.recording.mixin;
import com.replaymod.core.versions.MCVer;
import com.replaymod.recording.handler.RecordingEventHandler;
import net.minecraft.client.Minecraft;
import net.minecraft.client.network.NetHandlerPlayClient;
@@ -13,7 +14,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//#if MC>=10800
//#if MC>=10904
import net.minecraft.network.play.server.SPacketPlayerListItem.Action;
// FIXME requires RuntimeInvisParam workaround import net.minecraft.network.play.server.SPacketPlayerListItem.AddPlayerData;
import net.minecraft.network.play.server.SPacketPlayerListItem.AddPlayerData;
//#else
//$$ import net.minecraft.network.play.server.S38PacketPlayerListItem.Action;
//$$ import net.minecraft.network.play.server.S38PacketPlayerListItem.AddPlayerData;
@@ -30,8 +31,7 @@ import static com.replaymod.core.versions.MCVer.*;
@Mixin(NetHandlerPlayClient.class)
public abstract class MixinNetHandlerPlayClient {
@Shadow
private Minecraft gameController;
private static Minecraft gameController = MCVer.getMinecraft();
//#if MC>=10800
@Shadow
@@ -61,7 +61,6 @@ public abstract class MixinNetHandlerPlayClient {
RecordingEventHandler handler = getRecordingEventHandler();
if (handler != null && packet.getAction() == Action.ADD_PLAYER) {
/* FIXME see import
for (AddPlayerData data : packet.getEntries()) {
if (data.getProfile() == null || data.getProfile().getId() == null) continue;
// Only add spawn packet for our own player and only if he isn't known yet
@@ -70,7 +69,6 @@ public abstract class MixinNetHandlerPlayClient {
handler.onPlayerJoin();
}
}
*/
}
}
//#else

View File

@@ -1,78 +1,77 @@
/* FIXME
package com.replaymod.recording.mixin;
import com.replaymod.recording.handler.FMLHandshakeFilter;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.embedded.EmbeddedChannel;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//#if MC<11300
//$$ package com.replaymod.recording.mixin;
//$$
//$$ import com.replaymod.recording.handler.FMLHandshakeFilter;
//$$ import io.netty.channel.ChannelPipeline;
//$$ import io.netty.channel.embedded.EmbeddedChannel;
//$$ import org.spongepowered.asm.mixin.Mixin;
//$$ import org.spongepowered.asm.mixin.Shadow;
//$$ import org.spongepowered.asm.mixin.injection.At;
//$$ import org.spongepowered.asm.mixin.injection.Inject;
//$$ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//$$
//#if MC>=11200
import io.netty.channel.ChannelConfig;
import net.minecraft.network.EnumConnectionState;
import net.minecraft.network.NetworkManager;
import org.spongepowered.asm.mixin.injection.Redirect;
//$$ import io.netty.channel.ChannelConfig;
//$$ import net.minecraft.network.EnumConnectionState;
//$$ import net.minecraft.network.NetworkManager;
//$$ import org.spongepowered.asm.mixin.injection.Redirect;
//#endif
//$$
//#if MC>=10800
import net.minecraftforge.fml.common.network.handshake.FMLHandshakeCodec;
import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher;
import net.minecraftforge.fml.relauncher.Side;
//$$ import net.minecraftforge.fml.common.network.handshake.FMLHandshakeCodec;
//$$ import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher;
//$$ import net.minecraftforge.fml.relauncher.Side;
//#else
//$$ import cpw.mods.fml.common.network.handshake.FMLHandshakeCodec;
//$$ import cpw.mods.fml.common.network.handshake.NetworkDispatcher;
//$$ import cpw.mods.fml.relauncher.Side;
//#endif
@Mixin(value = NetworkDispatcher.class, remap = false)
public abstract class MixinNetworkDispatcher {
@Shadow
private Side side;
@Shadow
private EmbeddedChannel handshakeChannel;
/**
* 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.
* Injects a {@link FMLHandshakeFilter} on the client side to filter out
* those extra, unexpected packets.
*/
/*
@Inject(method = "insertIntoChannel", at=@At("HEAD"))
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());
}
}
//$$
//$$ @Mixin(value = NetworkDispatcher.class, remap = false)
//$$ public abstract class MixinNetworkDispatcher {
//$$
//$$ @Shadow
//$$ private Side side;
//$$
//$$ @Shadow
//$$ private EmbeddedChannel handshakeChannel;
//$$
//$$ /**
//$$ * 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.
//$$ * Injects a {@link FMLHandshakeFilter} on the client side to filter out
//$$ * those extra, unexpected packets.
//$$ */
//$$ @Inject(method = "insertIntoChannel", at=@At("HEAD"))
//$$ 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());
//$$ }
//$$ }
//$$
//#if MC>=11200
@Redirect(method = "clientListenForServerHandshake", at = @At(value = "INVOKE", remap = true, target =
"Lnet/minecraft/network/NetworkManager;setConnectionState(Lnet/minecraft/network/EnumConnectionState;)V"))
public void replayModRecording_raceConditionWorkAround1(NetworkManager self, EnumConnectionState ignored) { }
@Redirect(method = "insertIntoChannel", at = @At(value = "INVOKE", target =
"Lio/netty/channel/ChannelConfig;setAutoRead(Z)Lio/netty/channel/ChannelConfig;"))
public ChannelConfig replayModRecording_raceConditionWorkAround2(ChannelConfig self, boolean autoRead) {
if (side == Side.CLIENT) {
autoRead = false;
}
return self.setAutoRead(autoRead);
}
//$$ @Redirect(method = "clientListenForServerHandshake", at = @At(value = "INVOKE", remap = true, target =
//$$ "Lnet/minecraft/network/NetworkManager;setConnectionState(Lnet/minecraft/network/EnumConnectionState;)V"))
//$$ public void replayModRecording_raceConditionWorkAround1(NetworkManager self, EnumConnectionState ignored) { }
//$$
//$$ @Redirect(method = "insertIntoChannel", at = @At(value = "INVOKE", target =
//$$ "Lio/netty/channel/ChannelConfig;setAutoRead(Z)Lio/netty/channel/ChannelConfig;"))
//$$ public ChannelConfig replayModRecording_raceConditionWorkAround2(ChannelConfig self, boolean autoRead) {
//$$ if (side == Side.CLIENT) {
//$$ autoRead = false;
//$$ }
//$$ return self.setAutoRead(autoRead);
//$$ }
//#endif
}
*/
//$$ }
//#endif

View File

@@ -7,6 +7,7 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
//#if MC>=11300
import net.minecraft.client.renderer.WorldRenderer;
//#else
//$$ import net.minecraft.client.renderer.RenderGlobal;
//#endif
@@ -19,8 +20,11 @@ import net.minecraft.util.math.BlockPos;
//#endif
//#endif
/* FIXME runtimeinvisparameter
@Mixin(RenderGlobal.class)
//#if MC>=11300
@Mixin(WorldRenderer.class)
//#else
//$$ @Mixin(RenderGlobal.class)
//#endif
public abstract class MixinRenderGlobal implements RecordingEventHandler.RecordingEventSender {
private RecordingEventHandler recordingEventHandler;
@@ -58,4 +62,3 @@ public abstract class MixinRenderGlobal implements RecordingEventHandler.Recordi
}
}
}
*/

View File

@@ -17,6 +17,10 @@ import net.minecraft.network.play.server.*;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
//#if MC>=11300
import net.minecraft.network.login.server.SPacketLoginSuccess;
//#endif
//#if MC>=10904
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.util.text.TextComponentString;
@@ -67,6 +71,11 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
private long lastSentPacket;
private long timePassedWhilePaused;
private volatile boolean serverWasPaused;
//#if MC>=11300
private EnumConnectionState connectionState = EnumConnectionState.LOGIN;
//#else
//$$ private EnumConnectionState connectionState = EnumConnectionState.PLAY;
//#endif
/**
* Used to keep track of the last metadata save job submitted to the save service and
@@ -147,6 +156,12 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
throw new RuntimeException(e);
}
});
//#if MC>=11300
if (packet instanceof SPacketLoginSuccess) {
connectionState = EnumConnectionState.PLAY;
}
//#endif
} catch(Exception e) {
logger.error("Writing packet:", e);
}
@@ -339,9 +354,9 @@ public class PacketListener extends ChannelInboundHandlerAdapter {
//#endif
//#if MC>=10800
Integer packetId = EnumConnectionState.PLAY.getPacketId(EnumPacketDirection.CLIENTBOUND, packet);
Integer packetId = connectionState.getPacketId(EnumPacketDirection.CLIENTBOUND, packet);
//#else
//$$ Integer packetId = (Integer) EnumConnectionState.PLAY.func_150755_b().inverse().get(packet.getClass());
//$$ Integer packetId = (Integer) connectionState.func_150755_b().inverse().get(packet.getClass());
//#endif
if (packetId == null) {
throw new IOException("Unknown packet type:" + packet.getClass());