diff --git a/build.gradle b/build.gradle index f0661b4e..813b9460 100644 --- a/build.gradle +++ b/build.gradle @@ -264,6 +264,7 @@ dependencies { 11903: '1.19.3-rc3', 11904: '1.19.4', 12001: '1.20.1', + 12002: '1.20.2', ][mcVersion] mappings 'net.fabricmc:yarn:' + [ 11404: '1.14.4+build.16', @@ -281,6 +282,7 @@ 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', ][mcVersion] modImplementation 'net.fabricmc:fabric-loader:0.14.11' def fabricApiVersion = [ @@ -299,6 +301,7 @@ dependencies { 11903: '0.68.1+1.19.3', 11904: '0.76.0+1.19.4', 12001: '0.83.1+1.20.1', + 12002: '0.90.7+1.20.2', ][mcVersion] def fabricApiModules = [ "api-base", @@ -379,7 +382,7 @@ dependencies { shadow 'com.github.ReplayMod.JavaBlend:2.79.0:a0696f8' - shadow "com.github.ReplayMod:ReplayStudio:a1e2b83", shadeExclusions + shadow "com.github.ReplayMod:ReplayStudio:e7a8ff7", 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 +393,9 @@ dependencies { shadow 'com.github.ReplayMod:lwjgl-utils:27dcd66' if (FABRIC) { - if (mcVersion >= 12000) { + 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..d4f6bc60 160000 --- a/jGui +++ b/jGui @@ -1 +1 @@ -Subproject commit eb3297f5a62f3b4c8b8445be85b95205d54763b7 +Subproject commit d4f6bc60c9f4368a4db1465465dfd1b9d2a06430 diff --git a/root.gradle.kts b/root.gradle.kts index 522c0c30..632364cd 100755 --- a/root.gradle.kts +++ b/root.gradle.kts @@ -193,6 +193,7 @@ val doRelease by tasks.registering { defaultTasks("bundleJar") preprocess { + 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 +219,7 @@ preprocess { val mc10800 = createNode("1.8", 10800, "srg") val mc10710 = createNode("1.7.10", 10710, "srg") + 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..05dbe159 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -38,6 +38,7 @@ val jGuiVersions = listOf( "1.19.3", "1.19.4", "1.20.1", + "1.20.2", ) val replayModVersions = listOf( // "1.7.10", @@ -64,6 +65,7 @@ val replayModVersions = listOf( "1.19.3", "1.19.4", "1.20.1", + "1.20.2", ) 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/versions/MCVer.java b/src/main/java/com/replaymod/core/versions/MCVer.java index ff348bfa..a8cbb328 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()), 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/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..6f06a823 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; @@ -93,6 +99,9 @@ public class PacketListener extends ChannelInboundHandlerAdapter { 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>=12002 + //$$ private static final int PACKET_ID_CONFIG_RESOURCE_PACK_SEND = getPacketId(NetworkState.CONFIGURATION, new ResourcePackSendS2CPacket("", "", false, null)); + //#endif //#if MC<10904 //$$ private static final int PACKET_ID_PLAY_COMPRESSION = getPacketId(EnumConnectionState.PLAY, new S46PacketSetCompressionLevel()); //#endif @@ -152,7 +161,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 +356,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,7 +381,11 @@ 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))); return; @@ -385,8 +402,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 +424,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 +438,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 +463,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) ); @@ -503,12 +527,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/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/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/replay/FullReplaySender.java b/src/main/java/com/replaymod/replay/FullReplaySender.java index f6c2af20..7272c7a4 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; @@ -663,6 +687,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 +754,7 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend //#if MC>=12000 //$$ , packet.portalCooldown() //#endif + //#endif ); //#else //#if MC>=10800 @@ -758,6 +794,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 +825,7 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend //#if MC>=12000 //$$ , respawn.getPortalCooldown() //#endif + //#endif ); //#else //#if MC>=10809 @@ -916,6 +957,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 +1067,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 +1094,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 +1117,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 +1160,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 +1300,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 +1319,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 +1331,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 +1550,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..c0475556 100644 --- a/src/main/java/com/replaymod/replay/ReplayHandler.java +++ b/src/main/java/com/replaymod/replay/ReplayHandler.java @@ -42,6 +42,13 @@ import net.minecraft.network.ClientConnection; import java.io.IOException; import java.util.*; +//#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 +308,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,28 +352,6 @@ 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 @@ -787,4 +802,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/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/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/mapping-fabric-1.20.3-1.20.2.txt b/versions/mapping-fabric-1.20.3-1.20.2.txt new file mode 100644 index 00000000..e69de29b