diff --git a/build.gradle b/build.gradle index 5337e60a..cd3f4f57 100644 --- a/build.gradle +++ b/build.gradle @@ -264,6 +264,8 @@ dependencies { 11903: '1.19.3-rc3', 11904: '1.19.4', 12001: '1.20.1', + 12002: '1.20.2', + 12004: '1.20.4', ][mcVersion] mappings 'net.fabricmc:yarn:' + [ 11404: '1.14.4+build.16', @@ -281,8 +283,10 @@ dependencies { 11903: '1.19.3-rc3+build.1:v2', 11904: '1.19.4+build.1:v2', 12001: '1.20.1+build.2:v2', + 12002: '1.20.2+build.4:v2', + 12004: '1.20.4+build.1:v2', ][mcVersion] - modImplementation 'net.fabricmc:fabric-loader:0.14.11' + modImplementation 'net.fabricmc:fabric-loader:0.15.1' def fabricApiVersion = [ 11404: '0.4.3+build.247-1.14', 11502: '0.5.1+build.294-1.15', @@ -299,6 +303,8 @@ dependencies { 11903: '0.68.1+1.19.3', 11904: '0.76.0+1.19.4', 12001: '0.83.1+1.20.1', + 12002: '0.91.2+1.20.2', + 12004: '0.91.2+1.20.4', ][mcVersion] def fabricApiModules = [ "api-base", @@ -312,10 +318,10 @@ dependencies { } if (mcVersion >= 11604) { fabricApiModules.add("screen-api-v1") + fabricApiModules.add("networking-api-v1") } if (mcVersion >= 11700) { fabricApiModules.remove("networking-v0") - fabricApiModules.add("networking-api-v1") } fabricApiModules.each { module -> modImplementation fabricApi.module("fabric-$module", fabricApiVersion) @@ -379,7 +385,7 @@ dependencies { shadow 'com.github.ReplayMod.JavaBlend:2.79.0:a0696f8' - shadow "com.github.ReplayMod:ReplayStudio:a1e2b83", shadeExclusions + shadow "com.github.ReplayMod:ReplayStudio:6cd39b0", shadeExclusions // FIXME this should be pulled in by ReplayStudio, and IntelliJ sees it, but javac for some reason does not implementation 'com.github.viaversion:opennbt:0a02214' // 2.0-SNAPSHOT (ViaVersion Edition) @@ -390,7 +396,11 @@ dependencies { shadow 'com.github.ReplayMod:lwjgl-utils:27dcd66' if (FABRIC) { - if (mcVersion >= 12000) { + if (mcVersion >= 12003) { + modImplementation 'com.terraformersmc:modmenu:9.0.0-pre.1' + } else if (mcVersion >= 12002) { + modImplementation 'com.terraformersmc:modmenu:8.0.0' + } else if (mcVersion >= 12000) { modImplementation 'com.terraformersmc:modmenu:7.0.1' } else if (mcVersion >= 11904) { modImplementation 'com.terraformersmc:modmenu:6.1.0-rc.4' diff --git a/jGui b/jGui index eb3297f5..83f71ae3 160000 --- a/jGui +++ b/jGui @@ -1 +1 @@ -Subproject commit eb3297f5a62f3b4c8b8445be85b95205d54763b7 +Subproject commit 83f71ae3e20f5d8a93f81ce7f30f7547262d3c22 diff --git a/root.gradle.kts b/root.gradle.kts index 522c0c30..72cfd411 100755 --- a/root.gradle.kts +++ b/root.gradle.kts @@ -193,6 +193,8 @@ val doRelease by tasks.registering { defaultTasks("bundleJar") preprocess { + val mc12004 = createNode("1.20.4", 12004, "yarn") + val mc12002 = createNode("1.20.2", 12002, "yarn") val mc12001 = createNode("1.20.1", 12001, "yarn") val mc11904 = createNode("1.19.4", 11904, "yarn") val mc11903 = createNode("1.19.3", 11903, "yarn") @@ -218,6 +220,8 @@ preprocess { val mc10800 = createNode("1.8", 10800, "srg") val mc10710 = createNode("1.7.10", 10710, "srg") + mc12004.link(mc12002, file("versions/mapping-fabric-1.20.4-1.20.2.txt")) + mc12002.link(mc12001) mc12001.link(mc11904) mc11904.link(mc11903) mc11903.link(mc11902, file("versions/mapping-fabric-1.19.3-1.19.2.txt")) diff --git a/settings.gradle.kts b/settings.gradle.kts index 6d230a13..bb1dd6e4 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -38,6 +38,8 @@ val jGuiVersions = listOf( "1.19.3", "1.19.4", "1.20.1", + "1.20.2", + "1.20.4", ) val replayModVersions = listOf( // "1.7.10", @@ -64,6 +66,8 @@ val replayModVersions = listOf( "1.19.3", "1.19.4", "1.20.1", + "1.20.2", + "1.20.4", ) rootProject.buildFileName = "root.gradle.kts" diff --git a/src/main/java/com/replaymod/core/gui/RestoreReplayGui.java b/src/main/java/com/replaymod/core/gui/RestoreReplayGui.java index c68a3210..2f615533 100644 --- a/src/main/java/com/replaymod/core/gui/RestoreReplayGui.java +++ b/src/main/java/com/replaymod/core/gui/RestoreReplayGui.java @@ -7,6 +7,7 @@ import com.replaymod.core.utils.Utils; import com.replaymod.core.versions.MCVer; import com.replaymod.replaystudio.io.ReplayInputStream; import com.replaymod.replaystudio.io.ReplayOutputStream; +import com.replaymod.replaystudio.lib.viaversion.api.protocol.packet.State; import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayMetaData; import de.johni0702.minecraft.gui.container.AbstractGuiScreen; @@ -122,7 +123,7 @@ public class RestoreReplayGui extends AbstractGuiScreen { if (metaData != null && metaData.getDuration() == 0) { // Try to restore replay duration // We need to re-write the packet data in case there are any incomplete packets dangling at the end - try (ReplayInputStream in = replayFile.getPacketData(MCVer.getPacketTypeRegistry(true)); + try (ReplayInputStream in = replayFile.getPacketData(MCVer.getPacketTypeRegistry(State.LOGIN)); ReplayOutputStream out = replayFile.writePacketData()) { while (true) { // To prevent failing at un-parsable packets and to support recovery in minimal mode, diff --git a/src/main/java/com/replaymod/core/utils/Restrictions.java b/src/main/java/com/replaymod/core/utils/Restrictions.java index 359eaf77..0fc0e9f2 100644 --- a/src/main/java/com/replaymod/core/utils/Restrictions.java +++ b/src/main/java/com/replaymod/core/utils/Restrictions.java @@ -6,7 +6,7 @@ import net.minecraft.network.PacketByteBuf; import net.minecraft.util.Identifier; //#endif -//#if MC<=10710 +//#if MC<=10710 || MC>=12002 //$$ import io.netty.buffer.Unpooled; //#endif @@ -26,7 +26,10 @@ public class Restrictions { private boolean onlyRecordingPlayer; public String handle(CustomPayloadS2CPacket packet) { - //#if MC>=10800 + //#if MC>=12002 + //$$ PacketByteBuf buffer = new PacketByteBuf(Unpooled.buffer()); + //$$ packet.write(buffer); + //#elseif MC>=10800 PacketByteBuf buffer = packet.getData(); //#else //$$ PacketBuffer buffer = new PacketBuffer(Unpooled.wrappedBuffer(packet.func_149168_d())); diff --git a/src/main/java/com/replaymod/core/utils/Utils.java b/src/main/java/com/replaymod/core/utils/Utils.java index 826f78fa..e9706634 100644 --- a/src/main/java/com/replaymod/core/utils/Utils.java +++ b/src/main/java/com/replaymod/core/utils/Utils.java @@ -80,7 +80,7 @@ public class Utils { static { Image thumbnail; try { - thumbnail = Image.read(getResourceAsStream("/default_thumb.jpg")); + thumbnail = Image.read(getResourceAsStream("/default_thumb.png")); } catch (Exception e) { thumbnail = new Image(1, 1); e.printStackTrace(); diff --git a/src/main/java/com/replaymod/core/utils/WrappedTimer.java b/src/main/java/com/replaymod/core/utils/WrappedTimer.java index 2db83188..754db425 100644 --- a/src/main/java/com/replaymod/core/utils/WrappedTimer.java +++ b/src/main/java/com/replaymod/core/utils/WrappedTimer.java @@ -9,7 +9,9 @@ public class WrappedTimer extends RenderTickCounter { protected final RenderTickCounter wrapped; public WrappedTimer(RenderTickCounter wrapped) { - //#if MC>=11400 + //#if MC>=12003 + //$$ super(0, 0, f -> f); + //#elseif MC>=11400 super(0, 0); //#else //$$ super(0); diff --git a/src/main/java/com/replaymod/core/versions/MCVer.java b/src/main/java/com/replaymod/core/versions/MCVer.java index ff348bfa..44e64f14 100644 --- a/src/main/java/com/replaymod/core/versions/MCVer.java +++ b/src/main/java/com/replaymod/core/versions/MCVer.java @@ -10,6 +10,7 @@ import de.johni0702.minecraft.gui.utils.lwjgl.vector.Vector3f; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; import net.minecraft.client.render.BufferBuilder; +import net.minecraft.network.NetworkState; import net.minecraft.util.Identifier; import net.minecraft.util.Util; import net.minecraft.util.math.Vec3d; @@ -103,6 +104,40 @@ public class MCVer { //#endif } + public static NetworkState asMc(State state) { + switch (state) { + case HANDSHAKE: return NetworkState.HANDSHAKING; + case STATUS: return NetworkState.STATUS; + case LOGIN: return NetworkState.LOGIN; + //#if MC>=12002 + //$$ case CONFIGURATION: return NetworkState.CONFIGURATION; + //#endif + case PLAY: return NetworkState.PLAY; + } + throw new IllegalArgumentException("Unexpected value: " + state); + } + + public static State fromMc(NetworkState mcState) { + switch (mcState) { + case HANDSHAKING: return State.HANDSHAKE; + case STATUS: return State.STATUS; + case LOGIN: return State.LOGIN; + //#if MC>=12002 + //$$ case CONFIGURATION: return State.CONFIGURATION; + //#endif + case PLAY: return State.PLAY; + } + throw new IllegalArgumentException("Unexpected value: " + mcState); + } + + public static PacketTypeRegistry getPacketTypeRegistry(NetworkState state) { + return getPacketTypeRegistry(fromMc(state)); + } + + public static PacketTypeRegistry getPacketTypeRegistry(State state) { + return PacketTypeRegistry.get(ProtocolVersion.getProtocol(getProtocolVersion()), state); + } + public static PacketTypeRegistry getPacketTypeRegistry(boolean loginPhase) { return PacketTypeRegistry.get( ProtocolVersion.getProtocol(getProtocolVersion()), @@ -133,6 +168,7 @@ public class MCVer { //$$ } //#endif + //#if MC<12003 public static //#if MC>=11400 CompletableFuture @@ -160,6 +196,7 @@ public class MCVer { //#endif //#endif } + //#endif public static void addCallback( //#if MC>=11400 diff --git a/src/main/java/com/replaymod/core/versions/Patterns.java b/src/main/java/com/replaymod/core/versions/Patterns.java index bc52be00..984b3fb8 100644 --- a/src/main/java/com/replaymod/core/versions/Patterns.java +++ b/src/main/java/com/replaymod/core/versions/Patterns.java @@ -6,6 +6,7 @@ import com.google.common.util.concurrent.ListenableFuture; import com.replaymod.core.mixin.MinecraftAccessor; import com.replaymod.gradle.remap.Pattern; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.AbstractClientPlayerEntity; import net.minecraft.client.options.GameOptions; import net.minecraft.client.options.KeyBinding; import net.minecraft.client.render.VertexFormat; @@ -16,6 +17,10 @@ import net.minecraft.client.render.entity.EntityRenderDispatcher; import net.minecraft.client.sound.PositionedSoundInstance; import net.minecraft.entity.player.PlayerInventory; import net.minecraft.item.ItemStack; +import net.minecraft.network.NetworkSide; +import net.minecraft.network.NetworkState; +import net.minecraft.network.Packet; +import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket; import net.minecraft.resource.Resource; import net.minecraft.resource.ResourceManager; import net.minecraft.sound.SoundCategory; @@ -59,6 +64,7 @@ import net.minecraft.util.collection.DefaultedList; //#endif //#if MC>=10904 +import net.minecraft.network.packet.s2c.play.UnloadChunkS2CPacket; import net.minecraft.sound.SoundEvent; import net.minecraft.util.crash.CrashCallable; //#else @@ -906,4 +912,64 @@ class Patterns { return entity.world; //#endif } + + @Pattern + public Object channel(CustomPayloadS2CPacket packet) { + //#if MC>=12002 + //$$ return packet.payload().id(); + //#else + return packet.getChannel(); + //#endif + } + + //#if MC>=10904 + @Pattern + public Integer getPacketId(NetworkState state, NetworkSide side, Packet packet) throws Exception { + //#if MC>=12002 + //$$ return state.getHandler(side).getId(packet); + //#else + return state.getPacketId(side, packet); + //#endif + } + + @Pattern + public int UnloadChunkPacket_getX(UnloadChunkS2CPacket packet) { + //#if MC>=12002 + //$$ return packet.pos().x; + //#else + return packet.getX(); + //#endif + } + + @Pattern + public int UnloadChunkPacket_getZ(UnloadChunkS2CPacket packet) { + //#if MC>=12002 + //$$ return packet.pos().z; + //#else + return packet.getZ(); + //#endif + } + //#else + //$$ @Pattern public void getPacketId() {} + //$$ @Pattern public void UnloadChunkPacket_getZ() {} + //$$ @Pattern public void UnloadChunkPacket_getX() {} + //#endif + + @Pattern + public Identifier getSkinTexture(AbstractClientPlayerEntity player) { + //#if MC>=12002 + //$$ return player.getSkinTextures().texture(); + //#else + return player.getSkinTexture(); + //#endif + } + + @Pattern + public boolean isDebugHudEnabled(MinecraftClient mc) { + //#if MC>=12002 + //$$ return mc.getDebugHud().shouldShowDebugHud(); + //#else + return mc.options.debugEnabled; + //#endif + } } diff --git a/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java b/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java index 54940eb3..c3881f55 100644 --- a/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java +++ b/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java @@ -9,6 +9,7 @@ import com.replaymod.replaystudio.filter.SquashFilter; import com.replaymod.replaystudio.filter.StreamFilter; import com.replaymod.replaystudio.io.ReplayInputStream; import com.replaymod.replaystudio.io.ReplayOutputStream; +import com.replaymod.replaystudio.lib.viaversion.api.protocol.packet.State; import com.replaymod.replaystudio.protocol.PacketTypeRegistry; import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayMetaData; @@ -122,7 +123,7 @@ public class MarkerProcessor { String replayName = FilenameUtils.getBaseName(path.getFileName().toString()); int splitCounter = 0; - PacketTypeRegistry registry = MCVer.getPacketTypeRegistry(true); + PacketTypeRegistry registry = MCVer.getPacketTypeRegistry(State.LOGIN); DimensionTracker dimensionTracker = new DimensionTracker(); SquashFilter squashFilter = new SquashFilter(null, null, null); diff --git a/src/main/java/com/replaymod/pathing/player/ReplayTimer.java b/src/main/java/com/replaymod/pathing/player/ReplayTimer.java index 30d5f462..d970cb05 100644 --- a/src/main/java/com/replaymod/pathing/player/ReplayTimer.java +++ b/src/main/java/com/replaymod/pathing/player/ReplayTimer.java @@ -8,7 +8,9 @@ import net.minecraft.client.render.RenderTickCounter; * Wrapper around the current timer that prevents the timer from advancing by itself. */ public class ReplayTimer extends WrappedTimer { - //#if MC>=11400 + //#if MC>=12003 + //$$ private final RenderTickCounter state = new RenderTickCounter(0, 0, f -> f); + //#elseif MC>=11400 private final RenderTickCounter state = new RenderTickCounter(0, 0); //#else //$$ private final Timer state = new Timer(0); diff --git a/src/main/java/com/replaymod/recording/handler/ConnectionEventHandler.java b/src/main/java/com/replaymod/recording/handler/ConnectionEventHandler.java index 1137485f..ef71a08b 100644 --- a/src/main/java/com/replaymod/recording/handler/ConnectionEventHandler.java +++ b/src/main/java/com/replaymod/recording/handler/ConnectionEventHandler.java @@ -112,7 +112,11 @@ public class ConnectionEventHandler { //#endif serverName = worldName; //#if MC>=11100 + //#if MC>=12002 + //$$ } else if (serverInfo != null && serverInfo.isRealm()) { + //#else } else if (mc.isConnectedToRealms()) { + //#endif // we can't access the server name without tapping too deep in the Realms Library worldName = "A Realms Server"; //#endif diff --git a/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java b/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java index fe2772e6..8d948069 100644 --- a/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java +++ b/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java @@ -16,7 +16,6 @@ import net.minecraft.network.packet.s2c.play.EntityPositionS2CPacket; import net.minecraft.network.packet.s2c.play.EntityS2CPacket; import net.minecraft.network.packet.s2c.play.EntitySetHeadYawS2CPacket; import net.minecraft.network.packet.s2c.play.EntityVelocityUpdateS2CPacket; -import net.minecraft.network.packet.s2c.play.PlayerSpawnS2CPacket; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.network.Packet; @@ -30,6 +29,12 @@ import net.minecraft.server.integrated.IntegratedServer; //$$ import net.minecraftforge.event.entity.player.PlayerEvent.ItemPickupEvent; //#endif +//#if MC>=12002 +//$$ import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket; +//#else +import net.minecraft.network.packet.s2c.play.PlayerSpawnS2CPacket; +//#endif + //#if MC>=11600 import com.mojang.datafixers.util.Pair; //#endif @@ -103,7 +108,11 @@ public class RecordingEventHandler extends EventRegistrations { try { ClientPlayerEntity player = mc.player; assert player != null; + //#if MC>=12002 + //$$ packetListener.save(new EntitySpawnS2CPacket(player)); + //#else packetListener.save(new PlayerSpawnS2CPacket(player)); + //#endif //#if MC>=11903 //$$ packetListener.save(new EntityTrackerUpdateS2CPacket(player.getId(), player.getDataTracker().getChangedEntries())); //#elseif MC>=11500 diff --git a/src/main/java/com/replaymod/recording/mixin/MixinMouseHelper.java b/src/main/java/com/replaymod/recording/mixin/MixinMouseHelper.java index c7e8f5d4..f696f3f3 100644 --- a/src/main/java/com/replaymod/recording/mixin/MixinMouseHelper.java +++ b/src/main/java/com/replaymod/recording/mixin/MixinMouseHelper.java @@ -32,6 +32,12 @@ public abstract class MixinMouseHelper { private void handleReplayModScroll( long _p0, double _p1, double _p2, CallbackInfo ci, + //#if MC>=12002 + //$$ boolean discreteScroll, + //$$ double sensitivity, + //$$ double xOffsetAccumulated, + //$$ double yOffsetAccumulated + //#else double _l1, //#if MC>=11802 //$$ int yOffsetAccumulated @@ -40,6 +46,7 @@ public abstract class MixinMouseHelper { //#else //$$ double yOffsetAccumulated //#endif + //#endif ) { if (ReplayModReplay.instance.getReplayHandler() != null) { InputReplayTimer.handleScroll((int) (yOffsetAccumulated * 120)); diff --git a/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java b/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java index 8be310c4..b4ac0a8b 100644 --- a/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java +++ b/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java @@ -2,6 +2,7 @@ package com.replaymod.recording.mixin; import com.replaymod.core.versions.MCVer; import com.replaymod.recording.handler.RecordingEventHandler; +import com.replaymod.replaystudio.lib.viaversion.api.protocol.packet.State; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.network.packet.s2c.play.PlayerRespawnS2CPacket; @@ -78,7 +79,7 @@ public abstract class MixinNetHandlerPlayClient { byteBuf.readBytes(array); for (PacketPlayerListEntry data : PacketPlayerListEntry.read(new Packet( - MCVer.getPacketTypeRegistry(false), 0, PacketType.PlayerListEntry, + MCVer.getPacketTypeRegistry(State.PLAY), 0, PacketType.PlayerListEntry, com.github.steveice10.netty.buffer.Unpooled.wrappedBuffer(array) ))) { if (data.getUuid() == null) continue; diff --git a/src/main/java/com/replaymod/recording/packet/PacketListener.java b/src/main/java/com/replaymod/recording/packet/PacketListener.java index be72bedb..1cd11622 100644 --- a/src/main/java/com/replaymod/recording/packet/PacketListener.java +++ b/src/main/java/com/replaymod/recording/packet/PacketListener.java @@ -30,7 +30,6 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.network.ClientConnection; import net.minecraft.network.packet.s2c.play.CustomPayloadS2CPacket; import net.minecraft.network.packet.s2c.play.DisconnectS2CPacket; -import net.minecraft.network.packet.s2c.play.PlayerSpawnS2CPacket; import net.minecraft.entity.Entity; import net.minecraft.network.NetworkState; import net.minecraft.network.PacketByteBuf; @@ -41,6 +40,13 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +//#if MC>=12002 +//$$ import net.minecraft.entity.EntityType; +//$$ import net.minecraft.network.packet.s2c.play.EntitySpawnS2CPacket; +//#else +import net.minecraft.network.packet.s2c.play.PlayerSpawnS2CPacket; +//#endif + //#if MC>=10800 //#if MC<10904 //$$ import net.minecraft.network.play.server.S46PacketSetCompressionLevel; @@ -86,11 +92,22 @@ public class PacketListener extends ChannelInboundHandlerAdapter { private static final MinecraftClient mc = getMinecraft(); private static final Logger logger = LogManager.getLogger(); + private static final ResourcePackSendS2CPacket RESOURCE_PACK_SEND_PACKET = + //#if MC>=12003 + //$$ new ResourcePackSendS2CPacket(null, "", "", false, null) + //#elseif MC>=11700 + //$$ new ResourcePackSendS2CPacket("", "", false, null) + //#else + new ResourcePackSendS2CPacket() + //#endif + ; + private static final int PACKET_ID_RESOURCE_PACK_SEND = getPacketId(NetworkState.PLAY, RESOURCE_PACK_SEND_PACKET); + //#if MC>=12002 + //$$ private static final int PACKET_ID_CONFIG_RESOURCE_PACK_SEND = getPacketId(NetworkState.CONFIGURATION, RESOURCE_PACK_SEND_PACKET); + //#endif //#if MC>=11700 - //$$ private static final int PACKET_ID_RESOURCE_PACK_SEND = getPacketId(NetworkState.PLAY, new ResourcePackSendS2CPacket("", "", false, null)); //$$ private static final int PACKET_ID_LOGIN_COMPRESSION = getPacketId(NetworkState.LOGIN, new LoginCompressionS2CPacket(0)); //#else - private static final int PACKET_ID_RESOURCE_PACK_SEND = getPacketId(NetworkState.PLAY, new ResourcePackSendS2CPacket()); private static final int PACKET_ID_LOGIN_COMPRESSION = getPacketId(NetworkState.LOGIN, new LoginCompressionS2CPacket()); //#endif //#if MC<10904 @@ -152,7 +169,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter { out.write(json.getBytes()); } } else { - replayFile.writeMetaData(MCVer.getPacketTypeRegistry(true), metaData); + replayFile.writeMetaData(MCVer.getPacketTypeRegistry(State.LOGIN), metaData); } } } catch (IOException e) { @@ -347,7 +364,11 @@ public class PacketListener extends ChannelInboundHandlerAdapter { // for integrated server connections MC is passing the packet objects directly, so we need to encode them // ourselves to be able to store them //#if MC>=11904 + //#if MC>=12002 + //$$ PacketBundleHandler bundleHandler = ctx.channel().attr(ClientConnection.CLIENTBOUND_PROTOCOL_KEY).get().getBundler(); + //#else //$$ PacketBundleHandler bundleHandler = ctx.channel().attr(PacketBundleHandler.KEY).get().getBundler(NetworkSide.CLIENTBOUND); + //#endif //$$ List packets = new ArrayList<>(1); //$$ bundleHandler.forEachPacket((net.minecraft.network.packet.Packet) msg, unbundledPacket -> { //$$ try { @@ -368,9 +389,16 @@ public class PacketListener extends ChannelInboundHandlerAdapter { } if (packet != null) { - if (connectionState == NetworkState.PLAY && packet.getId() == PACKET_ID_RESOURCE_PACK_SEND) { + if (connectionState == NetworkState.PLAY && packet.getId() == PACKET_ID_RESOURCE_PACK_SEND + //#if MC>=12002 + //$$ || connectionState == NetworkState.CONFIGURATION && packet.getId() == PACKET_ID_CONFIG_RESOURCE_PACK_SEND + //#endif + ) { ClientConnection connection = ctx.pipeline().get(ClientConnection.class); save(resourcePackRecorder.handleResourcePack(connection, (ResourcePackSendS2CPacket) decodeMcPacket(packet))); + //#if MC>=12003 + //$$ super.channelRead(ctx, msg); + //#endif return; } @@ -385,8 +413,13 @@ public class PacketListener extends ChannelInboundHandlerAdapter { if (ctx == null) { return NetworkState.LOGIN; } + //#if MC>=12002 + //$$ AttributeKey> key = ClientConnection.CLIENTBOUND_PROTOCOL_KEY; + //$$ return ctx.channel().attr(key).get().getState(); + //#else AttributeKey key = ClientConnection.ATTR_KEY_PROTOCOL; return ctx.channel().attr(key).get(); + //#endif } private static Packet encodeMcPacket(NetworkState connectionState, net.minecraft.network.Packet packet) throws Exception { @@ -402,7 +435,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter { try { packet.write(new PacketByteBuf(byteBuf)); return new Packet( - MCVer.getPacketTypeRegistry(connectionState == NetworkState.LOGIN), + MCVer.getPacketTypeRegistry(connectionState), packetId, com.github.steveice10.netty.buffer.Unpooled.wrappedBuffer( byteBuf.array(), @@ -416,11 +449,13 @@ public class PacketListener extends ChannelInboundHandlerAdapter { } private static net.minecraft.network.Packet decodeMcPacket(Packet packet) throws IOException, IllegalAccessException, InstantiationException { - NetworkState connectionState = packet.getRegistry().getState() == State.LOGIN ? NetworkState.LOGIN : NetworkState.PLAY; + NetworkState connectionState = asMc(packet.getRegistry().getState()); int packetId = packet.getId(); PacketByteBuf packetBuf = new PacketByteBuf(Unpooled.wrappedBuffer(packet.getBuf().nioBuffer())); - //#if MC>=11700 + //#if MC>=12002 + //$$ return connectionState.getHandler(NetworkSide.CLIENTBOUND).createPacket(packetId, packetBuf); + //#elseif MC>=11700 //$$ return connectionState.getPacketHandler(NetworkSide.CLIENTBOUND, packetId, packetBuf); //#else //#if MC>=10800 @@ -439,7 +474,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter { byte[] bytes = new byte[packetBuf.readableBytes()]; packetBuf.readBytes(bytes); return new Packet( - MCVer.getPacketTypeRegistry(connectionState == NetworkState.LOGIN), + MCVer.getPacketTypeRegistry(connectionState), packetId, com.github.steveice10.netty.buffer.Unpooled.wrappedBuffer(bytes) ); @@ -492,6 +527,10 @@ public class PacketListener extends ChannelInboundHandlerAdapter { this.serverWasPaused = true; } + public ResourcePackRecorder getResourcePackRecorder() { + return resourcePackRecorder; + } + private class DecodedPacketListener extends ChannelInboundHandlerAdapter { @Override public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { @@ -503,12 +542,17 @@ public class PacketListener extends ChannelInboundHandlerAdapter { } } + //#if MC>=12002 + //$$ if (msg instanceof EntitySpawnS2CPacket packet && packet.getEntityType() == EntityType.PLAYER) { + //$$ UUID uuid = packet.getUuid(); + //#else if (msg instanceof PlayerSpawnS2CPacket) { //#if MC>=10800 UUID uuid = ((PlayerSpawnS2CPacket) msg).getPlayerUuid(); //#else //$$ UUID uuid = ((S0CPacketSpawnPlayer) msg).func_148948_e().getId(); //#endif + //#endif Set uuids = new HashSet<>(Arrays.asList(metaData.getPlayers())); uuids.add(uuid.toString()); metaData.setPlayers(uuids.toArray(new String[uuids.size()])); diff --git a/src/main/java/com/replaymod/recording/packet/ResourcePackRecorder.java b/src/main/java/com/replaymod/recording/packet/ResourcePackRecorder.java index 01fd26c9..1d2fc815 100644 --- a/src/main/java/com/replaymod/recording/packet/ResourcePackRecorder.java +++ b/src/main/java/com/replaymod/recording/packet/ResourcePackRecorder.java @@ -1,7 +1,6 @@ package com.replaymod.recording.packet; import com.google.common.hash.Hashing; -import com.google.common.io.Files; import com.replaymod.replaystudio.replay.ReplayFile; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.ConfirmScreen; @@ -11,6 +10,10 @@ import net.minecraft.client.resource.ClientBuiltinResourcePackProvider; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +//#if MC>=12003 +//$$ import java.util.UUID; +//#endif + //#if MC>=11900 //$$ import java.net.MalformedURLException; //$$ import java.net.URL; @@ -55,6 +58,8 @@ import net.minecraft.network.ClientConnection; import java.io.File; import java.io.IOException; import java.io.OutputStream; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.HashMap; import java.util.Map; @@ -75,10 +80,10 @@ public class ResourcePackRecorder { this.replayFile = replayFile; } - public void recordResourcePack(File file, int requestId) { + public void recordResourcePack(Path file, int requestId) { try { // Read in resource pack file - byte[] bytes = Files.toByteArray(file); + byte[] bytes = Files.readAllBytes(file); // Check whether it is already known String hash = Hashing.sha1().hashBytes(bytes).toString(); boolean doWrite = false; // Whether we are the first and have to write it @@ -105,7 +110,24 @@ public class ResourcePackRecorder { } } - //#if MC>=10800 + //#if MC>=12003 + //$$ private final Map mcIdToReplayId = new HashMap<>(); + //$$ + //$$ public synchronized ResourcePackSendS2CPacket handleResourcePack(ClientConnection netManager, ResourcePackSendS2CPacket packet) { + //$$ final int requestId = nextRequestId++; + //$$ mcIdToReplayId.put(packet.id(), requestId); + //$$ return new ResourcePackSendS2CPacket(packet.id(), "replay://" + requestId, "", packet.required(), packet.prompt()); + //$$ } + //$$ + //$$ public void recordResourcePack(Path file, UUID uuid) { + //$$ Integer id = mcIdToReplayId.get(uuid); + //$$ if (id == null) { + //$$ logger.warn("Got resource pack download for unexpected uuid " + uuid + " at " + file); + //$$ return; + //$$ } + //$$ recordResourcePack(file, id); + //$$ } + //#elseif MC>=10800 public ResourcePackStatusC2SPacket makeStatusPacket(String hash, Status action) { //#if MC>=11002 return new ResourcePackStatusC2SPacket(action); @@ -128,7 +150,7 @@ public class ResourcePackRecorder { if (levelDir.isFile()) { netManager.send(makeStatusPacket(hash, Status.ACCEPTED)); addCallback(setServerResourcePack(levelDir), result -> { - recordResourcePack(levelDir, requestId); + recordResourcePack(levelDir.toPath(), requestId); netManager.send(makeStatusPacket(hash, Status.SUCCESSFULLY_LOADED)); }, throwable -> { netManager.send(makeStatusPacket(hash, Status.FAILED_DOWNLOAD)); @@ -196,7 +218,7 @@ public class ResourcePackRecorder { //#endif downloadResourcePack(final int requestId, String url, String hash) { ClientBuiltinResourcePackProvider packFinder = mc.getResourcePackDownloader(); - ((IDownloadingPackFinder) packFinder).setRequestCallback(file -> recordResourcePack(file, requestId)); + ((IDownloadingPackFinder) packFinder).setRequestCallback(file -> recordResourcePack(file.toPath(), requestId)); //#if MC>=11900 //$$ try { //$$ URL theUrl = new URL(url); @@ -286,7 +308,7 @@ public class ResourcePackRecorder { //$$ acc.setActive(false); //$$ acc.setPack(new FileResourcePack(file)); //$$ Minecraft.getMinecraft().scheduleResourcesRefresh(); - //$$ recordResourcePack(file, requestId); + //$$ recordResourcePack(file.toPath(), requestId); //$$ } //$$ } //$$ }, hashmap, 50*1024*1024, guiScreen, Minecraft.getMinecraft().getProxy()); diff --git a/src/main/java/com/replaymod/render/gui/GuiVideoRenderer.java b/src/main/java/com/replaymod/render/gui/GuiVideoRenderer.java index cd9c6268..448cf5d0 100644 --- a/src/main/java/com/replaymod/render/gui/GuiVideoRenderer.java +++ b/src/main/java/com/replaymod/render/gui/GuiVideoRenderer.java @@ -28,7 +28,7 @@ import net.minecraft.client.texture.NativeImage; import java.nio.ByteBuffer; public class GuiVideoRenderer extends GuiScreen implements Tickable { - private static final Identifier NO_PREVIEW_TEXTURE = new Identifier("replaymod", "logo.jpg"); + private static final Identifier NO_PREVIEW_TEXTURE = new Identifier("replaymod", "logo.png"); private final VideoRenderer renderer; diff --git a/src/main/java/com/replaymod/render/mixin/ChunkRenderingDataPreparerAccessor.java b/src/main/java/com/replaymod/render/mixin/ChunkRenderingDataPreparerAccessor.java new file mode 100644 index 00000000..472c6be9 --- /dev/null +++ b/src/main/java/com/replaymod/render/mixin/ChunkRenderingDataPreparerAccessor.java @@ -0,0 +1 @@ +// 1.20.2+ diff --git a/src/main/java/com/replaymod/render/mixin/Mixin_BlockOnChunkRebuilds.java b/src/main/java/com/replaymod/render/mixin/Mixin_BlockOnChunkRebuilds.java index fdbdd826..579e3268 100644 --- a/src/main/java/com/replaymod/render/mixin/Mixin_BlockOnChunkRebuilds.java +++ b/src/main/java/com/replaymod/render/mixin/Mixin_BlockOnChunkRebuilds.java @@ -9,6 +9,7 @@ import net.minecraft.util.thread.TaskExecutor; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -20,12 +21,28 @@ import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; +//#if MC>=12003 +//$$ import net.minecraft.client.render.chunk.BlockBufferBuilderPool; +//#endif + @Mixin(ChunkBuilder.class) public abstract class Mixin_BlockOnChunkRebuilds implements ForceChunkLoadingHook.IBlockOnChunkRebuilds { + //#if MC>=12003 + //$$ @Shadow @Final private BlockBufferBuilderPool buffersPool; + //$$ @Unique + //$$ private int getAvailableBufferCount() { + //$$ return this.buffersPool.getAvailableBuilderCount(); + //$$ } + //#else @Shadow @Final private Queue threadBuffers; + @Unique + private int getAvailableBufferCount() { + return threadBuffers.size(); + } + //#endif //#if MC>=11800 - //$$ @org.spongepowered.asm.mixin.Unique + //$$ @Unique //$$ private boolean upload() { //$$ boolean anything = false; //$$ Runnable runnable; @@ -52,12 +69,12 @@ public abstract class Mixin_BlockOnChunkRebuilds implements ForceChunkLoadingHoo @Inject(method = "", at = @At("RETURN")) private void rememberTotalThreads(CallbackInfo ci) { - this.totalBufferCount = this.threadBuffers.size(); + this.totalBufferCount = getAvailableBufferCount(); } @Inject(method = "scheduleRunTasks", at = @At("RETURN")) private void notifyMainThreadIfEverythingIsDone(CallbackInfo ci) { - if (this.threadBuffers.size() == this.totalBufferCount) { + if (getAvailableBufferCount() == this.totalBufferCount) { // Looks like we're done, better notify the main thread in case the previous task didn't generate an upload this.waitingForWorkLock.lock(); try { @@ -84,7 +101,7 @@ public abstract class Mixin_BlockOnChunkRebuilds implements ForceChunkLoadingHoo private boolean waitForMainThreadWork() { boolean allDone = this.mailbox.ask(reply -> () -> { scheduleRunTasks(); - reply.send(this.threadBuffers.size() == this.totalBufferCount); + reply.send(getAvailableBufferCount() == this.totalBufferCount); }).join(); if (allDone) { diff --git a/src/main/java/com/replaymod/render/rendering/VideoRenderer.java b/src/main/java/com/replaymod/render/rendering/VideoRenderer.java index b1b57737..09743225 100644 --- a/src/main/java/com/replaymod/render/rendering/VideoRenderer.java +++ b/src/main/java/com/replaymod/render/rendering/VideoRenderer.java @@ -321,7 +321,11 @@ public class VideoRenderer implements RenderInfo { //#endif if (mc.options.debugEnabled) { debugInfoWasShown = true; + //#if MC>=12002 + //$$ mc.getDebugHud().toggleDebugHud(); + //#else mc.options.debugEnabled = false; + //#endif } //#if MC>=11400 if (mc.mouse.isCursorLocked()) { @@ -385,7 +389,13 @@ public class VideoRenderer implements RenderInfo { //$$ Display.setResizable(true); //$$ } //#endif - mc.options.debugEnabled = debugInfoWasShown; + if (debugInfoWasShown) { + //#if MC>=12002 + //$$ mc.getDebugHud().toggleDebugHud(); + //#else + mc.options.debugEnabled = true; + //#endif + } if (mouseWasGrabbed) { //#if MC>=11400 mc.mouse.lockCursor(); diff --git a/src/main/java/com/replaymod/render/utils/Lwjgl3Loader.java b/src/main/java/com/replaymod/render/utils/Lwjgl3Loader.java index 8c832acd..916a1d90 100644 --- a/src/main/java/com/replaymod/render/utils/Lwjgl3Loader.java +++ b/src/main/java/com/replaymod/render/utils/Lwjgl3Loader.java @@ -33,9 +33,15 @@ public class Lwjgl3Loader extends URLClassLoader { Path nativesDir = ReplayMod.instance.folders.getCacheFolder().resolve("lwjgl-natives"); Class configClass = Class.forName("org.lwjgl.system.Configuration", true, this); - Object extractDirField = configClass.getField("SHARED_LIBRARY_EXTRACT_DIRECTORY").get(null); Method setMethod = configClass.getMethod("set", Object.class); - setMethod.invoke(extractDirField, nativesDir.toAbsolutePath().toString()); + setMethod.invoke( + configClass.getField("SHARED_LIBRARY_EXTRACT_DIRECTORY").get(null), + nativesDir.toAbsolutePath().toString() + ); + setMethod.invoke( + configClass.getField("SHARED_LIBRARY_EXTRACT_PATH").get(null), + (Object) null + ); } private boolean canBeSharedWithMc(String name) { diff --git a/src/main/java/com/replaymod/replay/FullReplaySender.java b/src/main/java/com/replaymod/replay/FullReplaySender.java index f6c2af20..39ea79ec 100644 --- a/src/main/java/com/replaymod/replay/FullReplaySender.java +++ b/src/main/java/com/replaymod/replay/FullReplaySender.java @@ -10,6 +10,8 @@ import com.replaymod.core.mixin.TimerAccessor; import com.replaymod.core.utils.Restrictions; import com.replaymod.replay.camera.CameraEntity; import com.replaymod.replaystudio.io.ReplayInputStream; +import com.replaymod.replaystudio.lib.viaversion.api.protocol.packet.State; +import com.replaymod.replaystudio.protocol.PacketTypeRegistry; import com.replaymod.replaystudio.replay.ReplayFile; import de.johni0702.minecraft.gui.utils.EventRegistrations; import de.johni0702.minecraft.gui.versions.callbacks.PreTickCallback; @@ -48,7 +50,6 @@ import net.minecraft.network.packet.s2c.play.ParticleS2CPacket; import net.minecraft.network.packet.s2c.play.PlayerAbilitiesS2CPacket; import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket; import net.minecraft.network.packet.s2c.play.PlayerRespawnS2CPacket; -import net.minecraft.network.packet.s2c.play.PlayerSpawnS2CPacket; import net.minecraft.network.packet.s2c.play.SignEditorOpenS2CPacket; import net.minecraft.network.packet.s2c.play.StatisticsS2CPacket; import net.minecraft.text.Text; @@ -57,6 +58,14 @@ import net.minecraft.util.math.Vec3d; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +//#if MC>=12002 +//$$ import net.minecraft.network.packet.s2c.config.ReadyS2CPacket; +//$$ import net.minecraft.network.packet.s2c.play.CommonPlayerSpawnInfo; +//$$ import net.minecraft.network.packet.s2c.play.EnterReconfigurationS2CPacket; +//#else +import net.minecraft.network.packet.s2c.play.PlayerSpawnS2CPacket; +//#endif + //#if MC>=11904 //$$ import net.minecraft.network.packet.s2c.play.PositionFlag; //#endif @@ -238,9 +247,9 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend protected PacketData nextPacket; /** - * Whether we're currently reading packets from the login phase. + * Which protocol (state) we're currently in. */ - private boolean loginPhase = true; + private PacketTypeRegistry registry = getPacketTypeRegistry(State.LOGIN); /** * Whether we need to restart the current replay. E.g. when jumping backwards in time @@ -470,8 +479,10 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend int i = pb.readVarInt(); - NetworkState state = loginPhase ? NetworkState.LOGIN : NetworkState.PLAY; - //#if MC>=11700 + NetworkState state = asMc(registry.getState()); + //#if MC>=12002 + //$$ Packet p = state.getHandler(NetworkSide.CLIENTBOUND).createPacket(i, pb); + //#elseif MC>=11700 //$$ Packet p = state.getPacketHandler(NetworkSide.CLIENTBOUND, i, pb); //#else //#if MC>=10800 @@ -493,8 +504,10 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend return; // MC should have enough time to tick } - boolean relevantPacket = packet instanceof PlayerSpawnS2CPacket - || packet instanceof EntitySpawnS2CPacket + boolean relevantPacket = packet instanceof EntitySpawnS2CPacket + //#if MC<12002 + || packet instanceof PlayerSpawnS2CPacket + //#endif //#if MC<11900 || packet instanceof MobSpawnS2CPacket || packet instanceof PaintingSpawnS2CPacket @@ -557,9 +570,20 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend */ protected Packet processPacket(Packet p) throws Exception { if (p instanceof LoginSuccessS2CPacket) { - loginPhase = false; + registry = registry.withLoginSuccess(); return p; } + //#if MC>=12002 + //$$ if (p instanceof ReadyS2CPacket) { + //$$ registry = registry.withState(State.PLAY); + //$$ return p; + //$$ } + //$$ if (p instanceof EnterReconfigurationS2CPacket) { + //$$ registry = registry.withState(State.CONFIGURATION); + //$$ hasWorldLoaded = false; + //$$ return p; + //$$ } + //#endif if (p instanceof CustomPayloadS2CPacket) { CustomPayloadS2CPacket packet = (CustomPayloadS2CPacket) p; @@ -631,7 +655,11 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend if(p instanceof ResourcePackSendS2CPacket) { ResourcePackSendS2CPacket packet = (ResourcePackSendS2CPacket) p; + //#if MC>=12003 + //$$ String url = packet.url(); + //#else String url = packet.getURL(); + //#endif if (url.startsWith("replay://")) { //#else //$$ String url; @@ -647,7 +675,11 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend if (!file.exists()) { IOUtils.copy(replayFile.getResourcePack(hash).get(), new FileOutputStream(file)); } + //#if MC>=12003 + //$$ mc.getServerResourcePackProvider().addResourcePack(packet.id(), file.toPath()); + //#else setServerResourcePack(file); + //#endif } } return null; @@ -663,6 +695,17 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend //#if MC>=11400 p = new GameJoinS2CPacket( entId, + //#if MC>=12002 + //$$ packet.hardcore(), + //$$ packet.dimensionIds(), + //$$ packet.maxPlayers(), + //$$ packet.viewDistance(), + //$$ packet.simulationDistance(), + //$$ packet.reducedDebugInfo(), + //$$ packet.showDeathScreen(), + //$$ packet.doLimitedCrafting(), + //$$ withSpectatorMode(packet.commonPlayerSpawnInfo()) + //#else //#if MC>=11800 //$$ packet.hardcore(), //#endif @@ -719,6 +762,7 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend //#if MC>=12000 //$$ , packet.portalCooldown() //#endif + //#endif ); //#else //#if MC>=10800 @@ -758,6 +802,10 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend PlayerRespawnS2CPacket respawn = (PlayerRespawnS2CPacket) p; //#if MC>=11400 p = new PlayerRespawnS2CPacket( + //#if MC>=12002 + //$$ withSpectatorMode(respawn.commonPlayerSpawnInfo()), + //$$ (byte) 0 + //#else //#if MC>=11600 respawn.method_29445(), //#endif @@ -785,6 +833,7 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend //#if MC>=12000 //$$ , respawn.getPortalCooldown() //#endif + //#endif ); //#else //#if MC>=10809 @@ -916,6 +965,22 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend } } + //#if MC>=12002 + //$$ private CommonPlayerSpawnInfo withSpectatorMode(CommonPlayerSpawnInfo org) { + //$$ return new CommonPlayerSpawnInfo( + //$$ org.dimensionType(), + //$$ org.dimension(), + //$$ org.seed(), + //$$ GameMode.SPECTATOR, + //$$ GameMode.SPECTATOR, + //$$ org.isDebug(), + //$$ org.isFlat(), + //$$ org.lastDeathLocation(), + //$$ org.portalCooldown() + //$$ ); + //$$ } + //#endif + @Override @SuppressWarnings("unchecked") public void channelActive(ChannelHandlerContext ctx) throws Exception { @@ -1010,7 +1075,7 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend while (!terminate) { synchronized (FullReplaySender.this) { if (replayIn == null) { - replayIn = replayFile.getPacketData(getPacketTypeRegistry(true)); + replayIn = replayFile.getPacketData(getPacketTypeRegistry(State.LOGIN)); } // Packet loop while (true) { @@ -1037,7 +1102,7 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend // Read the next packet if we don't already have one if (nextPacket == null) { - nextPacket = new PacketData(replayIn, loginPhase); + nextPacket = new PacketData(replayIn); } int nextTimeStamp = nextPacket.timestamp; @@ -1060,6 +1125,17 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend lastTimeStamp = nextTimeStamp; + // MC as of 1.20.2 relies on autoRead, so it can update the connection state on the main + // thread before the next packet is read. As such, we need to stall if that was just + // enabled. + // Might be safe to do the same on older versions too, but I'd rather not poke the + // monster that is Forge networking. + //#if MC>=12002 + //$$ while (!ctx.channel().config().isAutoRead()) { + //$$ Thread.sleep(0, 100_000); + //$$ } + //#endif + // In case we finished jumping // We need to check that we aren't planing to restart so we don't accidentally run this // code before we actually restarted @@ -1092,7 +1168,7 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend // Restart the replay. hasWorldLoaded = false; lastTimeStamp = 0; - loginPhase = true; + registry = getPacketTypeRegistry(State.LOGIN); startFromBeginning = false; nextPacket = null; realTimeStart = System.currentTimeMillis(); @@ -1232,14 +1308,14 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend replayIn.close(); replayIn = null; } - loginPhase = true; + registry = getPacketTypeRegistry(State.LOGIN); startFromBeginning = false; nextPacket = null; ReplayMod.instance.runSync(replayHandler::restartedReplay); } if (replayIn == null) { - replayIn = replayFile.getPacketData(getPacketTypeRegistry(true)); + replayIn = replayFile.getPacketData(getPacketTypeRegistry(State.LOGIN)); } while (true) { // Send packets @@ -1251,7 +1327,7 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend nextPacket = null; } else { // Otherwise read one from the input stream - pd = new PacketData(replayIn, loginPhase); + pd = new PacketData(replayIn); } int nextTimeStamp = pd.timestamp; @@ -1263,6 +1339,17 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend // Process packet channelRead(ctx, pd.bytes); + + // MC as of 1.20.2 relies on autoRead, so it can update the connection state on the main + // thread before the next packet is read. As such, we need to stall if that was just + // enabled. + // Might be safe to do the same on older versions too, but I'd rather not poke the + // monster that is Forge networking. + //#if MC>=12002 + //$$ while (!ctx.channel().config().isAutoRead()) { + //$$ Thread.sleep(0, 100_000); + //$$ } + //#endif } catch (EOFException eof) { // Shit! We hit the end before finishing our job! What shall we do now? // well, let's just pretend we're done... @@ -1471,7 +1558,7 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend private final int timestamp; private final byte[] bytes; - PacketData(ReplayInputStream in, boolean loginPhase) throws IOException { + PacketData(ReplayInputStream in) throws IOException { if (ReplayMod.isMinimalMode()) { // Minimal mode, we can only read our exact protocol version and cannot use ReplayStudio timestamp = readInt(in); diff --git a/src/main/java/com/replaymod/replay/QuickReplaySender.java b/src/main/java/com/replaymod/replay/QuickReplaySender.java index 6592b47d..6641ddc5 100644 --- a/src/main/java/com/replaymod/replay/QuickReplaySender.java +++ b/src/main/java/com/replaymod/replay/QuickReplaySender.java @@ -7,6 +7,7 @@ import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; import com.replaymod.core.mixin.MinecraftAccessor; import com.replaymod.core.mixin.TimerAccessor; +import com.replaymod.replaystudio.lib.viaversion.api.protocol.packet.State; import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.rar.RandomAccessReplay; import de.johni0702.minecraft.gui.utils.EventRegistrations; @@ -30,6 +31,7 @@ import java.util.function.Consumer; import com.replaymod.core.utils.WrappedTimer; //#endif +import static com.replaymod.core.versions.MCVer.asMc; import static com.replaymod.core.versions.MCVer.getMinecraft; import static com.replaymod.core.versions.MCVer.getPacketTypeRegistry; import static com.replaymod.replay.ReplayModReplay.LOGGER; @@ -63,7 +65,7 @@ public class QuickReplaySender extends ChannelHandlerAdapter implements ReplaySe public QuickReplaySender(ReplayModReplay mod, ReplayFile replayFile) { this.mod = mod; - this.replay = new RandomAccessReplay(replayFile, getPacketTypeRegistry(false)) { + this.replay = new RandomAccessReplay(replayFile, getPacketTypeRegistry(State.PLAY)) { private byte[] buf = new byte[0]; @Override @@ -78,18 +80,21 @@ public class QuickReplaySender extends ChannelHandlerAdapter implements ReplaySe wrappedBuf.writerIndex(size); PacketByteBuf packetByteBuf = new PacketByteBuf(wrappedBuf); + NetworkState state = asMc(packet.getRegistry().getState()); //#if MC>=10809 Packet mcPacket; //#else //$$ Packet mcPacket; //#endif - //#if MC>=11700 - //$$ mcPacket = NetworkState.PLAY.getPacketHandler(NetworkSide.CLIENTBOUND, packet.getId(), packetByteBuf); + //#if MC>=12002 + //$$ mcPacket = state.getHandler(NetworkSide.CLIENTBOUND).createPacket(packet.getId(), packetByteBuf); + //#elseif MC>=11700 + //$$ mcPacket = state.getPacketHandler(NetworkSide.CLIENTBOUND, packet.getId(), packetByteBuf); //#elseif MC>=11500 - mcPacket = NetworkState.PLAY.getPacketHandler(NetworkSide.CLIENTBOUND, packet.getId()); + mcPacket = state.getPacketHandler(NetworkSide.CLIENTBOUND, packet.getId()); //#else //$$ try { - //$$ mcPacket = NetworkState.PLAY.getPacketHandler(NetworkSide.CLIENTBOUND, packet.getId()); + //$$ mcPacket = state.getPacketHandler(NetworkSide.CLIENTBOUND, packet.getId()); //$$ } catch (IllegalAccessException | InstantiationException e) { //$$ e.printStackTrace(); //$$ return; diff --git a/src/main/java/com/replaymod/replay/ReplayHandler.java b/src/main/java/com/replaymod/replay/ReplayHandler.java index 5718557b..15b14a57 100644 --- a/src/main/java/com/replaymod/replay/ReplayHandler.java +++ b/src/main/java/com/replaymod/replay/ReplayHandler.java @@ -42,6 +42,17 @@ import net.minecraft.network.ClientConnection; import java.io.IOException; import java.util.*; +//#if MC>=12003 +//$$ import net.minecraft.client.resource.server.ServerResourcePackManager; +//#endif + +//#if MC>=12002 +//$$ import io.netty.channel.ChannelDuplexHandler; +//$$ import io.netty.channel.ChannelPromise; +//$$ import net.minecraft.network.handler.NetworkStateTransitionHandler; +//$$ import net.minecraft.network.packet.Packet; +//#endif + //#if MC>=12000 //$$ import com.mojang.blaze3d.systems.VertexSorter; //$$ import net.minecraft.client.gui.DrawContext; @@ -301,6 +312,36 @@ public class ReplayHandler { //$$ FMLClientHandler.instance().connectToRealmsServer(null, 0); // just to init the playClientBlock latch //#endif + + //#if MC>=11200 + channel = new EmbeddedChannel(); + //#else + //$$ ChannelOutboundHandlerAdapter dummyHandler = new ChannelOutboundHandlerAdapter(); + //$$ channel = new EmbeddedChannel(dummyHandler); + //$$ channel.pipeline().remove(dummyHandler); + //#endif + //#if MC>=10800 + channel.pipeline().addLast("ReplayModReplay_quickReplaySender", quickReplaySender); + //#endif + channel.pipeline().addLast("ReplayModReplay_replaySender", fullReplaySender); + //#if MC>=12002 + //$$ channel.pipeline().addLast("ReplayModReplay_transition", new DummyNetworkStateTransitionHandler()); + //$$ channel.pipeline().addLast("bundler", new PacketBundler(ClientConnection.CLIENTBOUND_PROTOCOL_KEY)); + //#elseif MC>=11904 + //$$ channel.pipeline().addLast("bundler", new PacketBundler(NetworkSide.CLIENTBOUND)); + //#endif + channel.pipeline().addLast("packet_handler", networkManager); + channel.pipeline().fireChannelActive(); + + // MC usually transitions from handshake to login via the packets it sends. + // We don't send any packets (there is no server to receive them), so we need to switch manually. + //#if MC>=12002 + //$$ channel.attr(ClientConnection.CLIENTBOUND_PROTOCOL_KEY).set(NetworkState.LOGIN.getHandler(NetworkSide.CLIENTBOUND)); + //$$ channel.attr(ClientConnection.SERVERBOUND_PROTOCOL_KEY).set(NetworkState.LOGIN.getHandler(NetworkSide.SERVERBOUND)); + //#else + networkManager.setState(NetworkState.LOGIN); + //#endif + networkManager.setPacketListener(new ClientLoginNetworkHandler( networkManager, mc, @@ -315,31 +356,13 @@ public class ReplayHandler { //#endif )); - - //#if MC>=11200 - channel = new EmbeddedChannel(); - //#else - //$$ ChannelOutboundHandlerAdapter dummyHandler = new ChannelOutboundHandlerAdapter(); - //$$ channel = new EmbeddedChannel(dummyHandler); - //$$ channel.pipeline().remove(dummyHandler); - //#endif - //#if MC>=10800 - channel.pipeline().addLast("ReplayModReplay_quickReplaySender", quickReplaySender); - //#endif - channel.pipeline().addLast("ReplayModReplay_replaySender", fullReplaySender); - //#if MC>=11904 - //$$ channel.pipeline().addLast("bundler", new PacketBundler(NetworkSide.CLIENTBOUND)); - //#endif - channel.pipeline().addLast("packet_handler", networkManager); - channel.pipeline().fireChannelActive(); - - // MC usually transitions from handshake to login via the packets it sends. - // We don't send any packets (there is no server to receive them), so we need to switch manually. - networkManager.setState(NetworkState.LOGIN); - //#if MC>=11400 ((MinecraftAccessor) mc).setConnection(networkManager); //#endif + + //#if MC>=12003 + //$$ mc.getServerResourcePackProvider().init(networkManager, ServerResourcePackManager.AcceptanceStatus.ALLOWED); + //#endif } public ReplayFile getReplayFile() { @@ -787,4 +810,24 @@ public class ReplayHandler { //$$ } //#endif } + + //#if MC>=12002 + //$$ private static class DummyNetworkStateTransitionHandler extends ChannelDuplexHandler { + //$$ @Override + //$$ public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + //$$ if (msg instanceof Packet packet) { + //$$ NetworkStateTransitionHandler.handle(ctx.channel().attr(ClientConnection.CLIENTBOUND_PROTOCOL_KEY), packet); + //$$ } + //$$ super.channelRead(ctx, msg); + //$$ } + //$$ + //$$ @Override + //$$ public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception { + //$$ if (msg instanceof Packet packet) { + //$$ NetworkStateTransitionHandler.handle(ctx.channel().attr(ClientConnection.SERVERBOUND_PROTOCOL_KEY), packet); + //$$ } + //$$ super.write(ctx, msg, promise); + //$$ } + //$$ } + //#endif } diff --git a/src/main/java/com/replaymod/replay/camera/CameraEntity.java b/src/main/java/com/replaymod/replay/camera/CameraEntity.java index aef121e9..f6c22940 100644 --- a/src/main/java/com/replaymod/replay/camera/CameraEntity.java +++ b/src/main/java/com/replaymod/replay/camera/CameraEntity.java @@ -40,6 +40,10 @@ import net.minecraft.util.math.Box; //$$ import net.minecraftforge.eventbus.api.SubscribeEvent; //#endif +//#if MC>=12002 +//$$ import net.minecraft.client.util.SkinTextures; +//#endif + //#if MC>=11400 import net.minecraft.client.world.ClientWorld; import net.minecraft.fluid.Fluid; @@ -475,6 +479,16 @@ public class CameraEntity return super.isInvisible(); } + //#if MC>=12002 + //$$ @Override + //$$ public SkinTextures getSkinTextures() { + //$$ Entity view = this.client.getCameraEntity(); + //$$ if (view != this && view instanceof AbstractClientPlayerEntity) { + //$$ return ((AbstractClientPlayerEntity) view).getSkinTextures(); + //$$ } + //$$ return super.getSkinTextures(); + //$$ } + //#else @Override public Identifier getSkinTexture() { Entity view = this.client.getCameraEntity(); @@ -493,7 +507,10 @@ public class CameraEntity } return super.getModel(); } + //#endif + //#endif + //#if MC>=10800 @Override public boolean isPartVisible(PlayerModelPart modelPart) { Entity view = this.client.getCameraEntity(); diff --git a/src/main/java/com/replaymod/replay/mixin/entity_tracking/Mixin_FixPartialUpdates.java b/src/main/java/com/replaymod/replay/mixin/entity_tracking/Mixin_FixPartialUpdates.java index 2ffd980a..c86350e5 100644 --- a/src/main/java/com/replaymod/replay/mixin/entity_tracking/Mixin_FixPartialUpdates.java +++ b/src/main/java/com/replaymod/replay/mixin/entity_tracking/Mixin_FixPartialUpdates.java @@ -59,20 +59,24 @@ public class Mixin_FixPartialUpdates { // // Use correct position for rotation-only updates // + // Except for the special case of the entity riding another entity because of a second vanilla bug where it won't + // send tracked position updates to the client while the entity is riding... + // Nothing we can do in that case, fixing that would require modifying the server. + // @Redirect(method = "onEntityUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;getX()D")) private double getTrackedX(Entity instance) { - return instance.getTrackedPosition().getX(); + return instance.hasVehicle() ? instance.getX() : instance.getTrackedPosition().getX(); } @Redirect(method = "onEntityUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;getY()D")) private double getTrackedY(Entity instance) { - return instance.getTrackedPosition().getY(); + return instance.hasVehicle() ? instance.getY() : instance.getTrackedPosition().getY(); } @Redirect(method = "onEntityUpdate", at = @At(value = "INVOKE", target = "Lnet/minecraft/entity/Entity;getZ()D")) private double getTrackedZ(Entity instance) { - return instance.getTrackedPosition().getZ(); + return instance.hasVehicle() ? instance.getZ() : instance.getTrackedPosition().getZ(); } //#endif diff --git a/src/main/resources/assets/replaymod/logo.jpg b/src/main/resources/assets/replaymod/logo.jpg deleted file mode 100644 index fbe12762..00000000 Binary files a/src/main/resources/assets/replaymod/logo.jpg and /dev/null differ diff --git a/src/main/resources/assets/replaymod/logo.png b/src/main/resources/assets/replaymod/logo.png new file mode 100644 index 00000000..c9abe1dd Binary files /dev/null and b/src/main/resources/assets/replaymod/logo.png differ diff --git a/src/main/resources/default_thumb.jpg b/src/main/resources/default_thumb.jpg deleted file mode 100644 index fbe12762..00000000 Binary files a/src/main/resources/default_thumb.jpg and /dev/null differ diff --git a/src/main/resources/default_thumb.png b/src/main/resources/default_thumb.png new file mode 100644 index 00000000..8edcab42 Binary files /dev/null and b/src/main/resources/default_thumb.png differ diff --git a/src/main/resources/mixins.render.replaymod.json b/src/main/resources/mixins.render.replaymod.json index 2759769b..bd3761e4 100644 --- a/src/main/resources/mixins.render.replaymod.json +++ b/src/main/resources/mixins.render.replaymod.json @@ -5,8 +5,12 @@ "server": [], "client": [ //#if MC>=11800 + //#if MC>=12002 + //$$ "ChunkRenderingDataPreparerAccessor", + //#else //$$ "ChunkInfoAccessor", //#endif + //#endif "Mixin_ChromaKeyColorSky", "Mixin_ChromaKeyDisableFog", "Mixin_ChromaKeyForceSky", diff --git a/version.txt b/version.txt index 6ac04ca9..ec4704a2 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.6.13 +2.6.14 diff --git a/versions/1.18.1/src/main/java/com/replaymod/render/mixin/ChunkInfoAccessor.java b/versions/1.18.1/src/main/java/com/replaymod/render/mixin/ChunkInfoAccessor.java index c4fbdd96..d20bd726 100644 --- a/versions/1.18.1/src/main/java/com/replaymod/render/mixin/ChunkInfoAccessor.java +++ b/versions/1.18.1/src/main/java/com/replaymod/render/mixin/ChunkInfoAccessor.java @@ -1,4 +1,4 @@ -// 1.18+ +// 1.18 - 1.20.1 package com.replaymod.render.mixin; import net.minecraft.client.render.chunk.ChunkBuilder; diff --git a/versions/1.20.2/logs/latest.log b/versions/1.20.2/logs/latest.log new file mode 100644 index 00000000..e69de29b diff --git a/versions/1.20.2/src/main/java/com/replaymod/render/mixin/ChunkInfoAccessor.java b/versions/1.20.2/src/main/java/com/replaymod/render/mixin/ChunkInfoAccessor.java new file mode 100644 index 00000000..42f3f3b7 --- /dev/null +++ b/versions/1.20.2/src/main/java/com/replaymod/render/mixin/ChunkInfoAccessor.java @@ -0,0 +1 @@ +// 1.18 - 1.20.1 diff --git a/versions/1.20.2/src/main/java/com/replaymod/render/mixin/ChunkRenderingDataPreparerAccessor.java b/versions/1.20.2/src/main/java/com/replaymod/render/mixin/ChunkRenderingDataPreparerAccessor.java new file mode 100644 index 00000000..986d08d1 --- /dev/null +++ b/versions/1.20.2/src/main/java/com/replaymod/render/mixin/ChunkRenderingDataPreparerAccessor.java @@ -0,0 +1,21 @@ +package com.replaymod.render.mixin; + +import net.minecraft.client.render.BuiltChunkStorage; +import net.minecraft.client.render.ChunkRenderingDataPreparer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.concurrent.Future; +import java.util.concurrent.atomic.AtomicBoolean; + +@Mixin(ChunkRenderingDataPreparer.class) +public interface ChunkRenderingDataPreparerAccessor { + @Accessor("field_45623") + BuiltChunkStorage builtChunkStorage(); + + @Accessor("field_45621") + boolean shouldUpdate(); + + @Accessor("field_45622") + Future fullUpdateFuture(); +} diff --git a/versions/1.20.2/src/main/java/com/replaymod/render/mixin/Mixin_ForceChunkLoading.java b/versions/1.20.2/src/main/java/com/replaymod/render/mixin/Mixin_ForceChunkLoading.java new file mode 100644 index 00000000..4843023e --- /dev/null +++ b/versions/1.20.2/src/main/java/com/replaymod/render/mixin/Mixin_ForceChunkLoading.java @@ -0,0 +1,119 @@ +package com.replaymod.render.mixin; + +import com.replaymod.render.hooks.ForceChunkLoadingHook; +import com.replaymod.render.hooks.IForceChunkLoading; +import com.replaymod.render.utils.FlawlessFrames; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.ChunkRenderingDataPreparer; +import net.minecraft.client.render.Frustum; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.render.LightmapTextureManager; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.client.render.chunk.ChunkBuilder; +import net.minecraft.client.render.chunk.ChunkRendererRegionBuilder; +import net.minecraft.client.util.math.MatrixStack; +import org.joml.Matrix4f; +import org.spongepowered.asm.mixin.Final; +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 java.util.concurrent.ExecutionException; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +@Mixin(WorldRenderer.class) +public abstract class Mixin_ForceChunkLoading implements IForceChunkLoading { + private ForceChunkLoadingHook replayModRender_hook; + + @Override + public void replayModRender_setHook(ForceChunkLoadingHook hook) { + this.replayModRender_hook = hook; + } + + @Shadow private ChunkBuilder field_45614; + + @Shadow @Final private ChunkRenderingDataPreparer field_45615; + + @Shadow protected abstract void setupTerrain(Camera par1, Frustum par2, boolean par3, boolean par4); + + @Shadow private Frustum frustum; + + @Shadow private Frustum capturedFrustum; + + @Shadow @Final private MinecraftClient client; + + @Shadow protected abstract void applyFrustum(Frustum par1); + + @Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/WorldRenderer;setupTerrain(Lnet/minecraft/client/render/Camera;Lnet/minecraft/client/render/Frustum;ZZ)V")) + private void forceAllChunks(MatrixStack matrices, float tickDelta, long limitTime, boolean renderBlockOutline, Camera camera, GameRenderer gameRenderer, LightmapTextureManager lightmapTextureManager, Matrix4f matrix4f, CallbackInfo ci) { + if (replayModRender_hook == null) { + return; + } + if (FlawlessFrames.hasSodium()) { + return; + } + + assert this.client.player != null; + + ChunkRenderingDataPreparer renderingData = this.field_45615; + ChunkRenderingDataPreparerAccessor renderingDataAcc = (ChunkRenderingDataPreparerAccessor) renderingData; + ChunkRendererRegionBuilder chunkRendererRegionBuilder = new ChunkRendererRegionBuilder(); + + do { + boolean areWeDoneYet = true; + + // Determine which chunks shall be visible + setupTerrain(camera, this.frustum, this.capturedFrustum != null, this.client.player.isSpectator()); + + // Wait for async processing to be complete + Future fullUpdateFuture = renderingDataAcc.fullUpdateFuture(); + if (fullUpdateFuture != null) { + try { + fullUpdateFuture.get(5, TimeUnit.SECONDS); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + return; + } catch (ExecutionException e) { + throw new RuntimeException(e); + } catch (TimeoutException e) { + e.printStackTrace(); + } + } + + // If that async processing did change the chunk graph, we need to re-apply the frustum (otherwise this is + // only done in the next setupTerrain call, which not happen this frame) + if (renderingData.method_52836()) { + this.applyFrustum((new Frustum(frustum)).coverBoxAroundSetPosition(8)); // call based on the one in setupTerrain + } + + // Schedule all chunks which need rebuilding (we schedule even important rebuilds because we wait for + // all of them anyway and this way we can take advantage of threading) + for (ChunkBuilder.BuiltChunk builtChunk : renderingDataAcc.builtChunkStorage().chunks) { + if (!builtChunk.needsRebuild()) { + continue; + } + // MC sometimes schedules invalid chunks when you're outside of loaded chunks (e.g. y > 256) + if (builtChunk.shouldBuild()) { + builtChunk.scheduleRebuild(this.field_45614, chunkRendererRegionBuilder); + areWeDoneYet = false; + } + builtChunk.cancelRebuild(); + } + + // Upload all chunks + if (((ForceChunkLoadingHook.IBlockOnChunkRebuilds) this.field_45614).uploadEverythingBlocking()) { + areWeDoneYet = false; + } + + // Repeat until no more updates are needed + if (!areWeDoneYet) { + renderingData.method_52817(); // sets shouldUpdate to true + } + } while (renderingDataAcc.shouldUpdate()); + } +} diff --git a/versions/1.20.2/src/main/java/com/replaymod/replay/mixin/entity_tracking/Mixin_FixPartialUpdates.java b/versions/1.20.2/src/main/java/com/replaymod/replay/mixin/entity_tracking/Mixin_FixPartialUpdates.java new file mode 100644 index 00000000..8dbc9b05 --- /dev/null +++ b/versions/1.20.2/src/main/java/com/replaymod/replay/mixin/entity_tracking/Mixin_FixPartialUpdates.java @@ -0,0 +1,9 @@ +package com.replaymod.replay.mixin.entity_tracking; + +import net.minecraft.client.network.ClientPlayNetworkHandler; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(ClientPlayNetworkHandler.class) +public class Mixin_FixPartialUpdates { + // Looks like this has finally been fixed in 1.20.2 +} diff --git a/versions/1.20.4/src/main/java/com/replaymod/recording/mixin/MixinDownloadingPackFinder.java b/versions/1.20.4/src/main/java/com/replaymod/recording/mixin/MixinDownloadingPackFinder.java new file mode 100644 index 00000000..7dc67921 --- /dev/null +++ b/versions/1.20.4/src/main/java/com/replaymod/recording/mixin/MixinDownloadingPackFinder.java @@ -0,0 +1,29 @@ +package com.replaymod.recording.mixin; + +import com.replaymod.recording.ReplayModRecording; +import com.replaymod.recording.packet.PacketListener; +import net.minecraft.client.resource.server.ServerResourcePackManager; +import net.minecraft.util.Downloader; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Coerce; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.nio.file.Path; +import java.util.Map; +import java.util.UUID; + +@Mixin(ServerResourcePackManager.class) +public abstract class MixinDownloadingPackFinder { + @Inject(method = "onDownload", at = @At("HEAD")) + private void recordDownloadedPack(@Coerce Object packs, Downloader.DownloadResult result, CallbackInfo ci) { + PacketListener packetListener = ReplayModRecording.instance.getConnectionEventHandler().getPacketListener(); + if (packetListener == null) { + return; + } + for (Map.Entry entry : result.downloaded().entrySet()) { + packetListener.getResourcePackRecorder().recordResourcePack(entry.getValue(), entry.getKey()); + } + } +} diff --git a/versions/mapping-fabric-1.20.4-1.20.2.txt b/versions/mapping-fabric-1.20.4-1.20.2.txt new file mode 100644 index 00000000..91036156 --- /dev/null +++ b/versions/mapping-fabric-1.20.4-1.20.2.txt @@ -0,0 +1 @@ +net.minecraft.text.PlainTextContent net.minecraft.text.LiteralTextContent