diff --git a/src/main/java/com/replaymod/recording/packet/PacketListener.java b/src/main/java/com/replaymod/recording/packet/PacketListener.java index 569fcf73..b8a8a576 100644 --- a/src/main/java/com/replaymod/recording/packet/PacketListener.java +++ b/src/main/java/com/replaymod/recording/packet/PacketListener.java @@ -45,6 +45,10 @@ //$$ import net.minecraftforge.fml.common.network.internal.FMLProxyPacket; //#endif +//#if FABRIC<1 +//$$ import net.minecraft.network.login.server.SCustomPayloadLoginPacket; +//#endif + //#if MC>=10904 //#else //$$ import java.util.List; @@ -104,8 +108,8 @@ public class PacketListener extends ChannelInboundHandlerAdapter { private boolean loginPhase = true; //#else //#if MC>=11600 - //$$ private ProtocolType connectionState = ProtocolType.PLAY; - //$$ private boolean loginPhase = false; + //$$ private ProtocolType connectionState = ProtocolType.LOGIN; + //$$ private boolean loginPhase = true; //#else //$$ private EnumConnectionState connectionState = EnumConnectionState.PLAY; //$$ private boolean loginPhase = false; @@ -365,6 +369,15 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception //$$ } //#endif + //#if FABRIC<1 + //$$ if (packet instanceof SCustomPayloadLoginPacket) { + //$$ PacketBuffer buffer = new PacketBuffer(Unpooled.buffer()); + //$$ packet.writePacketData(buffer); + //$$ packet = new SCustomPayloadLoginPacket(); + //$$ packet.readPacketData(buffer); + //$$ } + //#endif + //#if MC>=10800 if (packet instanceof CustomPayloadS2CPacket) { // Forge may read from this ByteBuf and/or release it during handling diff --git a/src/main/java/com/replaymod/replay/FullReplaySender.java b/src/main/java/com/replaymod/replay/FullReplaySender.java index 076a3de6..69ad17a3 100644 --- a/src/main/java/com/replaymod/replay/FullReplaySender.java +++ b/src/main/java/com/replaymod/replay/FullReplaySender.java @@ -390,78 +390,83 @@ public void channelRead(ChannelHandlerContext ctx, Object msg) if (msg instanceof byte[]) { try { - Packet p = deserializePacket((byte[]) msg); + Packet deserialized = deserializePacket((byte[]) msg); - if (p != null) { - p = processPacket(p); - if (p != null) { - super.channelRead(ctx, p); - } + if (deserialized == null) { + System.out.println("Failed to deserialize a packet to send. loginPhase: " + loginPhase); + return; + } - // If we do not give minecraft time to tick, there will be dead entity artifacts left in the world - // Therefore we have to remove all loaded, dead entities manually if we are in sync mode. - // We do this after every SpawnX packet and after the destroy entities packet. - if (!asyncMode && mc.world != null) { - if (p instanceof PlayerSpawnS2CPacket - || p instanceof EntitySpawnS2CPacket - || p instanceof MobSpawnS2CPacket - //#if MC<11600 - //$$ || p instanceof EntitySpawnGlobalS2CPacket - //#endif - || p instanceof PaintingSpawnS2CPacket - || p instanceof ExperienceOrbSpawnS2CPacket - || p instanceof EntitiesDestroyS2CPacket) { - ClientWorld world = mc.world; - //#if MC>=11400 - // Note: Not sure if it's still required but there's this really handy method anyway - world.finishRemovingEntities(); - //#else - //$$ Iterator iter = world.loadedEntityList.iterator(); - //$$ while (iter.hasNext()) { - //$$ Entity entity = iter.next(); - //$$ if (entity.isDead) { - //$$ int chunkX = entity.chunkCoordX; - //$$ int chunkY = entity.chunkCoordZ; - //$$ - //#if MC>=11400 - //$$ if (entity.addedToChunk && world.getChunkProvider().provideChunk(chunkX, chunkY, false, false) != null) { - //#else - //#if MC>=10904 - //$$ if (entity.addedToChunk && world.getChunkProvider().getLoadedChunk(chunkX, chunkY) != null) { - //#else - //$$ if (entity.addedToChunk && world.getChunkProvider().chunkExists(chunkX, chunkY)) { - //#endif - //#endif - //$$ world.getChunkFromChunkCoords(chunkX, chunkY).removeEntity(entity); - //$$ } - //$$ - //$$ iter.remove(); - //$$ world.onEntityRemoved(entity); - //$$ } - //$$ - //$$ } + Packet processed = processPacket(deserialized); + + if (processed != null) { + // System.out.println("Processing a packet. Class: " + processed.getClass().toString()); + super.channelRead(ctx, processed); + } + + // If we do not give minecraft time to tick, there will be dead entity artifacts left in the world + // Therefore we have to remove all loaded, dead entities manually if we are in sync mode. + // We do this after every SpawnX packet and after the destroy entities packet. + if (!asyncMode && mc.world != null) { + if (processed instanceof PlayerSpawnS2CPacket + || processed instanceof EntitySpawnS2CPacket + || processed instanceof MobSpawnS2CPacket + //#if MC<11600 + //$$ || processed instanceof EntitySpawnGlobalS2CPacket //#endif - } + || processed instanceof PaintingSpawnS2CPacket + || processed instanceof ExperienceOrbSpawnS2CPacket + || processed instanceof EntitiesDestroyS2CPacket) { + ClientWorld world = mc.world; + //#if MC>=11400 + // Note: Not sure if it's still required but there's this really handy method anyway + world.finishRemovingEntities(); + //#else + //$$ Iterator iter = world.loadedEntityList.iterator(); + //$$ while (iter.hasNext()) { + //$$ Entity entity = iter.next(); + //$$ if (entity.isDead) { + //$$ int chunkX = entity.chunkCoordX; + //$$ int chunkY = entity.chunkCoordZ; + //$$ + //#if MC>=11400 + //$$ if (entity.addedToChunk && world.getChunkProvider().provideChunk(chunkX, chunkY, false, false) != null) { + //#else + //#if MC>=10904 + //$$ if (entity.addedToChunk && world.getChunkProvider().getLoadedChunk(chunkX, chunkY) != null) { + //#else + //$$ if (entity.addedToChunk && world.getChunkProvider().chunkExists(chunkX, chunkY)) { + //#endif + //#endif + //$$ world.getChunkFromChunkCoords(chunkX, chunkY).removeEntity(entity); + //$$ } + //$$ + //$$ iter.remove(); + //$$ world.onEntityRemoved(entity); + //$$ } + //$$ + //$$ } + //#endif } + } - //#if MC>=11400 - if (p instanceof ChunkDataS2CPacket) { - Runnable doLightUpdates = () -> { - if (mc.world != null) { - LightingProvider provider = mc.world.getChunkManager().getLightingProvider(); - while (provider.hasUpdates()) { - provider.doLightUpdates(Integer.MAX_VALUE, true, true); - } + //#if MC>=11400 + if (processed instanceof ChunkDataS2CPacket) { + Runnable doLightUpdates = () -> { + if (mc.world != null) { + LightingProvider provider = mc.world.getChunkManager().getLightingProvider(); + while (provider.hasUpdates()) { + provider.doLightUpdates(Integer.MAX_VALUE, true, true); } - }; - if (mc.isOnThread()) { - doLightUpdates.run(); - } else { - mc.send(doLightUpdates); } + }; + if (mc.isOnThread()) { + doLightUpdates.run(); + } else { + mc.send(doLightUpdates); } - //#endif } + //#endif } catch (Exception e) { // We'd rather not have a failure parsing one packet screw up the whole replay process e.printStackTrace(); diff --git a/src/main/java/com/replaymod/replay/ReplayHandler.java b/src/main/java/com/replaymod/replay/ReplayHandler.java index 6f08871c..d8114bac 100644 --- a/src/main/java/com/replaymod/replay/ReplayHandler.java +++ b/src/main/java/com/replaymod/replay/ReplayHandler.java @@ -40,6 +40,10 @@ import java.io.IOException; import java.util.*; +//#if FABRIC<1 +//$$ import io.netty.util.AttributeKey; +//#endif + //#if MC>=11600 import net.minecraft.client.util.math.MatrixStack; //#endif diff --git a/src/main/resources/mixins.replaymod.json b/src/main/resources/mixins.replaymod.json index fd578ae0..8b4718c0 100644 --- a/src/main/resources/mixins.replaymod.json +++ b/src/main/resources/mixins.replaymod.json @@ -22,7 +22,6 @@ "MixinDownloadingPackFinder", "MixinEntityRenderer", "MixinFMLHandshakeHandler", - "MixinFMLPlayMessages", "MixinClientPlayNetHandler", "MixinGameRenderer", "MixinGlStateManager", @@ -79,6 +78,7 @@ "Mixin_Stereoscopic_HandRenderPass", "Mixin_WindowsWorkaroundForTinyEXRNatives", "NetworkManagerAccessor", + "NetworkRegistryAccessor", "ParticleAccessor", "SPacketSpawnMobAccessor", "SPacketSpawnPlayerAccessor", diff --git a/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/MixinClientPlayNetHandler.java b/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/MixinClientPlayNetHandler.java index ea32150a..1bf9487d 100644 --- a/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/MixinClientPlayNetHandler.java +++ b/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/MixinClientPlayNetHandler.java @@ -15,7 +15,6 @@ @Mixin(ClientPlayNetHandler.class) public abstract class MixinClientPlayNetHandler { - @Shadow @Final private NetworkManager netManager; @@ -30,7 +29,8 @@ public void replayMod_ignoreHandshakeConnectionClose(STagsListPacket packetIn, C System.out.println("Injected ClientPlayNetHandler.handleTags"); // PacketThreadUtil.checkThreadAndEnqueue(packetIn, this, this.client); ITagCollectionSupplier itagcollectionsupplier = packetIn.getTags(); - boolean vanillaConnection = net.minecraftforge.fml.network.NetworkHooks.isVanillaConnection(netManager); + // boolean vanillaConnection = net.minecraftforge.fml.network.NetworkHooks.isVanillaConnection(netManager); + boolean vanillaConnection = false; net.minecraftforge.common.ForgeTagHandler.resetCachedTagCollections(true, vanillaConnection); itagcollectionsupplier = ITagCollectionSupplier.reinjectOptionalTags(itagcollectionsupplier); this.networkTagManager = itagcollectionsupplier; diff --git a/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/MixinFMLHandshakeHandler.java b/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/MixinFMLHandshakeHandler.java index a107cc40..29815314 100644 --- a/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/MixinFMLHandshakeHandler.java +++ b/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/MixinFMLHandshakeHandler.java @@ -1,14 +1,41 @@ package com.replaymod.mixin; +import io.netty.channel.ChannelPipeline; import net.minecraft.network.NetworkManager; import net.minecraft.util.text.ITextComponent; import net.minecraftforge.fml.network.FMLHandshakeHandler; +import net.minecraftforge.fml.network.NetworkDirection; +import net.minecraftforge.fml.network.NetworkRegistry; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.MixinEnvironment; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.gen.Accessor; 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.util.List; @Mixin(FMLHandshakeHandler.class) public abstract class MixinFMLHandshakeHandler { + @Shadow + private List messageList; + + @Shadow @Final + private NetworkDirection direction; + + @Inject(method = "(Lnet/minecraft/network/NetworkManager;Lnet/minecraftforge/fml/network/NetworkDirection;)V", at = @At("TAIL")) + public void replayModRecording_setupForLocalRecording(NetworkManager networkManager, NetworkDirection side, CallbackInfo ci) { + if (!networkManager.isLocalChannel()) { + return; + } + + System.out.println("Force FML handshaking and set LoginPayloads"); + this.messageList = NetworkRegistryAccessor.invokeGatherLoginPayloads(this.direction, false); + } + @Redirect(method = "handleRegistryLoading", at=@At(value = "INVOKE", target = "Lnet/minecraft/network/NetworkManager;closeChannel(Lnet/minecraft/util/text/ITextComponent;)V")) public void replayMod_ignoreHandshakeConnectionClose(NetworkManager networkManager, ITextComponent message) {} } diff --git a/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/MixinFMLPlayMessages.java b/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/MixinFMLPlayMessages.java deleted file mode 100644 index f1508e28..00000000 --- a/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/MixinFMLPlayMessages.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.replaymod.mixin; - -import net.minecraft.client.Minecraft; -import net.minecraft.tags.ITagCollectionSupplier; -import net.minecraft.tags.TagRegistryManager; -import net.minecraftforge.common.ForgeTagHandler; -import net.minecraftforge.common.MinecraftForge; -import net.minecraftforge.event.TagsUpdatedEvent; -import net.minecraftforge.fml.network.FMLPlayMessages; -import net.minecraftforge.fml.network.NetworkEvent; -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.callback.CallbackInfo; - -import java.util.function.Supplier; - -@Mixin(FMLPlayMessages.class) -public abstract class MixinFMLPlayMessages { - @Inject(method = "handle", at=@At(value = "HEAD"), cancellable = true) - public void replayMod_ignoreHandshakeConnectionClose(FMLPlayMessages.SyncCustomTagTypes msg, Supplier ctx, CallbackInfo ci) { - System.out.println("Injected FMLPlayMessage.handle"); - - ctx.get().enqueueWork(() -> { - if (Minecraft.getInstance().world != null) { - ITagCollectionSupplier tagCollectionSupplier = Minecraft.getInstance().world.getTags(); - ForgeTagHandler.updateCustomTagTypes(msg); - if (!ctx.get().getNetworkManager().isLocalChannel()) { - TagRegistryManager.fetchCustomTagTypes(tagCollectionSupplier); - MinecraftForge.EVENT_BUS.post(new TagsUpdatedEvent.CustomTagTypes(tagCollectionSupplier)); - } - } - }); - ctx.get().setPacketHandled(true); - ci.cancel(); - } -} diff --git a/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/MixinNetHandlerLoginClient.java b/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/MixinNetHandlerLoginClient.java index 7112ea0a..91234a81 100644 --- a/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/MixinNetHandlerLoginClient.java +++ b/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/MixinNetHandlerLoginClient.java @@ -1,8 +1,11 @@ package com.replaymod.mixin; +import com.replaymod.core.versions.MCVer; import com.replaymod.recording.ReplayModRecording; +import com.replaymod.recording.handler.RecordingEventHandler; import net.minecraft.client.network.login.ClientLoginNetHandler; import net.minecraft.network.NetworkManager; +import net.minecraft.network.login.server.SCustomPayloadLoginPacket; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -21,8 +24,19 @@ public abstract class MixinNetHandlerLoginClient { * We cannot use the {@link FMLNetworkEvent.ClientConnectedToServerEvent} * as it only fires after the forge handshake. */ - @Inject(method = "handleLoginSuccess", at=@At("HEAD")) - public void replayModRecording_initiateRecording(CallbackInfo cb) { + @Inject(method = "handleCustomPayloadLogin", at=@At("HEAD")) + public void replayModRecording_initiateRecording(SCustomPayloadLoginPacket packetIn, CallbackInfo cb) { + initiateRecording(packetIn); + } + + private void initiateRecording(SCustomPayloadLoginPacket packet) { + RecordingEventHandler.RecordingEventSender eventSender = (RecordingEventHandler.RecordingEventSender) MCVer.getMinecraft().worldRenderer; + if (eventSender.getRecordingEventHandler() != null) { + return; // already recording + } ReplayModRecording.instance.initiateRecording(this.networkManager); + if (eventSender.getRecordingEventHandler() != null) { + eventSender.getRecordingEventHandler().onPacket(packet); + } } } diff --git a/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/NetworkRegistryAccessor.java b/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/NetworkRegistryAccessor.java new file mode 100644 index 00000000..d1475f9b --- /dev/null +++ b/versions/1.16.4-forge/src/main/java/com/replaymod/mixin/NetworkRegistryAccessor.java @@ -0,0 +1,16 @@ +package com.replaymod.mixin; + +import net.minecraftforge.fml.network.NetworkDirection; +import net.minecraftforge.fml.network.NetworkRegistry; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +import java.util.List; + +@Mixin(NetworkRegistry.class) +public interface NetworkRegistryAccessor { + @Invoker("gatherLoginPayloads") + static List invokeGatherLoginPayloads(NetworkDirection direction, boolean isLocal) { + throw new AssertionError(); + } +}