From 42aef017c19fbed4877cdeb037956f1b274d2b30 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 17 Jul 2022 16:02:41 +0200 Subject: [PATCH 01/19] Fix skin in Player Overview and first person on 1.8.9 (fixes #749) No clue why `getResourceLocationForPlayerUUID` was implemented the way it was, that commit dates back to 2015 (332c36a), but if the skin isn't loaded yet, it won't load it, so sometimes it just won't show properly in first person and in the Player Overview. This commit just calls `AbstractClientPlayer.getSkinTexture` instead. Unclear which MC versions are affected. At some point before 1.16.4 something else was introduced which loads the skin for us, hence why modern versions were not affected. --- .../java/com/replaymod/core/utils/Utils.java | 30 ------------------- .../playeroverview/PlayerOverviewGui.java | 5 ++-- .../replaymod/replay/camera/CameraEntity.java | 4 +-- 3 files changed, 5 insertions(+), 34 deletions(-) diff --git a/src/main/java/com/replaymod/core/utils/Utils.java b/src/main/java/com/replaymod/core/utils/Utils.java index 1b518534..826f78fa 100644 --- a/src/main/java/com/replaymod/core/utils/Utils.java +++ b/src/main/java/com/replaymod/core/utils/Utils.java @@ -1,7 +1,6 @@ package com.replaymod.core.utils; import com.google.common.base.Throwables; -import com.google.common.collect.Iterables; import com.google.common.net.PercentEscaper; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; @@ -27,7 +26,6 @@ import de.johni0702.minecraft.gui.versions.Image; import de.johni0702.minecraft.gui.versions.MCVer; import net.minecraft.client.gui.screen.Screen; import net.minecraft.util.crash.CrashReport; -import net.minecraft.util.Identifier; import org.apache.commons.io.Charsets; import org.apache.commons.io.FilenameUtils; import org.apache.logging.log4j.LogManager; @@ -38,15 +36,6 @@ import org.apache.logging.log4j.Logger; //$$ import org.lwjgl.input.Keyboard; //#endif -//#if MC>=10800 -import net.minecraft.client.network.PlayerListEntry; -import net.minecraft.client.util.DefaultSkinHelper; -//#else -//$$ import net.minecraft.client.Minecraft; -//$$ import net.minecraft.client.entity.AbstractClientPlayer; -//$$ import net.minecraft.entity.player.EntityPlayer; -//#endif - import javax.annotation.Nonnull; import javax.annotation.Nullable; import javax.net.ssl.SSLContext; @@ -233,25 +222,6 @@ public class Utils { } } - public static Identifier getResourceLocationForPlayerUUID(UUID uuid) { - //#if MC>=10800 - PlayerListEntry info = getMinecraft().getNetworkHandler().getPlayerListEntry(uuid); - Identifier skinLocation; - if (info != null && info.hasSkinTexture()) { - skinLocation = info.getSkinTexture(); - } else { - skinLocation = DefaultSkinHelper.getTexture(uuid); - } - return skinLocation; - //#else - //$$ EntityPlayer player = Minecraft.getMinecraft().theWorld.getPlayerEntityByUUID(uuid); - //$$ if (player != null || !(player instanceof AbstractClientPlayer)) { - //$$ return AbstractClientPlayer.locationStevePng; - //$$ } - //$$ return ((AbstractClientPlayer) player).getLocationSkin(); - //#endif - } - public static boolean isCtrlDown() { //#if MC>=11400 return Screen.hasControlDown(); diff --git a/src/main/java/com/replaymod/extras/playeroverview/PlayerOverviewGui.java b/src/main/java/com/replaymod/extras/playeroverview/PlayerOverviewGui.java index 01bf1f8f..78ed336e 100644 --- a/src/main/java/com/replaymod/extras/playeroverview/PlayerOverviewGui.java +++ b/src/main/java/com/replaymod/extras/playeroverview/PlayerOverviewGui.java @@ -1,6 +1,5 @@ package com.replaymod.extras.playeroverview; -import com.replaymod.core.utils.Utils; import com.replaymod.replay.ReplayModReplay; import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.RenderInfo; @@ -20,6 +19,7 @@ import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.lwjgl.Dimension; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; +import net.minecraft.client.network.AbstractClientPlayerEntity; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.util.Identifier; @@ -94,7 +94,8 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable { Collections.sort(players, new PlayerComparator()); // Sort by name, spectators last for (final PlayerEntity p : players) { - final Identifier texture = Utils.getResourceLocationForPlayerUUID(p.getUuid()); + if (!(p instanceof AbstractClientPlayerEntity)) continue; + final Identifier texture = ((AbstractClientPlayerEntity) p).getSkinTexture(); final GuiClickable panel = new GuiClickable().setLayout(new HorizontalLayout().setSpacing(2)).addElements( new HorizontalLayout.Data(0.5), new GuiImage() { @Override diff --git a/src/main/java/com/replaymod/replay/camera/CameraEntity.java b/src/main/java/com/replaymod/replay/camera/CameraEntity.java index 797c0ed9..f9c6fe53 100644 --- a/src/main/java/com/replaymod/replay/camera/CameraEntity.java +++ b/src/main/java/com/replaymod/replay/camera/CameraEntity.java @@ -472,8 +472,8 @@ public class CameraEntity @Override public Identifier getSkinTexture() { Entity view = this.client.getCameraEntity(); - if (view != this && view instanceof PlayerEntity) { - return Utils.getResourceLocationForPlayerUUID(view.getUuid()); + if (view != this && view instanceof AbstractClientPlayerEntity) { + return ((AbstractClientPlayerEntity) view).getSkinTexture(); } return super.getSkinTexture(); } From 633ac10d8927be0dc5d97908ebba35b012dba689 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 17 Jul 2022 16:25:23 +0200 Subject: [PATCH 02/19] Fix main hand in first person spectator view (fixes #731) --- .../com/replaymod/replay/camera/CameraEntity.java | 12 ++++++++++++ versions/1.14.4-forge/mapping.txt | 1 + 2 files changed, 13 insertions(+) diff --git a/src/main/java/com/replaymod/replay/camera/CameraEntity.java b/src/main/java/com/replaymod/replay/camera/CameraEntity.java index f9c6fe53..19ca60cd 100644 --- a/src/main/java/com/replaymod/replay/camera/CameraEntity.java +++ b/src/main/java/com/replaymod/replay/camera/CameraEntity.java @@ -73,6 +73,7 @@ import net.minecraft.client.recipebook.ClientRecipeBook; //$$ import net.minecraft.stats.RecipeBook; //#endif //#endif +import net.minecraft.util.Arm; import net.minecraft.util.Hand; //#endif @@ -498,6 +499,17 @@ public class CameraEntity } //#endif + //#if MC>=10904 + @Override + public Arm getMainArm() { + Entity view = this.client.getCameraEntity(); + if (view != this && view instanceof PlayerEntity) { + return ((PlayerEntity) view).getMainArm(); + } + return super.getMainArm(); + } + //#endif + @Override public float getHandSwingProgress(float renderPartialTicks) { Entity view = this.client.getCameraEntity(); diff --git a/versions/1.14.4-forge/mapping.txt b/versions/1.14.4-forge/mapping.txt index 3bd9a84e..90dff9e6 100644 --- a/versions/1.14.4-forge/mapping.txt +++ b/versions/1.14.4-forge/mapping.txt @@ -23,6 +23,7 @@ org.lwjgl.glfw.GLFW com.replaymod.core.versions.GLFW net.minecraft.client.MainWindow com.replaymod.core.versions.Window net.minecraft.client.audio.SimpleSound net.minecraft.client.audio.PositionedSoundRecord net.minecraft.client.gui.IngameGui net.minecraft.client.gui.GuiIngame +net.minecraft.util.HandSide net.minecraft.util.EnumHandSide net.minecraft.resources.FolderPack getInputStream() getInputStreamByName() net.minecraft.client.gui.GuiYesNoCallback confirmResult() confirmClicked() From 6e3d30e41be915193874455411a0866d26dc1aca Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 17 Jul 2022 16:28:56 +0200 Subject: [PATCH 03/19] Fix thread unsafety in OpenGlToBitmapProcessor Sharing the temporary `row` and `rowSwap` buffers between different calls to the processor is not safe because it will be used by multiple threads. As a result some of the rows in the image could randomly get corrupted. This commit gets rid of the cached buffers (the value of which questionable anyway) and simply re-uses the same code which other processors use. --- .../processor/OpenGlToBitmapProcessor.java | 37 +++++-------------- 1 file changed, 10 insertions(+), 27 deletions(-) diff --git a/src/main/java/com/replaymod/render/processor/OpenGlToBitmapProcessor.java b/src/main/java/com/replaymod/render/processor/OpenGlToBitmapProcessor.java index 6cb60f5e..8740716d 100644 --- a/src/main/java/com/replaymod/render/processor/OpenGlToBitmapProcessor.java +++ b/src/main/java/com/replaymod/render/processor/OpenGlToBitmapProcessor.java @@ -2,42 +2,25 @@ package com.replaymod.render.processor; import com.replaymod.render.frame.OpenGlFrame; import com.replaymod.render.frame.BitmapFrame; +import com.replaymod.render.utils.ByteBufferPool; +import de.johni0702.minecraft.gui.utils.lwjgl.Dimension; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; import java.nio.ByteBuffer; -public class OpenGlToBitmapProcessor extends AbstractFrameProcessor { +import static com.replaymod.render.utils.Utils.openGlBytesToBitmap; - private byte[] row, rowSwap; +public class OpenGlToBitmapProcessor extends AbstractFrameProcessor { @Override public BitmapFrame process(OpenGlFrame rawFrame) { - // Flip whole image in place - ReadableDimension size = rawFrame.getSize(); + int width = size.getWidth(); + int height = size.getHeight(); int bpp = rawFrame.getBytesPerPixel(); - int rowSize = size.getWidth() * bpp; - if (row == null || row.length < rowSize) { - row = new byte[rowSize]; - rowSwap = new byte[rowSize]; - } - ByteBuffer buffer = rawFrame.getByteBuffer(); - int rows = size.getHeight(); - byte[] row = this.row; - byte[] rowSwap = this.rowSwap; - for (int i = 0; i < rows / 2; i++) { - int from = rowSize * i; - int to = rowSize * (rows - i - 1); - buffer.position(from); - buffer.get(row); - buffer.position(to); - buffer.get(rowSwap); - buffer.position(to); - buffer.put(row); - buffer.position(from); - buffer.put(rowSwap); - } - buffer.rewind(); - return new BitmapFrame(rawFrame.getFrameId(), size, bpp, buffer); + ByteBuffer result = ByteBufferPool.allocate(width * height * bpp); + openGlBytesToBitmap(rawFrame, 0, 0, result, width); + ByteBufferPool.release(rawFrame.getByteBuffer()); + return new BitmapFrame(rawFrame.getFrameId(), new Dimension(width, height), bpp, result); } } From 738320e8969f61aecaf0885a2737a1103946e594 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 17 Jul 2022 23:03:44 +0200 Subject: [PATCH 04/19] Update translations --- src/main/resources/assets/replaymod/lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/replaymod/lang b/src/main/resources/assets/replaymod/lang index 7614b69e..d8beaa51 160000 --- a/src/main/resources/assets/replaymod/lang +++ b/src/main/resources/assets/replaymod/lang @@ -1 +1 @@ -Subproject commit 7614b69e249eb728d219c02c3c71effcc5fc00a2 +Subproject commit d8beaa51ae2966214df6d5cebec0307475fcec60 From 05387b0ac71eb28a5da88cbb709c42b23c510996 Mon Sep 17 00:00:00 2001 From: bela333 Date: Tue, 21 Jun 2022 18:01:56 +0200 Subject: [PATCH 05/19] Modified FREX notice --- src/main/java/com/replaymod/render/rendering/VideoRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/replaymod/render/rendering/VideoRenderer.java b/src/main/java/com/replaymod/render/rendering/VideoRenderer.java index 92021f8b..6d554e28 100644 --- a/src/main/java/com/replaymod/render/rendering/VideoRenderer.java +++ b/src/main/java/com/replaymod/render/rendering/VideoRenderer.java @@ -651,7 +651,7 @@ public class VideoRenderer implements RenderInfo { return new String[] { "Rendering is not supported with your Sodium version.", "It is missing support for the FREX Flawless Frames API.", - "Either update to the latest version or uninstall Sodium before rendering!", + "Either use the Sodium build from replaymod.com or uninstall Sodium before rendering!", }; } //#if MC>=11700 From f0299fed66995fba7ee733a1cf11150bb3e30983 Mon Sep 17 00:00:00 2001 From: bela333 Date: Tue, 21 Jun 2022 18:18:25 +0200 Subject: [PATCH 06/19] Update documentation to include compatible Sodium --- docs/content.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/content.md b/docs/content.md index 0681006f..e767cb76 100755 --- a/docs/content.md +++ b/docs/content.md @@ -619,7 +619,7 @@ The _Baritone_ mod can cause a crash when you're trying to load a replay. If you Minecraft may crash if you try to use _RandomPatches_ together with ReplayMod. Try removing RandomPatches if Minecraft crashes on startup. ### Sodium [sodium] -ReplayMod can record when _Sodium_ is installed but will crash during render. Disable Sodium before rendering, it can be re-enabled after that. +ReplayMod can record when _Sodium_ is installed, but currently lacks the FREX Flawless Frames API to render. A modified build of _Sodium_, that supports this API, is available from the ReplayMod downloads, by clicking the `Click to show compatible Sodium versions` button. ### Resource Loader [resourceloader] The _Resource Loader_ mod is not compatible with ReplayMod. From 89d771fdbf3b6a112cce10852e09ce6154ce0896 Mon Sep 17 00:00:00 2001 From: Kepler-17c <17955785+Kepler-17c@users.noreply.github.com> Date: Wed, 12 Jan 2022 03:38:14 +0100 Subject: [PATCH 07/19] Updated the compatibility section on shaders --- docs/content.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/content.md b/docs/content.md index e767cb76..62746584 100755 --- a/docs/content.md +++ b/docs/content.md @@ -582,7 +582,10 @@ In General, the Replay Mod _should_ be compatible with most Forge and Fabric Mod ### Shaders Mod [shaders] _Karyonix' Shaders Mod_ is no longer compatible with Minecraft Forge starting with 1.9.4. As such it is not compatible with the Replay Mod either. -Please use _Optifine_ instead. + +Below Minecraft 1.16 you can try _Optifine_ instead. Note however, that official support has ended and many versions break ReplayMod. + +On Minecraft 1.16.5 and up you can use _Iris_, which is fully supported. For the time being, you will have to use it with the custom _Sodium_ provided on our download page. The fix it includes is pending for the official version. ### Custom Main Menu [custom-main-menu] The _Custom Main Menu_ mod is often used in mod packs to customize their Main Menu with a button layout fitting the background image, links to their website / bug tracker and similar. From a95edaa31df0765a0795c5ae228a8a8c8628f202 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 31 Jul 2022 09:30:19 +0200 Subject: [PATCH 08/19] Update translations --- src/main/resources/assets/replaymod/lang | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/assets/replaymod/lang b/src/main/resources/assets/replaymod/lang index d8beaa51..ed16d95c 160000 --- a/src/main/resources/assets/replaymod/lang +++ b/src/main/resources/assets/replaymod/lang @@ -1 +1 @@ -Subproject commit d8beaa51ae2966214df6d5cebec0307475fcec60 +Subproject commit ed16d95cd373d4b5ddd257ea4b30790d2fcff353 From 6db47ee070c23ac0382b8e3e84bf8f350f872796 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 31 Jul 2022 09:34:41 +0200 Subject: [PATCH 09/19] Updated broken forge docs link --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b8d5f000..6062e5db 100644 --- a/README.md +++ b/README.md @@ -35,7 +35,7 @@ It has by now acquired a lot more sophisticated features to make it as noninvasi Please read the [preprocessor's README](https://github.com/ReplayMod/preprocessor/blob/master/README.md) to understand how it works. ### Versioning -The ReplayMod uses the versioning scheme outlined [here](http://mcforge.readthedocs.io/en/latest/conventions/versioning/) +The ReplayMod uses the versioning scheme outlined [here](https://docs.minecraftforge.net/en/1.12.x/conventions/versioning/) with three changes: - No `MAJORAPI`, the ReplayMod does not provide any external API - "Updating to a new Minecraft version" should not increment `MAJORMOD`, we maintain one version of the ReplayMod From bdea84b9a2c2428b80dd88d972de7f53066eb737 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 31 Jul 2022 10:22:20 +0200 Subject: [PATCH 10/19] Remove unused GuiMainMenuAccessor --- .../core/mixin/GuiMainMenuAccessor.java | 16 ---------------- src/main/resources/mixins.core.replaymod.json | 1 - 2 files changed, 17 deletions(-) delete mode 100644 src/main/java/com/replaymod/core/mixin/GuiMainMenuAccessor.java diff --git a/src/main/java/com/replaymod/core/mixin/GuiMainMenuAccessor.java b/src/main/java/com/replaymod/core/mixin/GuiMainMenuAccessor.java deleted file mode 100644 index 3716e1ab..00000000 --- a/src/main/java/com/replaymod/core/mixin/GuiMainMenuAccessor.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.replaymod.core.mixin; - -import net.minecraft.client.gui.screen.TitleScreen; -import net.minecraft.client.gui.screen.Screen; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(TitleScreen.class) -public interface GuiMainMenuAccessor { - //#if MC>=10904 - @Accessor("realmsNotificationGui") - Screen getRealmsNotification(); - @Accessor("realmsNotificationGui") - void setRealmsNotification(Screen value); - //#endif -} diff --git a/src/main/resources/mixins.core.replaymod.json b/src/main/resources/mixins.core.replaymod.json index a326dc8d..7e10b9c9 100644 --- a/src/main/resources/mixins.core.replaymod.json +++ b/src/main/resources/mixins.core.replaymod.json @@ -18,7 +18,6 @@ //#endif "MixinKeyboardListener", "MixinMinecraft", - "GuiMainMenuAccessor", "GuiScreenAccessor", "KeyBindingAccessor", "MinecraftAccessor", From 400ec6bda58480c26f08aada7232e7017ba4e3a9 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 31 Jul 2022 10:37:22 +0200 Subject: [PATCH 11/19] Port to MC 1.19.1 --- build.gradle | 5 ++++- jGui | 2 +- root.gradle.kts | 2 ++ settings.gradle.kts | 2 ++ src/main/java/com/replaymod/replay/handler/GuiHandler.java | 2 +- 5 files changed, 10 insertions(+), 3 deletions(-) diff --git a/build.gradle b/build.gradle index 40ad0f52..76afaae9 100644 --- a/build.gradle +++ b/build.gradle @@ -249,6 +249,7 @@ dependencies { 11801: '1.18.1', 11802: '1.18.2', 11900: '1.19', + 11901: '1.19.1', ][mcVersion] mappings 'net.fabricmc:yarn:' + [ 11404: '1.14.4+build.16', @@ -262,6 +263,7 @@ dependencies { 11801: '1.18.1+build.1:v2', 11802: '1.18.2+build.1:v2', 11900: '1.19+build.2:v2', + 11901: '1.19.1+build.5:v2', ][mcVersion] modImplementation 'net.fabricmc:fabric-loader:0.14.6' def fabricApiVersion = [ @@ -276,6 +278,7 @@ dependencies { 11801: '0.43.1+1.18', 11802: '0.47.9+1.18.2', 11900: '0.55.3+1.19', + 11901: '0.58.5+1.19.1', ][mcVersion] def fabricApiModules = [ "api-base", @@ -348,7 +351,7 @@ dependencies { shadow 'com.github.ReplayMod.JavaBlend:2.79.0:a0696f8' - shadow "com.github.ReplayMod:ReplayStudio:74d8465", shadeExclusions + shadow "com.github.ReplayMod:ReplayStudio:b2c999d", 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) diff --git a/jGui b/jGui index c1e43fc9..aa031212 160000 --- a/jGui +++ b/jGui @@ -1 +1 @@ -Subproject commit c1e43fc9f0550bf6a6ab8685c1008e754e042772 +Subproject commit aa031212c7019c475abb3a83d67fb7ade3d3455c diff --git a/root.gradle.kts b/root.gradle.kts index 276002e6..cba0c385 100755 --- a/root.gradle.kts +++ b/root.gradle.kts @@ -189,6 +189,7 @@ val doRelease by tasks.registering { defaultTasks("bundleJar") preprocess { + val mc11901 = createNode("1.19.1", 11901, "yarn") val mc11900 = createNode("1.19", 11900, "yarn") val mc11802 = createNode("1.18.2", 11802, "yarn") val mc11801 = createNode("1.18.1", 11801, "yarn") @@ -210,6 +211,7 @@ preprocess { val mc10800 = createNode("1.8", 10800, "srg") val mc10710 = createNode("1.7.10", 10710, "srg") + mc11901.link(mc11900) mc11900.link(mc11802, file("versions/mapping-fabric-1.19-1.18.2.txt")) mc11802.link(mc11801) mc11801.link(mc11701, file("versions/mapping-fabric-1.18.1-1.17.1.txt")) diff --git a/settings.gradle.kts b/settings.gradle.kts index b3289dad..2907c38f 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -34,6 +34,7 @@ val jGuiVersions = listOf( "1.18.1", "1.18.2", "1.19", + "1.19.1", ) val replayModVersions = listOf( // "1.7.10", @@ -56,6 +57,7 @@ val replayModVersions = listOf( "1.18.1", "1.18.2", "1.19", + "1.19.1", ) rootProject.buildFileName = "root.gradle.kts" diff --git a/src/main/java/com/replaymod/replay/handler/GuiHandler.java b/src/main/java/com/replaymod/replay/handler/GuiHandler.java index 00bb3d9f..f70f7d95 100644 --- a/src/main/java/com/replaymod/replay/handler/GuiHandler.java +++ b/src/main/java/com/replaymod/replay/handler/GuiHandler.java @@ -138,7 +138,7 @@ public class GuiHandler extends EventRegistrations { //#if MC<11400 //$$ openToLan = b; //#endif - //#if MC>=11400 + //#if MC>=11400 && MC<11901 } else if (id.equals(BUTTON_OPTIONS)) { //#if MC>=11400 b.setWidth(204); From a9401a9779369b4455610d6d05230947331473eb Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 31 Jul 2022 10:38:07 +0200 Subject: [PATCH 12/19] Update ModMenu --- build.gradle | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/build.gradle b/build.gradle index 76afaae9..9d1db1cf 100644 --- a/build.gradle +++ b/build.gradle @@ -362,8 +362,10 @@ dependencies { shadow 'com.github.ReplayMod:lwjgl-utils:27dcd66' if (FABRIC) { - if (mcVersion >= 11900) { - modCompileOnly 'com.terraformersmc:modmenu:3.1.0' // FIXME update + if (mcVersion >= 11901) { + modImplementation 'com.terraformersmc:modmenu:4.0.5' + } else if (mcVersion >= 11900) { + modImplementation 'com.terraformersmc:modmenu:4.0.4' } else if (mcVersion >= 11802) { modImplementation 'com.terraformersmc:modmenu:3.1.0' } else if (mcVersion >= 11800) { From 962f08718fa63e9a4077a1f3d38854db43c460ac Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 31 Jul 2022 10:38:14 +0200 Subject: [PATCH 13/19] Fix "Show Chat" setting on 1.19+ (fixes #757) --- src/main/java/com/replaymod/replay/FullReplaySender.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/replaymod/replay/FullReplaySender.java b/src/main/java/com/replaymod/replay/FullReplaySender.java index aee4dac1..0806979f 100644 --- a/src/main/java/com/replaymod/replay/FullReplaySender.java +++ b/src/main/java/com/replaymod/replay/FullReplaySender.java @@ -58,6 +58,7 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; //#if MC>=11900 +//$$ import net.minecraft.network.packet.s2c.play.ChatMessageS2CPacket; //#else import net.minecraft.network.packet.s2c.play.MobSpawnS2CPacket; import net.minecraft.network.packet.s2c.play.PaintingSpawnS2CPacket; @@ -861,7 +862,11 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend } } + //#if MC>=11900 + //$$ if (p instanceof GameMessageS2CPacket || p instanceof ChatMessageS2CPacket) { + //#else if (p instanceof GameMessageS2CPacket) { + //#endif if (!ReplayModReplay.instance.getCore().getSettingsRegistry().get(Setting.SHOW_CHAT)) { return null; } From af3f6ddbe2c96064b4f2af8c2a7908f1ffbe3cb8 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 31 Jul 2022 11:12:53 +0200 Subject: [PATCH 14/19] Always handle PlayerPositionLookS2CPacket manually --- src/main/java/com/replaymod/replay/FullReplaySender.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/replaymod/replay/FullReplaySender.java b/src/main/java/com/replaymod/replay/FullReplaySender.java index 0806979f..a6e8ae7d 100644 --- a/src/main/java/com/replaymod/replay/FullReplaySender.java +++ b/src/main/java/com/replaymod/replay/FullReplaySender.java @@ -832,8 +832,11 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend CameraEntity cent = replayHandler.getCameraEntity(); cent.setCameraPosition(ppl.getX(), ppl.getY(), ppl.getZ()); + cent.setCameraRotation(ppl.getYaw(), ppl.getPitch(), cent.roll); } }.run(); + + return null; } if(p instanceof GameStateChangeS2CPacket) { From 7550e3dcb763b75e0dc090efd7119b57e730c437 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 31 Jul 2022 11:16:37 +0200 Subject: [PATCH 15/19] Fix race conditions in movement packet filtering (fixes #760) We cannot filter this packet purely on the netty thread because our filter depends on the current camera position (if it's too far, we do want to teleport it closer) and accessing the camera is only safe from the main thread. Previously the second part had not been considered which could lead to race conditions where the camera may not yet exist on the netty thread at time of handling. These appear to have gotten the wholly inappropriate "just put a null check around it" treatment, but that means that the filter sometimes doesn't work (hence the bug report). This commit changes handling such that all access to `allowMovement` is always done on the main thread and then moves the filtering there as well. And therefore filtering should now work properly. --- .../replaymod/replay/FullReplaySender.java | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/replaymod/replay/FullReplaySender.java b/src/main/java/com/replaymod/replay/FullReplaySender.java index a6e8ae7d..fd41e6e2 100644 --- a/src/main/java/com/replaymod/replay/FullReplaySender.java +++ b/src/main/java/com/replaymod/replay/FullReplaySender.java @@ -267,6 +267,8 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend /** * Whether to allow (process) the next player movement packet. + * + * Must only be accessed from the main thread. */ protected boolean allowMovement; @@ -639,7 +641,7 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend if(p instanceof GameJoinS2CPacket) { GameJoinS2CPacket packet = (GameJoinS2CPacket) p; int entId = packet.getEntityId(); - allowMovement = true; + schedulePacketHandler(() -> allowMovement = true); actualID = entId; entId = -1789435; // Camera entity id should be negative which is an invalid id and can't be used by servers //#if MC>=11400 @@ -773,7 +775,7 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend //#endif //#endif - allowMovement = true; + schedulePacketHandler(() -> allowMovement = true); } if(p instanceof PlayerPositionLookS2CPacket) { @@ -789,8 +791,6 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend if(replayHandler.shouldSuppressCameraMovements()) return null; - CameraEntity cent = replayHandler.getCameraEntity(); - //#if MC>=10800 //#if MC>=11400 for (PlayerPositionLookS2CPacket.Flag relative : ppl.getFlags()) { @@ -812,29 +812,28 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend } //#endif - if(cent != null) { - if(!allowMovement && !((Math.abs(cent.getX() - ppl.getX()) > TP_DISTANCE_LIMIT) || - (Math.abs(cent.getZ() - ppl.getZ()) > TP_DISTANCE_LIMIT))) { - return null; - } else { - allowMovement = false; - } - } - - new Runnable() { + schedulePacketHandler(new Runnable() { @Override @SuppressWarnings("unchecked") public void run() { + // FIXME: world shouldn't ever be null at this point, now that we use the packet queue + // probably fine to remove on the next non-patch version (don't want to break stuff now) if (mc.world == null || !mc.isOnThread()) { ReplayMod.instance.runLater(this); return; } CameraEntity cent = replayHandler.getCameraEntity(); + if (!allowMovement && !((Math.abs(cent.getX() - ppl.getX()) > TP_DISTANCE_LIMIT) || + (Math.abs(cent.getZ() - ppl.getZ()) > TP_DISTANCE_LIMIT))) { + return; + } else { + allowMovement = false; + } cent.setCameraPosition(ppl.getX(), ppl.getY(), ppl.getZ()); cent.setCameraRotation(ppl.getYaw(), ppl.getPitch(), cent.roll); } - }.run(); + }); return null; } @@ -1273,6 +1272,22 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend ReplayMod.instance.runTasks(); } + /** + * Runs the given runnable on the main thread as if it was a packet handler. + * Note that the packet handler queue has different behavior than the standard ReplayMod queue. + */ + private void schedulePacketHandler(Runnable runnable) { + if (mc.isOnThread()) { + runnable.run(); + } else { + //#if MC>=11400 + mc.execute(runnable); + //#else + //$$ mc.addScheduledTask(runnable); + //#endif + } + } + protected void processPacketSync(Packet p) { //#if MC>=10904 if (p instanceof UnloadChunkS2CPacket) { From 42d04d123c49797614ab3f333fbdaecbd65411fd Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 31 Jul 2022 12:25:43 +0200 Subject: [PATCH 16/19] Deduplicate code --- .../handler/RecordingEventHandler.java | 41 ++++--------------- 1 file changed, 8 insertions(+), 33 deletions(-) diff --git a/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java b/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java index ac303479..b2e403ea 100644 --- a/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java +++ b/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java @@ -254,8 +254,14 @@ public class RecordingEventHandler extends EventRegistrations { //#if MC>=10904 for (EquipmentSlot slot : EquipmentSlot.values()) { ItemStack stack = player.getEquippedStack(slot); - if (playerItems[slot.ordinal()] != stack) { - playerItems[slot.ordinal()] = stack; + int index = slot.ordinal(); + //#else + //$$ for (int slot = 0; slot < 5; slot++) { + //$$ ItemStack stack = player.getEquipmentInSlot(slot); + //$$ int index = slot; + //#endif + if (playerItems[index] != stack) { + playerItems[index] = stack; //#if MC>=11600 packetListener.save(new EntityEquipmentUpdateS2CPacket(player.getEntityId(), Collections.singletonList(Pair.of(slot, stack)))); //#else @@ -263,37 +269,6 @@ public class RecordingEventHandler extends EventRegistrations { //#endif } } - //#else - //$$ if(playerItems[0] != mc.thePlayer.getHeldItem()) { - //$$ playerItems[0] = mc.thePlayer.getHeldItem(); - //$$ S04PacketEntityEquipment pee = new S04PacketEntityEquipment(player.getEntityId(), 0, playerItems[0]); - //$$ packetListener.save(pee); - //$$ } - //$$ - //$$ if(playerItems[1] != mc.thePlayer.inventory.armorInventory[0]) { - //$$ playerItems[1] = mc.thePlayer.inventory.armorInventory[0]; - //$$ S04PacketEntityEquipment pee = new S04PacketEntityEquipment(player.getEntityId(), 1, playerItems[1]); - //$$ packetListener.save(pee); - //$$ } - //$$ - //$$ if(playerItems[2] != mc.thePlayer.inventory.armorInventory[1]) { - //$$ playerItems[2] = mc.thePlayer.inventory.armorInventory[1]; - //$$ S04PacketEntityEquipment pee = new S04PacketEntityEquipment(player.getEntityId(), 2, playerItems[2]); - //$$ packetListener.save(pee); - //$$ } - //$$ - //$$ if(playerItems[3] != mc.thePlayer.inventory.armorInventory[2]) { - //$$ playerItems[3] = mc.thePlayer.inventory.armorInventory[2]; - //$$ S04PacketEntityEquipment pee = new S04PacketEntityEquipment(player.getEntityId(), 3, playerItems[3]); - //$$ packetListener.save(pee); - //$$ } - //$$ - //$$ if(playerItems[4] != mc.thePlayer.inventory.armorInventory[3]) { - //$$ playerItems[4] = mc.thePlayer.inventory.armorInventory[3]; - //$$ S04PacketEntityEquipment pee = new S04PacketEntityEquipment(player.getEntityId(), 4, playerItems[4]); - //$$ packetListener.save(pee); - //$$ } - //#endif //Leaving Ride From 62b9cd05387ba620c77b2a16ba0c1fe26fba3df2 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 31 Jul 2022 12:49:46 +0200 Subject: [PATCH 17/19] Use DefaultedList for equipment tracking Because on 1.11+ a `null` item stack is not something that should exist. This then also allows us to compare item stacks by value rather than only by reference, potentially saving a few redundant entity equipment update packets (but more importantly, also allows us to store a copy instead of the original, which will be important for the next commit). --- .../java/com/replaymod/core/versions/Patterns.java | 14 ++++++++++++++ .../recording/handler/RecordingEventHandler.java | 11 ++++++++--- versions/1.11.2/mapping.txt | 1 + 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/replaymod/core/versions/Patterns.java b/src/main/java/com/replaymod/core/versions/Patterns.java index cfd7d5a9..c5a81d1f 100644 --- a/src/main/java/com/replaymod/core/versions/Patterns.java +++ b/src/main/java/com/replaymod/core/versions/Patterns.java @@ -15,6 +15,7 @@ import net.minecraft.client.render.Tessellator; 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.resource.Resource; import net.minecraft.resource.ResourceManager; import net.minecraft.text.LiteralText; @@ -49,6 +50,10 @@ import net.minecraft.client.util.Window; //$$ import net.minecraft.client.gui.GuiButton; //#endif +//#if MC>=11100 +import net.minecraft.util.collection.DefaultedList; +//#endif + //#if MC>=10904 import net.minecraft.sound.SoundEvent; import net.minecraft.util.crash.CrashCallable; @@ -669,4 +674,13 @@ class Patterns { return manager.getResource(id); //#endif } + + @Pattern + private static List DefaultedList_ofSize_ItemStack_Empty(int size) { + //#if MC>=11100 + return DefaultedList.ofSize(size, ItemStack.EMPTY); + //#else + //$$ return java.util.Arrays.asList(new ItemStack[size]); + //#endif + } } diff --git a/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java b/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java index b2e403ea..17427bad 100644 --- a/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java +++ b/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java @@ -40,6 +40,10 @@ import java.util.Collections; //$$ import net.minecraft.network.play.server.SPacketUseBed; //#endif +//#if MC>=11100 +import net.minecraft.util.collection.DefaultedList; +//#endif + //#if MC>=10904 import net.minecraft.network.packet.s2c.play.EntityTrackerUpdateS2CPacket; import net.minecraft.network.packet.s2c.play.WorldEventS2CPacket; @@ -53,6 +57,7 @@ import net.minecraft.util.math.BlockPos; //$$ import net.minecraft.util.MathHelper; //#endif +import java.util.List; import java.util.Objects; import static com.replaymod.core.versions.MCVer.*; @@ -63,7 +68,7 @@ public class RecordingEventHandler extends EventRegistrations { private final PacketListener packetListener; private Double lastX, lastY, lastZ; - private ItemStack[] playerItems = new ItemStack[6]; + private final List playerItems = DefaultedList.ofSize(6, ItemStack.EMPTY); private int ticksSinceLastCorrection; private boolean wasSleeping; private int lastRiding = -1; @@ -260,8 +265,8 @@ public class RecordingEventHandler extends EventRegistrations { //$$ ItemStack stack = player.getEquipmentInSlot(slot); //$$ int index = slot; //#endif - if (playerItems[index] != stack) { - playerItems[index] = stack; + if (!ItemStack.areEqual(playerItems.get(index), stack)) { + playerItems.set(index, stack); //#if MC>=11600 packetListener.save(new EntityEquipmentUpdateS2CPacket(player.getEntityId(), Collections.singletonList(Pair.of(slot, stack)))); //#else diff --git a/versions/1.11.2/mapping.txt b/versions/1.11.2/mapping.txt index 7f741972..48e74eec 100644 --- a/versions/1.11.2/mapping.txt +++ b/versions/1.11.2/mapping.txt @@ -1 +1,2 @@ net.minecraft.item.ItemStack EMPTY field_190927_a +net.minecraft.util.NonNullList withSize() func_191197_a() From c34f75d95aa15a938bb3f7d7e620991c59eae9a7 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 31 Jul 2022 12:53:11 +0200 Subject: [PATCH 18/19] Fix item staying visually equipped after being dropped (fixes #658) --- .../com/replaymod/recording/handler/RecordingEventHandler.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java b/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java index 17427bad..822c3944 100644 --- a/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java +++ b/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java @@ -266,6 +266,9 @@ public class RecordingEventHandler extends EventRegistrations { //$$ int index = slot; //#endif if (!ItemStack.areEqual(playerItems.get(index), stack)) { + // ItemStack has internal mutability, so we need to make a copy now if we want to compare its + // current state with future states (e.g. dropping on modern versions will set the count to zero). + stack = stack != null ? stack.copy() : null; playerItems.set(index, stack); //#if MC>=11600 packetListener.save(new EntityEquipmentUpdateS2CPacket(player.getEntityId(), Collections.singletonList(Pair.of(slot, stack)))); From ff98260216914c230be6a9a306775a511e914594 Mon Sep 17 00:00:00 2001 From: Jonas Herzig Date: Sun, 31 Jul 2022 13:45:49 +0200 Subject: [PATCH 19/19] Fix build on MC 1.8 --- versions/1.8.9/mapping.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/versions/1.8.9/mapping.txt b/versions/1.8.9/mapping.txt index 2dfdbda5..37f9ead2 100644 --- a/versions/1.8.9/mapping.txt +++ b/versions/1.8.9/mapping.txt @@ -8,6 +8,8 @@ net.minecraft.network.play.server.S48PacketResourcePackSend getHash() func_17978 net.minecraft.network.play.server.S08PacketPlayerPosLook getX() func_148932_c() net.minecraft.network.play.server.S08PacketPlayerPosLook getY() func_148928_d() net.minecraft.network.play.server.S08PacketPlayerPosLook getZ() func_148933_e() +net.minecraft.network.play.server.S08PacketPlayerPosLook getYaw() func_148931_f() +net.minecraft.network.play.server.S08PacketPlayerPosLook getPitch() func_148930_g() net.minecraft.network.play.server.S2BPacketChangeGameState getGameState() func_149138_c() net.minecraft.network.play.server.S0EPacketSpawnObject getType() func_148993_l() net.minecraft.network.play.server.S21PacketChunkData getChunkX() func_149273_e()