diff --git a/build.gradle b/build.gradle index c57fc62f..40ad0f52 100644 --- a/build.gradle +++ b/build.gradle @@ -1,4 +1,5 @@ import com.replaymod.gradle.preprocess.PreprocessTask +import static gg.essential.gradle.util.PrebundleKt.prebundle buildscript { def mcVersion @@ -18,23 +19,23 @@ buildscript { name = "fabric" url = "https://maven.fabricmc.net/" } - if (!fabric) { - maven { - name = "forge" - url = "https://maven.minecraftforge.net" - } + maven { + name = "forge" + url = "https://maven.minecraftforge.net" } maven { name = "sonatype" url = "https://oss.sonatype.org/content/repositories/snapshots/" } maven { url 'https://jitpack.io' } + maven { url "https://maven.architectury.dev" } + maven { url "https://repo.essential.gg/repository/maven-public" } } dependencies { classpath 'gradle.plugin.com.github.jengelman.gradle.plugins:shadow:7.0.0' if (fabric) { - classpath 'fabric-loom:fabric-loom.gradle.plugin:0.10-SNAPSHOT' + classpath 'fabric-loom:fabric-loom.gradle.plugin:0.11-SNAPSHOT' } else if (mcVersion >= 11400) { classpath('net.minecraftforge.gradle:ForgeGradle:5.0.5') { // the FG people still haven't learned to not do breaking changes exclude group: 'trove', module: 'trove' // preprocessor/idea requires more recent one @@ -48,6 +49,7 @@ buildscript { } else { classpath 'com.github.ReplayMod:ForgeGradle:a8a9e0ca:all' // FG 1.2 } + classpath 'gg.essential:essential-gradle-toolkit:0.1.10' } } @@ -246,6 +248,7 @@ dependencies { 11800: '1.18', 11801: '1.18.1', 11802: '1.18.2', + 11900: '1.19', ][mcVersion] mappings 'net.fabricmc:yarn:' + [ 11404: '1.14.4+build.16', @@ -258,8 +261,9 @@ dependencies { 11800: '1.18+build.1:v2', 11801: '1.18.1+build.1:v2', 11802: '1.18.2+build.1:v2', + 11900: '1.19+build.2:v2', ][mcVersion] - modImplementation 'net.fabricmc:fabric-loader:0.12.5' + modImplementation 'net.fabricmc:fabric-loader:0.14.6' def fabricApiVersion = [ 11404: '0.4.3+build.247-1.14', 11502: '0.5.1+build.294-1.15', @@ -271,6 +275,7 @@ dependencies { 11800: '0.43.1+1.18', 11801: '0.43.1+1.18', 11802: '0.47.9+1.18.2', + 11900: '0.55.3+1.19', ][mcVersion] def fabricApiModules = [ "api-base", @@ -279,9 +284,11 @@ dependencies { "resource-loader-v0", ] if (mcVersion >= 11600) { + fabricApiModules.remove("keybindings-v0") fabricApiModules.add("key-binding-api-v1") } if (mcVersion >= 11700) { + fabricApiModules.remove("networking-v0") fabricApiModules.add("networking-api-v1") } fabricApiModules.each { module -> @@ -318,13 +325,13 @@ dependencies { shadow 'com.google.api-client:google-api-client-java6:1.20.0', shadeExclusions shadow 'com.google.oauth-client:google-oauth-client-jetty:1.20.0' - if (mcVersion >= 11400) { // need lwjgl 3 - for (suffix in ['', ':natives-linux', ':natives-windows', ':natives-macos']) { - shadow('org.lwjgl:lwjgl-tinyexr:3.2.2' + suffix) { - exclude group: 'org.lwjgl', module: 'lwjgl' // comes with MC - } - } + def lwjgl = configurations.create("lwjgl") + for (suffix in ['', ':natives-linux', ':natives-windows', ':natives-macos', ':natives-macos-arm64']) { + add(lwjgl.name, 'org.lwjgl:lwjgl:3.3.1' + suffix) + add(lwjgl.name, 'org.lwjgl:lwjgl-tinyexr:3.3.1' + suffix) } + compileOnly('org.lwjgl:lwjgl-tinyexr:3.3.1') + shadow(prebundle(project, lwjgl, "com/replaymod/render/utils/lwjgl.jar", {})) if (mcVersion < 11200) { // The version which MC ships is too old, we'll need to ship our own @@ -341,7 +348,10 @@ dependencies { shadow 'com.github.ReplayMod.JavaBlend:2.79.0:a0696f8' - shadow "com.github.ReplayMod:ReplayStudio:6fc8e20", shadeExclusions + shadow "com.github.ReplayMod:ReplayStudio:74d8465", 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) implementation(FABRIC ? dependencies.project(path: jGui.path, configuration: "namedElements") : jGui) { transitive = false // FG 1.2 puts all MC deps into the compile configuration and we don't want to shade those @@ -349,7 +359,9 @@ dependencies { shadow 'com.github.ReplayMod:lwjgl-utils:27dcd66' if (FABRIC) { - if (mcVersion >= 11802) { + if (mcVersion >= 11900) { + modCompileOnly 'com.terraformersmc:modmenu:3.1.0' // FIXME update + } else if (mcVersion >= 11802) { modImplementation 'com.terraformersmc:modmenu:3.1.0' } else if (mcVersion >= 11800) { modImplementation 'com.terraformersmc:modmenu:3.0.0' diff --git a/jGui b/jGui index 5e41452b..c1e43fc9 160000 --- a/jGui +++ b/jGui @@ -1 +1 @@ -Subproject commit 5e41452b0e17700691efd38b8ce793b5200145c8 +Subproject commit c1e43fc9f0550bf6a6ab8685c1008e754e042772 diff --git a/root.gradle.kts b/root.gradle.kts index 8ee13ad4..276002e6 100755 --- a/root.gradle.kts +++ b/root.gradle.kts @@ -2,8 +2,8 @@ import groovy.json.JsonOutput import java.io.ByteArrayOutputStream plugins { - id("fabric-loom") version "0.10-SNAPSHOT" apply false - id("com.replaymod.preprocess") version "7746c47" + id("fabric-loom") version "0.11-SNAPSHOT" apply false + id("com.replaymod.preprocess") version "48e02ad" id("com.github.hierynomus.license") version "0.15.0" } @@ -189,6 +189,7 @@ val doRelease by tasks.registering { defaultTasks("bundleJar") preprocess { + val mc11900 = createNode("1.19", 11900, "yarn") val mc11802 = createNode("1.18.2", 11802, "yarn") val mc11801 = createNode("1.18.1", 11801, "yarn") val mc11701 = createNode("1.17.1", 11701, "yarn") @@ -209,6 +210,7 @@ preprocess { val mc10800 = createNode("1.8", 10800, "srg") val mc10710 = createNode("1.7.10", 10710, "srg") + 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")) mc11701.link(mc11700) diff --git a/settings.gradle.kts b/settings.gradle.kts index a77be868..b3289dad 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -33,6 +33,7 @@ val jGuiVersions = listOf( "1.17.1", "1.18.1", "1.18.2", + "1.19", ) val replayModVersions = listOf( // "1.7.10", @@ -54,6 +55,7 @@ val replayModVersions = listOf( "1.17.1", "1.18.1", "1.18.2", + "1.19", ) rootProject.buildFileName = "root.gradle.kts" diff --git a/src/main/java/com/replaymod/core/KeyBindingRegistry.java b/src/main/java/com/replaymod/core/KeyBindingRegistry.java index 6223717c..40eff2ee 100644 --- a/src/main/java/com/replaymod/core/KeyBindingRegistry.java +++ b/src/main/java/com/replaymod/core/KeyBindingRegistry.java @@ -14,7 +14,11 @@ import net.minecraft.util.crash.CrashException; //#if FABRIC>=1 import com.replaymod.core.versions.LangResourcePack; -import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding; +//#if MC>=11600 +import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; +//#else +//$$ import net.fabricmc.fabric.api.client.keybinding.FabricKeyBinding; +//#endif import net.minecraft.client.util.InputUtil; import net.minecraft.util.Identifier; import static com.replaymod.core.ReplayMod.MOD_ID; @@ -35,8 +39,8 @@ import java.util.function.Supplier; public class KeyBindingRegistry extends EventRegistrations { private static final String CATEGORY = "replaymod.title"; - //#if FABRIC>=1 - static { net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry.INSTANCE.addCategory(CATEGORY); } + //#if FABRIC>=1 && MC<11600 + //$$ static { net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry.INSTANCE.addCategory(CATEGORY); } //#endif private final Map bindings = new HashMap<>(); @@ -63,9 +67,15 @@ public class KeyBindingRegistry extends EventRegistrations { keyCode = -1; } Identifier id = new Identifier(MOD_ID, name.substring(LangResourcePack.LEGACY_KEY_PREFIX.length())); - FabricKeyBinding fabricKeyBinding = FabricKeyBinding.Builder.create(id, InputUtil.Type.KEYSYM, keyCode, CATEGORY).build(); - net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry.INSTANCE.register(fabricKeyBinding); - KeyBinding keyBinding = fabricKeyBinding; + //#if MC>=11600 + String key = String.format("key.%s.%s", id.getNamespace(), id.getPath()); + KeyBinding keyBinding = new KeyBinding(key, InputUtil.Type.KEYSYM, keyCode, CATEGORY); + KeyBindingHelper.registerKeyBinding(keyBinding); + //#else + //$$ FabricKeyBinding fabricKeyBinding = FabricKeyBinding.Builder.create(id, InputUtil.Type.KEYSYM, keyCode, CATEGORY).build(); + //$$ net.fabricmc.fabric.api.client.keybinding.KeyBindingRegistry.INSTANCE.register(fabricKeyBinding); + //$$ KeyBinding keyBinding = fabricKeyBinding; + //#endif //#else //$$ KeyBinding keyBinding = new KeyBinding(name, keyCode, CATEGORY); //$$ ClientRegistry.registerKeyBinding(keyBinding); diff --git a/src/main/java/com/replaymod/core/ReplayMod.java b/src/main/java/com/replaymod/core/ReplayMod.java index 39e55c12..d93876b6 100644 --- a/src/main/java/com/replaymod/core/ReplayMod.java +++ b/src/main/java/com/replaymod/core/ReplayMod.java @@ -18,7 +18,6 @@ import com.replaymod.replaystudio.studio.ReplayStudio; import com.replaymod.replaystudio.util.I18n; import com.replaymod.simplepathing.ReplayModSimplePathing; import net.minecraft.client.MinecraftClient; -import net.minecraft.client.options.Option; import net.minecraft.resource.DirectoryResourcePack; import net.minecraft.text.LiteralText; import net.minecraft.text.Style; @@ -37,6 +36,11 @@ import java.util.List; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeoutException; +//#if MC>=11900 +//#else +import net.minecraft.client.options.Option; +//#endif + public class ReplayMod implements Module, Scheduler { public static final String MOD_ID = "replaymod"; @@ -162,7 +166,8 @@ public class ReplayMod implements Module, Scheduler { keyBindingRegistry.register(); // 1.7.10 crashes when render distance > 16 - //#if MC>=10800 + // Post 1.19 this has become non-trivial to do, install Sodium+Bobby or OptiFine if you need it + //#if MC>=10800 && MC<11900 if (!MCVer.hasOptifine()) { Option.RENDER_DISTANCE.setMax(64f); } diff --git a/src/main/java/com/replaymod/core/mixin/SimpleOptionAccessor.java b/src/main/java/com/replaymod/core/mixin/SimpleOptionAccessor.java new file mode 100644 index 00000000..07b5fddd --- /dev/null +++ b/src/main/java/com/replaymod/core/mixin/SimpleOptionAccessor.java @@ -0,0 +1 @@ +// 1.19+ diff --git a/src/main/java/com/replaymod/core/versions/LangResourcePack.java b/src/main/java/com/replaymod/core/versions/LangResourcePack.java index 63e4304c..a3594f08 100644 --- a/src/main/java/com/replaymod/core/versions/LangResourcePack.java +++ b/src/main/java/com/replaymod/core/versions/LangResourcePack.java @@ -158,8 +158,12 @@ public class LangResourcePack extends AbstractFileResourcePack { String namespace, //#endif String path, + //#if MC>=11900 + //$$ Predicate filter + //#else int maxDepth, Predicate filter + //#endif ) { if (resourcePackType == ResourceType.CLIENT_RESOURCES && "lang".equals(path)) { Path base = baseLangPath(); @@ -174,8 +178,13 @@ public class LangResourcePack extends AbstractFileResourcePack { .map(LANG_FILE_NAME_PATTERN::matcher) .filter(Matcher::matches) .map(matcher -> String.format("%s_%s.json", matcher.group(1), matcher.group(1))) - .filter(filter::test) + //#if MC<11900 + .filter(filter) + //#endif .map(name -> new Identifier(ReplayMod.MOD_ID, "lang/" + name)) + //#if MC>=11900 + //$$ .filter(filter) + //#endif .collect(Collectors.toList()); } catch (IOException e) { e.printStackTrace(); diff --git a/src/main/java/com/replaymod/core/versions/MCVer.java b/src/main/java/com/replaymod/core/versions/MCVer.java index e53ed093..f24937ca 100644 --- a/src/main/java/com/replaymod/core/versions/MCVer.java +++ b/src/main/java/com/replaymod/core/versions/MCVer.java @@ -34,6 +34,7 @@ import net.minecraft.client.util.Window; import java.util.concurrent.CompletableFuture; //#if MC>=11600 +import net.minecraft.text.Text; import net.minecraft.text.TranslatableText; //#else //$$ import net.minecraft.client.resource.language.I18n; @@ -220,7 +221,7 @@ public class MCVer { //#if MC>=11400 public static Optional findButton(Iterable buttonList, @SuppressWarnings("unused") String text, @SuppressWarnings("unused") int id) { //#if MC>=11600 - final TranslatableText message = new TranslatableText(text); + final Text message = new TranslatableText(text); //#else //$$ final String message = I18n.translate(text); //#endif diff --git a/src/main/java/com/replaymod/core/versions/Patterns.java b/src/main/java/com/replaymod/core/versions/Patterns.java index 884eb955..cfd7d5a9 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.options.GameOptions; import net.minecraft.client.options.KeyBinding; import net.minecraft.client.render.VertexFormat; import net.minecraft.client.texture.TextureManager; @@ -14,6 +15,11 @@ 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.resource.Resource; +import net.minecraft.resource.ResourceManager; +import net.minecraft.text.LiteralText; +import net.minecraft.text.Text; +import net.minecraft.text.TranslatableText; import net.minecraft.util.crash.CrashException; import net.minecraft.util.crash.CrashReport; import net.minecraft.util.crash.CrashReportSection; @@ -61,6 +67,7 @@ import net.minecraft.client.render.BufferBuilder; //$$ import net.minecraft.entity.EntityLivingBase; //#endif +import java.io.IOException; import java.util.Collection; import java.util.List; @@ -549,7 +556,9 @@ class Patterns { @Pattern private static void setCrashReport(MinecraftClient mc, CrashReport report) { - //#if MC>=11800 + //#if MC>=11900 + //$$ mc.setCrashReportSupplier(report); + //#elseif MC>=11800 //$$ mc.setCrashReportSupplier(() -> report); //#else mc.setCrashReport(report); @@ -573,4 +582,91 @@ class Patterns { //$$ return com.replaymod.core.versions.MCVer.getTrackedPosition(entity); //#endif } + + @Pattern + private static Text newTextLiteral(String str) { + //#if MC>=11900 + //$$ return net.minecraft.text.Text.literal(str); + //#else + return new LiteralText(str); + //#endif + } + + @Pattern + private static Text newTextTranslatable(String key, Object...args) { + //#if MC>=11900 + //$$ return net.minecraft.text.Text.translatable(key, args); + //#else + return new TranslatableText(key, args); + //#endif + } + + //#if MC>=11500 + @Pattern + private static Vec3d getTrackedPos(Entity entity) { + //#if MC>=11900 + //$$ return entity.getTrackedPosition().withDelta(0, 0, 0); + //#else + return entity.getTrackedPosition(); + //#endif + } + //#else + //$$ @Pattern private static void getTrackedPos() {} + //#endif + + @Pattern + private static void setGamma(GameOptions options, double value) { + //#if MC>=11900 + //$$ ((com.replaymod.core.mixin.SimpleOptionAccessor) (Object) options.getGamma()).setRawValue(value); + //#elseif MC>=11400 + options.gamma = value; + //#else + //$$ options.gammaSetting = (float) value; + //#endif + } + + @Pattern + private static double getGamma(GameOptions options) { + //#if MC>=11900 + //$$ return options.getGamma().getValue(); + //#else + return options.gamma; + //#endif + } + + @Pattern + private static int getViewDistance(GameOptions options) { + //#if MC>=11900 + //$$ return options.getViewDistance().getValue(); + //#else + return options.viewDistance; + //#endif + } + + @Pattern + private static double getFov(GameOptions options) { + //#if MC>=11900 + //$$ return options.getFov().getValue(); + //#else + return options.fov; + //#endif + } + + @Pattern + private static int getGuiScale(GameOptions options) { + //#if MC>=11900 + //$$ return options.getGuiScale().getValue(); + //#else + return options.guiScale; + //#endif + } + + @Pattern + private static Resource getResource(ResourceManager manager, Identifier id) throws IOException { + //#if MC>=11900 + //$$ return manager.getResourceOrThrow(id); + //#else + return manager.getResource(id); + //#endif + } } diff --git a/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java b/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java index 7231cecf..54940eb3 100644 --- a/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java +++ b/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java @@ -124,7 +124,7 @@ public class MarkerProcessor { PacketTypeRegistry registry = MCVer.getPacketTypeRegistry(true); DimensionTracker dimensionTracker = new DimensionTracker(); - SquashFilter squashFilter = new SquashFilter(null, null); + SquashFilter squashFilter = new SquashFilter(null, null, null); List> outputPaths = new ArrayList<>(); diff --git a/src/main/java/com/replaymod/extras/FullBrightness.java b/src/main/java/com/replaymod/extras/FullBrightness.java index b6bef691..0dc22f6c 100644 --- a/src/main/java/com/replaymod/extras/FullBrightness.java +++ b/src/main/java/com/replaymod/extras/FullBrightness.java @@ -72,7 +72,7 @@ public class FullBrightness extends EventRegistrations implements Extra { Type type = getType(); if (type == Type.Gamma || type == Type.Both) { originalGamma = mc.options.gamma; - mc.options.gamma = 1000; + mc.options.gamma = 1000.0; } if (type == Type.NightVision || type == Type.Both) { if (mc.player != null) { diff --git a/src/main/java/com/replaymod/extras/advancedscreenshots/ScreenshotWriter.java b/src/main/java/com/replaymod/extras/advancedscreenshots/ScreenshotWriter.java index 70522b22..987e7839 100644 --- a/src/main/java/com/replaymod/extras/advancedscreenshots/ScreenshotWriter.java +++ b/src/main/java/com/replaymod/extras/advancedscreenshots/ScreenshotWriter.java @@ -63,4 +63,9 @@ public class ScreenshotWriter implements FrameConsumer { public void close() throws IOException { } + + @Override + public boolean isParallelCapable() { + return false; + } } diff --git a/src/main/java/com/replaymod/extras/playeroverview/PlayerOverviewGui.java b/src/main/java/com/replaymod/extras/playeroverview/PlayerOverviewGui.java index 97e0d20f..01bf1f8f 100644 --- a/src/main/java/com/replaymod/extras/playeroverview/PlayerOverviewGui.java +++ b/src/main/java/com/replaymod/extras/playeroverview/PlayerOverviewGui.java @@ -116,7 +116,7 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable { }.setSize(16, 16), new GuiLabel().setText( //#if MC>=11400 - p.getName().asString() + p.getName().getString() //#else //#if MC>=10800 //$$ p.getName() @@ -181,7 +181,7 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable { if (isSpectator(o1) && !isSpectator(o2)) return 1; if (isSpectator(o2) && !isSpectator(o1)) return -1; //#if MC>=11400 - return o1.getName().asString().compareToIgnoreCase(o2.getName().asString()); + return o1.getName().getString().compareToIgnoreCase(o2.getName().getString()); //#else //#if MC>=10800 //$$ return o1.getName().compareToIgnoreCase(o2.getName()); diff --git a/src/main/java/com/replaymod/recording/ReplayModRecording.java b/src/main/java/com/replaymod/recording/ReplayModRecording.java index 94ae0902..f65e4467 100644 --- a/src/main/java/com/replaymod/recording/ReplayModRecording.java +++ b/src/main/java/com/replaymod/recording/ReplayModRecording.java @@ -16,7 +16,11 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; //#if FABRIC>=1 +//#if MC>=11700 +//$$ import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking; +//#else import net.fabricmc.fabric.api.network.ClientSidePacketRegistry; +//#endif //#else //$$ import net.minecraftforge.fml.network.NetworkRegistry; //#endif @@ -68,7 +72,11 @@ public class ReplayModRecording implements Module { new GuiHandler(core).register(); //#if FABRIC>=1 + //#if MC>=11700 + //$$ ClientPlayNetworking.registerGlobalReceiver(Restrictions.PLUGIN_CHANNEL, (client, handler, buf, resp) -> {}); + //#else ClientSidePacketRegistry.INSTANCE.register(Restrictions.PLUGIN_CHANNEL, (packetContext, packetByteBuf) -> {}); + //#endif //#else //#if MC>=11400 //$$ NetworkRegistry.newEventChannel(Restrictions.PLUGIN_CHANNEL, () -> "0", any -> true, any -> true); diff --git a/src/main/java/com/replaymod/recording/handler/ConnectionEventHandler.java b/src/main/java/com/replaymod/recording/handler/ConnectionEventHandler.java index 0211c8e0..876430fc 100644 --- a/src/main/java/com/replaymod/recording/handler/ConnectionEventHandler.java +++ b/src/main/java/com/replaymod/recording/handler/ConnectionEventHandler.java @@ -3,7 +3,6 @@ package com.replaymod.recording.handler; import com.replaymod.core.ReplayMod; import com.replaymod.core.utils.ModCompat; import com.replaymod.core.utils.Utils; -import com.replaymod.core.versions.MCVer; import com.replaymod.editor.gui.MarkerProcessor; import com.replaymod.recording.ServerInfoExt; import com.replaymod.recording.Setting; @@ -43,7 +42,6 @@ import static com.replaymod.core.versions.MCVer.getMinecraft; */ public class ConnectionEventHandler { - private static final String packetHandlerKey = "packet_handler"; private static final String DATE_FORMAT = "yyyy_MM_dd_HH_mm_ss"; private static final SimpleDateFormat sdf = new SimpleDateFormat(DATE_FORMAT); private static final MinecraftClient mc = getMinecraft(); @@ -141,7 +139,13 @@ public class ConnectionEventHandler { metaData.setMcVersion(ReplayMod.instance.getMinecraftVersion()); packetListener = new PacketListener(core, outputPath, replayFile, metaData); Channel channel = ((NetworkManagerAccessor) networkManager).getChannel(); - channel.pipeline().addBefore(packetHandlerKey, "replay_recorder", packetListener); + if (channel.pipeline().get(PacketListener.DECODER_KEY) != null) { + // Regular channel, we'll inject our recorder directly before the decoder + channel.pipeline().addBefore(PacketListener.DECODER_KEY, PacketListener.RAW_RECORDER_KEY, packetListener); + } else { + // Integrated server passes packets directly, there's no splitting, decompression or decoding + channel.pipeline().addFirst(PacketListener.RAW_RECORDER_KEY, packetListener); + } recordingEventHandler = new RecordingEventHandler(packetListener); recordingEventHandler.register(); diff --git a/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java b/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java index 4ccc2d9e..ac303479 100644 --- a/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java +++ b/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java @@ -42,12 +42,9 @@ import java.util.Collections; //#if MC>=10904 import net.minecraft.network.packet.s2c.play.EntityTrackerUpdateS2CPacket; -import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket; import net.minecraft.network.packet.s2c.play.WorldEventS2CPacket; import net.minecraft.entity.EquipmentSlot; import net.minecraft.util.Hand; -import net.minecraft.sound.SoundCategory; -import net.minecraft.sound.SoundEvent; //#endif //#if MC>=10800 @@ -91,7 +88,7 @@ public class RecordingEventHandler extends EventRegistrations { } } - //#if MC>=11400 + //#if MC>=10904 public void onPacket(Packet packet) { packetListener.save(packet); } @@ -112,16 +109,6 @@ public class RecordingEventHandler extends EventRegistrations { } //#if MC>=10904 - public void onClientSound(SoundEvent sound, SoundCategory category, - double x, double y, double z, float volume, float pitch) { - try { - // Send to all other players in ServerWorldEventHandler#playSoundToAllNearExcept - packetListener.save(new PlaySoundS2CPacket(sound, category, x, y, z, volume, pitch)); - } catch(Exception e) { - e.printStackTrace(); - } - } - public void onClientEffect(int type, BlockPos pos, int data) { try { // Send to all other players in ServerWorldEventHandler#playEvent @@ -334,99 +321,6 @@ public class RecordingEventHandler extends EventRegistrations { } } - //#if FABRIC>=1 - // FIXME fabric - //#else - //$$ @SubscribeEvent - //$$ public void onPickupItem(ItemPickupEvent event) { - //$$ try { - //#if MC>=11100 - //#if MC>=11200 - //#if MC>=11400 - //$$ ItemStack stack = event.getStack(); - //$$ packetListener.save(new SCollectItemPacket( - //$$ event.getOriginalEntity().getEntityId(), - //$$ event.getPlayer().getEntityId(), - //$$ event.getStack().getCount() - //$$ )); - //#else - //$$ packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId(), - //$$ event.pickedUp.getItem().getMaxStackSize())); - //#endif - //#else - //$$ packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId(), - //$$ event.pickedUp.getEntityItem().getMaxStackSize())); - //#endif - //#else - //$$ packetListener.save(new SPacketCollectItem(event.pickedUp.getEntityId(), event.player.getEntityId())); - //#endif - //$$ } catch(Exception e) { - //$$ e.printStackTrace(); - //$$ } - //$$ } - //#endif - - //#if MC>=11400 - // FIXME fabric - //#else - //$$ @SubscribeEvent - //$$ public void onSleep(PlayerSleepInBedEvent event) { - //$$ try { - //#if MC>=10904 - //$$ if (event.getEntityPlayer() != mc.player) { - //$$ return; - //$$ } - //$$ - //$$ packetListener.save(new SPacketUseBed(event.getEntityPlayer(), event.getPos())); - //#else - //$$ if (event.entityPlayer != mc.thePlayer) { - //$$ return; - //$$ } - //$$ - //$$ packetListener.save(new S0APacketUseBed(event.entityPlayer, - //#if MC>=10800 - //$$ event.pos - //#else - //$$ event.x, event.y, event.z - //#endif - //$$ )); - //#endif - //$$ - //$$ wasSleeping = true; - //$$ - //$$ } catch(Exception e) { - //$$ e.printStackTrace(); - //$$ } - //$$ } - //#endif - - /* FIXME event not (yet?) on 1.13 - @SubscribeEvent - public void enterMinecart(MinecartInteractEvent event) { - try { - //#if MC>=10904 - if(event.getEntity() != mc.player) { - return; - } - - packetListener.save(new SPacketEntityAttach(event.getPlayer(), event.getMinecart())); - - lastRiding = event.getMinecart().getEntityId(); - //#else - //$$ if(event.entity != mc.thePlayer) { - //$$ return; - //$$ } - //$$ - //$$ packetListener.save(new S1BPacketEntityAttach(0, event.player, event.minecart)); - //$$ - //$$ lastRiding = event.minecart.getEntityId(); - //#endif - } catch(Exception e) { - e.printStackTrace(); - } - } - */ - //#if MC>=10800 public void onBlockBreakAnim(int breakerId, BlockPos pos, int progress) { //#else diff --git a/src/main/java/com/replaymod/recording/mixin/MixinClientConnection.java b/src/main/java/com/replaymod/recording/mixin/MixinClientConnection.java new file mode 100644 index 00000000..60d1314c --- /dev/null +++ b/src/main/java/com/replaymod/recording/mixin/MixinClientConnection.java @@ -0,0 +1,39 @@ +package com.replaymod.recording.mixin; + +import com.replaymod.recording.packet.PacketListener; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandler; +import net.minecraft.network.ClientConnection; +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.Map; + +@Mixin(ClientConnection.class) +public abstract class MixinClientConnection { + @Shadow + private Channel channel; + + @Inject(method = "setCompressionThreshold", at = @At("RETURN")) + private void ensureReplayModRecorderIsAfterDecompress(CallbackInfo ci) { + ChannelHandler recorder = null; + for (Map.Entry entry : channel.pipeline()) { + String key = entry.getKey(); + if (PacketListener.RAW_RECORDER_KEY.equals(key)) { + recorder = entry.getValue(); + } + if (PacketListener.DECOMPRESS_KEY.equals(key)) { + if (recorder != null) { + // If we've already found the recorder, then that means decompress is after recorder. That's no good + // because it means the recorder is getting compressed packets, we need to move the recorder. + channel.pipeline().remove(recorder); + channel.pipeline().addBefore(PacketListener.DECODER_KEY, PacketListener.RAW_RECORDER_KEY, recorder); + return; + } + } + } + } +} diff --git a/src/main/java/com/replaymod/recording/mixin/MixinDownloadingPackFinder.java b/src/main/java/com/replaymod/recording/mixin/MixinDownloadingPackFinder.java index 8a581096..196f0ad0 100644 --- a/src/main/java/com/replaymod/recording/mixin/MixinDownloadingPackFinder.java +++ b/src/main/java/com/replaymod/recording/mixin/MixinDownloadingPackFinder.java @@ -33,7 +33,11 @@ public abstract class MixinDownloadingPackFinder implements ResourcePackRecorder } //#if MC>=10800 + //#if MC>=11900 + //$$ @Inject(method = "loadServerPack(Ljava/io/File;Lnet/minecraft/resource/ResourcePackSource;)Ljava/util/concurrent/CompletableFuture;", at = @At("HEAD")) + //#else @Inject(method = "loadServerPack", at = @At("HEAD")) + //#endif private void recordDownloadedPack( File file, //#if MC>=11600 diff --git a/src/main/java/com/replaymod/recording/mixin/MixinWorldClient.java b/src/main/java/com/replaymod/recording/mixin/MixinWorldClient.java index 55bc21b2..d4951d9f 100644 --- a/src/main/java/com/replaymod/recording/mixin/MixinWorldClient.java +++ b/src/main/java/com/replaymod/recording/mixin/MixinWorldClient.java @@ -5,6 +5,7 @@ import com.replaymod.recording.handler.RecordingEventHandler; import net.minecraft.client.MinecraftClient; import net.minecraft.client.world.ClientWorld; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.network.packet.s2c.play.PlaySoundS2CPacket; import net.minecraft.util.profiler.Profiler; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvent; @@ -61,12 +62,20 @@ public abstract class MixinWorldClient extends World implements RecordingEventHa //#else DimensionType dimensionType, //#endif - Supplier profiler, boolean bl, boolean bl2, long l) { + Supplier profiler, boolean bl, boolean bl2, long l + //#if MC>=11900 + //$$ , int maxChainedNeighborUpdates + //#endif + ) { super(mutableWorldProperties, registryKey, //#if MC<11602 //$$ registryKey2, //#endif - dimensionType, profiler, bl, bl2, l); + dimensionType, profiler, bl, bl2, l + //#if MC>=11900 + //$$ , maxChainedNeighborUpdates + //#endif + ); } //#else //#if MC>=11400 @@ -102,7 +111,10 @@ public abstract class MixinWorldClient extends World implements RecordingEventHa // but are instead played directly by the client. The server only sends these sounds to // other clients so we have to record them manually. // E.g. Block place sounds - //#if MC>=11400 + //#if MC>=11900 + //$$ @Inject(method = "playSound(Lnet/minecraft/entity/player/PlayerEntity;DDDLnet/minecraft/sound/SoundEvent;Lnet/minecraft/sound/SoundCategory;FFJ)V", + //$$ at = @At("HEAD")) + //#elseif MC>=11400 //#if FABRIC>=1 @Inject(method = "playSound(Lnet/minecraft/entity/player/PlayerEntity;DDDLnet/minecraft/sound/SoundEvent;Lnet/minecraft/sound/SoundCategory;FF)V", at = @At("HEAD")) @@ -114,12 +126,23 @@ public abstract class MixinWorldClient extends World implements RecordingEventHa //$$ @Inject(method = "playSound(Lnet/minecraft/entity/player/EntityPlayer;DDDLnet/minecraft/util/SoundEvent;Lnet/minecraft/util/SoundCategory;FF)V", //$$ at = @At("HEAD")) //#endif - public void replayModRecording_recordClientSound(PlayerEntity player, double x, double y, double z, SoundEvent sound, SoundCategory category, - float volume, float pitch, CallbackInfo ci) { + public void replayModRecording_recordClientSound( + PlayerEntity player, double x, double y, double z, SoundEvent sound, SoundCategory category, + float volume, float pitch, + //#if MC>=11900 + //$$ long seed, + //#endif + CallbackInfo ci) { if (player == this.client.player) { RecordingEventHandler handler = replayModRecording_getRecordingEventHandler(); if (handler != null) { - handler.onClientSound(sound, category, x, y, z, volume, pitch); + // Sent to all other players in ServerWorldEventHandler#playSoundToAllNearExcept + handler.onPacket(new PlaySoundS2CPacket( + sound, category, x, y, z, volume, pitch + //#if MC>=11900 + //$$ , seed + //#endif + )); } } } diff --git a/src/main/java/com/replaymod/recording/mixin/SPacketSpawnMobAccessor.java b/src/main/java/com/replaymod/recording/mixin/SPacketSpawnMobAccessor.java index 78b66335..1cc43bbb 100644 --- a/src/main/java/com/replaymod/recording/mixin/SPacketSpawnMobAccessor.java +++ b/src/main/java/com/replaymod/recording/mixin/SPacketSpawnMobAccessor.java @@ -1,16 +1,16 @@ -package com.replaymod.recording.mixin; - -import net.minecraft.entity.data.DataTracker; -import net.minecraft.network.packet.s2c.play.MobSpawnS2CPacket; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(MobSpawnS2CPacket.class) -public interface SPacketSpawnMobAccessor { - //#if MC<11500 - //$$ @Accessor("dataTracker") - //$$ DataTracker getDataManager(); - //$$ @Accessor("dataTracker") - //$$ void setDataManager(DataTracker value); - //#endif -} +//#if MC<11500 +//$$ package com.replaymod.recording.mixin; +//$$ +//$$ import net.minecraft.entity.data.DataTracker; +//$$ import net.minecraft.network.packet.s2c.play.MobSpawnS2CPacket; +//$$ import org.spongepowered.asm.mixin.Mixin; +//$$ import org.spongepowered.asm.mixin.gen.Accessor; +//$$ +//$$ @Mixin(MobSpawnS2CPacket.class) +//$$ public interface SPacketSpawnMobAccessor { +//$$ @Accessor("dataTracker") +//$$ DataTracker getDataManager(); +//$$ @Accessor("dataTracker") +//$$ void setDataManager(DataTracker value); +//$$ } +//#endif diff --git a/src/main/java/com/replaymod/recording/mixin/SPacketSpawnPlayerAccessor.java b/src/main/java/com/replaymod/recording/mixin/SPacketSpawnPlayerAccessor.java index 73f7f667..b2eb8607 100644 --- a/src/main/java/com/replaymod/recording/mixin/SPacketSpawnPlayerAccessor.java +++ b/src/main/java/com/replaymod/recording/mixin/SPacketSpawnPlayerAccessor.java @@ -1,16 +1,16 @@ -package com.replaymod.recording.mixin; - -import net.minecraft.entity.data.DataTracker; -import net.minecraft.network.packet.s2c.play.PlayerSpawnS2CPacket; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; - -@Mixin(PlayerSpawnS2CPacket.class) -public interface SPacketSpawnPlayerAccessor { - //#if MC<11500 - //$$ @Accessor("dataTracker") - //$$ DataTracker getDataManager(); - //$$ @Accessor("dataTracker") - //$$ void setDataManager(DataTracker value); - //#endif -} +//#if MC<11500 +//$$ package com.replaymod.recording.mixin; +//$$ +//$$ import net.minecraft.entity.data.DataTracker; +//$$ import net.minecraft.network.packet.s2c.play.PlayerSpawnS2CPacket; +//$$ import org.spongepowered.asm.mixin.Mixin; +//$$ import org.spongepowered.asm.mixin.gen.Accessor; +//$$ +//$$ @Mixin(PlayerSpawnS2CPacket.class) +//$$ public interface SPacketSpawnPlayerAccessor { +//$$ @Accessor("dataTracker") +//$$ DataTracker getDataManager(); +//$$ @Accessor("dataTracker") +//$$ void setDataManager(DataTracker value); +//$$ } +//#endif diff --git a/src/main/java/com/replaymod/recording/packet/PacketListener.java b/src/main/java/com/replaymod/recording/packet/PacketListener.java index f99833a5..677673ad 100644 --- a/src/main/java/com/replaymod/recording/packet/PacketListener.java +++ b/src/main/java/com/replaymod/recording/packet/PacketListener.java @@ -2,7 +2,6 @@ package com.replaymod.recording.packet; import com.github.steveice10.netty.buffer.PooledByteBufAllocator; import com.github.steveice10.packetlib.tcp.io.ByteBufNetOutput; -import com.google.common.collect.Lists; import com.google.gson.Gson; import com.replaymod.core.ReplayMod; import com.replaymod.core.utils.Restrictions; @@ -13,29 +12,27 @@ import com.replaymod.recording.ReplayModRecording; import com.replaymod.recording.Setting; import com.replaymod.recording.gui.GuiSavingReplay; import com.replaymod.recording.handler.ConnectionEventHandler; -import com.replaymod.recording.mixin.SPacketSpawnMobAccessor; -import com.replaymod.recording.mixin.SPacketSpawnPlayerAccessor; import com.replaymod.replaystudio.PacketData; import com.replaymod.replaystudio.data.Marker; import com.replaymod.replaystudio.io.ReplayOutputStream; +import com.replaymod.replaystudio.lib.viaversion.api.protocol.packet.State; +import com.replaymod.replaystudio.protocol.Packet; import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayMetaData; import de.johni0702.minecraft.gui.container.VanillaGuiScreen; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; +import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; +import io.netty.util.AttributeKey; 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.ItemPickupAnimationS2CPacket; -import net.minecraft.network.packet.s2c.play.MobSpawnS2CPacket; import net.minecraft.network.packet.s2c.play.PlayerSpawnS2CPacket; import net.minecraft.entity.Entity; -import net.minecraft.entity.data.DataTracker; import net.minecraft.network.NetworkState; -import net.minecraft.network.Packet; import net.minecraft.network.PacketByteBuf; import net.minecraft.text.LiteralText; import net.minecraft.util.crash.CrashReport; @@ -44,17 +41,6 @@ import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -//#if MC>=11400 -import net.minecraft.network.packet.s2c.login.LoginSuccessS2CPacket; -//#else -//$$ import net.minecraftforge.fml.common.network.internal.FMLProxyPacket; -//#endif - -//#if MC>=10904 -//#else -//$$ import java.util.List; -//#endif - //#if MC>=10800 //#if MC<10904 //$$ import net.minecraft.network.play.server.S46PacketSetCompressionLevel; @@ -64,7 +50,6 @@ import net.minecraft.network.packet.s2c.play.ResourcePackSendS2CPacket; import net.minecraft.network.NetworkSide; //#endif -import java.io.DataOutputStream; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; @@ -82,12 +67,31 @@ import java.util.concurrent.atomic.AtomicInteger; import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.replaystudio.util.Utils.writeInt; +import static java.util.Objects.requireNonNull; +@ChannelHandler.Sharable // so we can re-order it public class PacketListener extends ChannelInboundHandlerAdapter { + public static final String RAW_RECORDER_KEY = "replay_recorder_raw"; + public static final String DECODED_RECORDER_KEY = "replay_recorder_decoded"; + + public static final String DECOMPRESS_KEY = "decompress"; + public static final String DECODER_KEY = "decoder"; + private static final MinecraftClient mc = getMinecraft(); private static final Logger logger = LogManager.getLogger(); + //#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 + //$$ private static final int PACKET_ID_PLAY_COMPRESSION = getPacketId(EnumConnectionState.PLAY, new S46PacketSetCompressionLevel()); + //#endif + private final ReplayMod core; private final Path outputPath; private final ReplayFile replayFile; @@ -105,13 +109,6 @@ public class PacketListener extends ChannelInboundHandlerAdapter { private long lastSentPacket; private long timePassedWhilePaused; private volatile boolean serverWasPaused; - //#if MC>=11400 - private NetworkState connectionState = NetworkState.LOGIN; - private boolean loginPhase = true; - //#else - //$$ private EnumConnectionState connectionState = EnumConnectionState.PLAY; - //$$ private boolean loginPhase = false; - //#endif /** * Used to keep track of the last metadata save job submitted to the save service and @@ -159,6 +156,17 @@ public class PacketListener extends ChannelInboundHandlerAdapter { }); } + public void save(net.minecraft.network.Packet packet) { + Packet encoded; + try { + encoded = encodeMcPacket(getConnectionState(), packet); + } catch (Exception e) { + logger.error("Encoding packet:", e); + return; + } + save(encoded); + } + public void save(Packet packet) { // If we're not on the main thread (i.e. we're on the netty thread), then we need to schedule the saving // to happen on the main thread so we can guarantee correct ordering of inbound and inject packets. @@ -169,24 +177,12 @@ public class PacketListener extends ChannelInboundHandlerAdapter { return; } try { - if(packet instanceof PlayerSpawnS2CPacket) { - //#if MC>=10800 - UUID uuid = ((PlayerSpawnS2CPacket) packet).getPlayerUuid(); - //#else - //$$ UUID uuid = ((S0CPacketSpawnPlayer) packet).func_148948_e().getId(); - //#endif - Set uuids = new HashSet<>(Arrays.asList(metaData.getPlayers())); - uuids.add(uuid.toString()); - metaData.setPlayers(uuids.toArray(new String[uuids.size()])); - saveMetaData(); - } - - //#if MC>=10800 - if (packet instanceof LoginCompressionS2CPacket) { + //#if MC>=11800 + if (packet.getRegistry().getState() == State.LOGIN && packet.getId() == PACKET_ID_LOGIN_COMPRESSION) { return; // Replay data is never compressed on the packet level } //#if MC<10904 - //$$ if (packet instanceof S46PacketSetCompressionLevel) { + //$$ if (packet.getRegistry().getState() == State.PLAY && packet.getId() == PACKET_ID_PLAY_COMPRESSION) { //$$ return; // Replay data is never compressed on the packet level //$$ } //#endif @@ -199,7 +195,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter { } int timestamp = (int) (now - startTime - timePassedWhilePaused); lastSentPacket = timestamp; - PacketData packetData = getPacketData(timestamp, packet); + PacketData packetData = new PacketData(timestamp, packet); saveService.submit(() -> { try { if (ReplayMod.isMinimalMode()) { @@ -226,18 +222,27 @@ public class PacketListener extends ChannelInboundHandlerAdapter { throw new RuntimeException(e); } }); - - //#if MC>=11400 - if (packet instanceof LoginSuccessS2CPacket) { - connectionState = NetworkState.PLAY; - loginPhase = false; - } - //#endif } catch(Exception e) { logger.error("Writing packet:", e); } } + @Override + public void handlerAdded(ChannelHandlerContext ctx) throws Exception { + super.handlerAdded(ctx); + + if (ctx.pipeline().get(DECODED_RECORDER_KEY) == null) { + if (ctx.pipeline().get(PacketListener.DECODER_KEY) != null) { + // Regular channel, we'll inject our decoded recorder directly after the decoder + ctx.pipeline().addAfter(DECODER_KEY, DECODED_RECORDER_KEY, new DecodedPacketListener()); + } else { + // Integrated server passes packets directly, there's no splitting, decompression or decoding + // The decoded packet handler can just go directly behind this hand + ctx.pipeline().addAfter(RAW_RECORDER_KEY, DECODED_RECORDER_KEY, new DecodedPacketListener()); + } + } + } + @Override public void channelInactive(ChannelHandlerContext ctx) { metaData.setDuration((int) lastSentPacket); @@ -324,139 +329,44 @@ public class PacketListener extends ChannelInboundHandlerAdapter { } this.context = ctx; - if (msg instanceof Packet) { - try { - Packet packet = (Packet) msg; + NetworkState connectionState = getConnectionState(); - //#if MC>=10904 - if(packet instanceof ItemPickupAnimationS2CPacket) { - if(mc.player != null || - ((ItemPickupAnimationS2CPacket) packet).getEntityId() == mc.player.getEntityId()) { - //#else - //$$ if(packet instanceof S0DPacketCollectItem) { - //$$ if(mc.thePlayer != null || ((S0DPacketCollectItem) packet).getEntityID() == mc.thePlayer.getEntityId()) { - //#endif - super.channelRead(ctx, msg); - return; - } - } + Packet packet = null; + if (msg instanceof ByteBuf) { + // for regular connections, we're expecting to observe `ByteBuf`s here + ByteBuf buf = (ByteBuf) msg; + if (buf.readableBytes() > 0) { + packet = decodePacket(connectionState, buf); + } + } else if (msg instanceof net.minecraft.network.Packet) { + // for integrated server connections MC is passing the packet objects directly, so we need to encode them + // ourselves to be able to store them + packet = encodeMcPacket(connectionState, (net.minecraft.network.Packet) msg); + } - //#if MC>=10800 - if (packet instanceof ResourcePackSendS2CPacket) { - ClientConnection connection = ctx.pipeline().get(ClientConnection.class); - save(resourcePackRecorder.handleResourcePack(connection, (ResourcePackSendS2CPacket) packet)); - return; - } - //#else - //$$ if (packet instanceof S3FPacketCustomPayload) { - //$$ S3FPacketCustomPayload p = (S3FPacketCustomPayload) packet; - //$$ if ("MC|RPack".equals(p.func_149169_c())) { - //$$ save(resourcePackRecorder.handleResourcePack(p)); - //$$ return; - //$$ } - //$$ } - //#endif - - //#if MC<11400 - //$$ if (packet instanceof FMLProxyPacket) { - //$$ // This packet requires special handling - //#if MC>=10800 - //$$ ((FMLProxyPacket) packet).toS3FPackets().forEach(this::save); - //#else - //$$ save(((FMLProxyPacket) packet).toS3FPacket()); - //#endif - //$$ super.channelRead(ctx, msg); - //$$ return; - //$$ } - //#endif - - //#if MC>=10800 - if (packet instanceof CustomPayloadS2CPacket) { - // Forge may read from this ByteBuf and/or release it during handling - // We want to save the full thing however, so we create a copy and save that one instead of the - // original one - // Note: This isn't an issue with vanilla MC because our saving code runs on the main thread - // shortly before the vanilla handling code does. Forge however does some stuff on the netty - // threads which leads to this race condition - packet = new CustomPayloadS2CPacket( - ((CustomPayloadS2CPacket) packet).getChannel(), - new PacketByteBuf(((CustomPayloadS2CPacket) packet).getData().slice().retain()) - ); - } - //#endif - - save(packet); - - if (packet instanceof CustomPayloadS2CPacket) { - CustomPayloadS2CPacket p = (CustomPayloadS2CPacket) packet; - if (Restrictions.PLUGIN_CHANNEL.equals(p.getChannel())) { - packet = new DisconnectS2CPacket(new LiteralText("Please update to view this replay.")); - save(packet); - } - } - } catch(Exception e) { - logger.error("Handling packet for recording:", e); + if (packet != null) { + if (connectionState == NetworkState.PLAY && packet.getId() == PACKET_ID_RESOURCE_PACK_SEND) { + ClientConnection connection = ctx.pipeline().get(ClientConnection.class); + save(resourcePackRecorder.handleResourcePack(connection, (ResourcePackSendS2CPacket) decodeMcPacket(packet))); + return; } + save(packet); } super.channelRead(ctx, msg); } - //#if MC>=10904 - private void DataManager_set(DataTracker dataManager, DataTracker.Entry entry) { - dataManager.startTracking(entry.getData(), entry.get()); + private NetworkState getConnectionState() { + ChannelHandlerContext ctx = context; + if (ctx == null) { + return NetworkState.LOGIN; + } + AttributeKey key = ClientConnection.ATTR_KEY_PROTOCOL; + return ctx.channel().attr(key).get(); } - //#endif - - @SuppressWarnings("unchecked") - private PacketData getPacketData(int timestamp, Packet packet) throws Exception { - //#if MC<11500 - //$$ if (packet instanceof MobSpawnS2CPacket) { - //$$ MobSpawnS2CPacket p = (MobSpawnS2CPacket) packet; - //$$ SPacketSpawnMobAccessor pa = (SPacketSpawnMobAccessor) p; - //$$ if (pa.getDataManager() == null) { - //$$ pa.setDataManager(new DataTracker(null)); - //$$ if (p.getTrackedValues() != null) { - //$$ Set seen = new HashSet<>(); - //#if MC>=10904 - //$$ for (DataTracker.Entry entry : Lists.reverse(p.getTrackedValues())) { - //$$ if (!seen.add(entry.getData().getId())) continue; - //$$ DataManager_set(pa.getDataManager(), entry); - //$$ } - //#else - //$$ for(DataWatcher.WatchableObject wo : Lists.reverse((List) p.func_149027_c())) { - //$$ if (!seen.add(wo.getDataValueId())) continue; - //$$ pa.getDataManager().addObject(wo.getDataValueId(), wo.getObject()); - //$$ } - //#endif - //$$ } - //$$ } - //$$ } - //$$ - //$$ if (packet instanceof PlayerSpawnS2CPacket) { - //$$ PlayerSpawnS2CPacket p = (PlayerSpawnS2CPacket) packet; - //$$ SPacketSpawnPlayerAccessor pa = (SPacketSpawnPlayerAccessor) p; - //$$ if (pa.getDataManager() == null) { - //$$ pa.setDataManager(new DataTracker(null)); - //$$ if (p.getTrackedValues() != null) { - //$$ Set seen = new HashSet<>(); - //#if MC>=10904 - //$$ for (DataTracker.Entry entry : Lists.reverse(p.getTrackedValues())) { - //$$ if (!seen.add(entry.getData().getId())) continue; - //$$ DataManager_set(pa.getDataManager(), entry); - //$$ } - //#else - //$$ for(DataWatcher.WatchableObject wo : Lists.reverse((List) p.func_148944_c())) { - //$$ if (!seen.add(wo.getDataValueId())) continue; - //$$ pa.getDataManager().addObject(wo.getDataValueId(), wo.getObject()); - //$$ } - //#endif - //$$ } - //$$ } - //$$ } - //#endif + private static Packet encodeMcPacket(NetworkState connectionState, net.minecraft.network.Packet packet) throws Exception { //#if MC>=10800 Integer packetId = connectionState.getPacketId(NetworkSide.CLIENTBOUND, packet); //#else @@ -468,23 +378,55 @@ public class PacketListener extends ChannelInboundHandlerAdapter { ByteBuf byteBuf = Unpooled.buffer(); try { packet.write(new PacketByteBuf(byteBuf)); - return new PacketData(timestamp, new com.replaymod.replaystudio.protocol.Packet( - MCVer.getPacketTypeRegistry(loginPhase), + return new Packet( + MCVer.getPacketTypeRegistry(connectionState == NetworkState.LOGIN), packetId, com.github.steveice10.netty.buffer.Unpooled.wrappedBuffer( byteBuf.array(), byteBuf.arrayOffset(), byteBuf.readableBytes() ) - )); + ); } finally { byteBuf.release(); + } + } - //#if MC>=10800 - if (packet instanceof CustomPayloadS2CPacket) { - ((CustomPayloadS2CPacket) packet).getData().release(); - } - //#endif + private static net.minecraft.network.Packet decodeMcPacket(Packet packet) throws IOException, IllegalAccessException, InstantiationException { + NetworkState connectionState = packet.getRegistry().getState() == State.LOGIN ? NetworkState.LOGIN : NetworkState.PLAY; + int packetId = packet.getId(); + PacketByteBuf packetBuf = new PacketByteBuf(Unpooled.wrappedBuffer(packet.getBuf().nioBuffer())); + + //#if MC>=11700 + //$$ return connectionState.getPacketHandler(NetworkSide.CLIENTBOUND, packetId, packetBuf); + //#else + //#if MC>=10800 + net.minecraft.network.Packet p = connectionState.getPacketHandler(NetworkSide.CLIENTBOUND, packetId); + //#else + //$$ net.minecraft.network.Packet p = net.minecraft.network.Packet.generatePacket(connectionState.func_150755_b(), packetId); + //#endif + p.read(packetBuf); + return p; + //#endif + } + + private static Packet decodePacket(NetworkState connectionState, ByteBuf buf) { + PacketByteBuf packetBuf = new PacketByteBuf(buf.slice()); + int packetId = packetBuf.readVarInt(); + byte[] bytes = new byte[packetBuf.readableBytes()]; + packetBuf.readBytes(bytes); + return new Packet( + MCVer.getPacketTypeRegistry(connectionState == NetworkState.LOGIN), + packetId, + com.github.steveice10.netty.buffer.Unpooled.wrappedBuffer(bytes) + ); + } + + private static int getPacketId(NetworkState networkState, net.minecraft.network.Packet packet) { + try { + return requireNonNull(networkState.getPacketId(NetworkSide.CLIENTBOUND, packet)); + } catch (Exception e) { + throw new RuntimeException("Failed to determine packet id for " + packet.getClass(), e); } } @@ -526,4 +468,31 @@ public class PacketListener extends ChannelInboundHandlerAdapter { public void setServerWasPaused() { this.serverWasPaused = true; } + + private class DecodedPacketListener extends ChannelInboundHandlerAdapter { + @Override + public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception { + + if (msg instanceof CustomPayloadS2CPacket) { + CustomPayloadS2CPacket packet = (CustomPayloadS2CPacket) msg; + if (Restrictions.PLUGIN_CHANNEL.equals(packet.getChannel())) { + save(new DisconnectS2CPacket(new LiteralText("Please update to view this replay."))); + } + } + + if (msg instanceof PlayerSpawnS2CPacket) { + //#if MC>=10800 + UUID uuid = ((PlayerSpawnS2CPacket) msg).getPlayerUuid(); + //#else + //$$ UUID uuid = ((S0CPacketSpawnPlayer) msg).func_148948_e().getId(); + //#endif + Set uuids = new HashSet<>(Arrays.asList(metaData.getPlayers())); + uuids.add(uuid.toString()); + metaData.setPlayers(uuids.toArray(new String[uuids.size()])); + saveMetaData(); + } + + super.channelRead(ctx, msg); + } + } } diff --git a/src/main/java/com/replaymod/recording/packet/ResourcePackRecorder.java b/src/main/java/com/replaymod/recording/packet/ResourcePackRecorder.java index 689f2cfd..01fd26c9 100644 --- a/src/main/java/com/replaymod/recording/packet/ResourcePackRecorder.java +++ b/src/main/java/com/replaymod/recording/packet/ResourcePackRecorder.java @@ -11,6 +11,11 @@ import net.minecraft.client.resource.ClientBuiltinResourcePackProvider; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; +//#if MC>=11900 +//$$ import java.net.MalformedURLException; +//$$ import java.net.URL; +//#endif + //#if MC>=11400 import net.minecraft.text.TranslatableText; //#else @@ -192,7 +197,18 @@ public class ResourcePackRecorder { downloadResourcePack(final int requestId, String url, String hash) { ClientBuiltinResourcePackProvider packFinder = mc.getResourcePackDownloader(); ((IDownloadingPackFinder) packFinder).setRequestCallback(file -> recordResourcePack(file, requestId)); - //#if MC>=11700 + //#if MC>=11900 + //$$ try { + //$$ URL theUrl = new URL(url); + //$$ String protocol = theUrl.getProtocol(); + //$$ if (!"http".equals(protocol) && !"https".equals(protocol)) { + //$$ throw new MalformedURLException("Unsupported protocol."); + //$$ } + //$$ return packFinder.download(theUrl, hash, true); + //$$ } catch (MalformedURLException e) { + //$$ return CompletableFuture.failedFuture(e); + //$$ } + //#elseif MC>=11700 //$$ return packFinder.download(url, hash, true); //#else return packFinder.download(url, hash); diff --git a/src/main/java/com/replaymod/render/EXRWriter.java b/src/main/java/com/replaymod/render/EXRWriter.java index 0bf981d8..2b072bda 100644 --- a/src/main/java/com/replaymod/render/EXRWriter.java +++ b/src/main/java/com/replaymod/render/EXRWriter.java @@ -1,4 +1,3 @@ -//#if MC>=11400 package com.replaymod.render; import com.replaymod.core.versions.MCVer; @@ -6,6 +5,7 @@ import com.replaymod.render.frame.BitmapFrame; import com.replaymod.render.rendering.Channel; import com.replaymod.render.rendering.FrameConsumer; import com.replaymod.render.utils.ByteBufferPool; +import com.replaymod.render.utils.Lwjgl3Loader; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; import net.minecraft.util.crash.CrashReport; import org.lwjgl.PointerBuffer; @@ -27,6 +27,19 @@ import static org.lwjgl.util.tinyexr.TinyEXR.*; public class EXRWriter implements FrameConsumer { + public static FrameConsumer create(Path outputFolder, boolean keepAlpha) { + return Lwjgl3Loader.createFrameConsumer( + EXRWriter.class, + new Class[]{ Path.class, boolean.class }, + new Object[]{ outputFolder, keepAlpha } + ); + } + + // Compression is pretty slow, so we'll only use it when we've got enough cpu cores to make up for that + private static final int COMPRESSION = Runtime.getRuntime().availableProcessors() >= 8 + ? TINYEXR_COMPRESSIONTYPE_ZIPS + : TINYEXR_COMPRESSIONTYPE_NONE; + private final Path outputFolder; private final boolean keepAlpha; @@ -63,6 +76,7 @@ public class EXRWriter implements FrameConsumer { header.channels(channelInfos); header.pixel_types(pixelTypes); header.requested_pixel_types(requestedPixelTypes); + header.compression_type(COMPRESSION); // Some readers ignore this, so we use the most expected order memASCII("A", true, channelInfos.get(0).name()); @@ -127,5 +141,9 @@ public class EXRWriter implements FrameConsumer { @Override public void close() { } + + @Override + public boolean isParallelCapable() { + return true; + } } -//#endif diff --git a/src/main/java/com/replaymod/render/FFmpegWriter.java b/src/main/java/com/replaymod/render/FFmpegWriter.java index f4e1381c..332f346c 100644 --- a/src/main/java/com/replaymod/render/FFmpegWriter.java +++ b/src/main/java/com/replaymod/render/FFmpegWriter.java @@ -134,6 +134,11 @@ public class FFmpegWriter implements FrameConsumer { } } + @Override + public boolean isParallelCapable() { + return false; + } + private void checkSize(ReadableDimension size) { checkSize(size.getWidth(), size.getHeight()); } diff --git a/src/main/java/com/replaymod/render/PNGWriter.java b/src/main/java/com/replaymod/render/PNGWriter.java index 525d154d..8912dcb0 100644 --- a/src/main/java/com/replaymod/render/PNGWriter.java +++ b/src/main/java/com/replaymod/render/PNGWriter.java @@ -71,4 +71,9 @@ public class PNGWriter implements FrameConsumer { @Override public void close() { } + + @Override + public boolean isParallelCapable() { + return true; + } } diff --git a/src/main/java/com/replaymod/render/RenderSettings.java b/src/main/java/com/replaymod/render/RenderSettings.java index b358b718..f5e92ffb 100644 --- a/src/main/java/com/replaymod/render/RenderSettings.java +++ b/src/main/java/com/replaymod/render/RenderSettings.java @@ -105,13 +105,6 @@ public class RenderSettings { public boolean isSupported() { if (this == BLEND) { return RenderMethod.BLEND.isSupported(); - } else if (this == EXR) { - // Need LJWGL 3 - //#if MC>=11400 - return true; - //#else - //$$ return false; - //#endif } else { return true; } diff --git a/src/main/java/com/replaymod/render/blend/BlendMeshBuilder.java b/src/main/java/com/replaymod/render/blend/BlendMeshBuilder.java index 8b9e2f50..de61bb5f 100644 --- a/src/main/java/com/replaymod/render/blend/BlendMeshBuilder.java +++ b/src/main/java/com/replaymod/render/blend/BlendMeshBuilder.java @@ -1,4 +1,4 @@ -//#if MC>=10800 +//#if MC>=10800 && MC<11900 package com.replaymod.render.blend; import com.replaymod.render.blend.data.DMaterial; diff --git a/src/main/java/com/replaymod/render/blend/BlendState.java b/src/main/java/com/replaymod/render/blend/BlendState.java index 0ef4a29b..a13c02a3 100644 --- a/src/main/java/com/replaymod/render/blend/BlendState.java +++ b/src/main/java/com/replaymod/render/blend/BlendState.java @@ -2,7 +2,7 @@ package com.replaymod.render.blend; import com.replaymod.render.blend.data.DScene; import com.replaymod.render.blend.data.Serializer; -//#if MC>=10800 +//#if MC>=10800 && MC<11900 // FIXME 1.15 //#if MC<11500 //$$ import com.replaymod.render.blend.exporters.ChunkExporter; @@ -53,7 +53,7 @@ public class BlendState implements Exporter { this.blenderFile = BlenderFactory.newBlenderFile(file); this.factory = new BlenderFactory(blenderFile); - //#if MC>=10800 + //#if MC>=10800 && MC<11900 RenderState renderState = new RenderState(this); register(renderState); // FIXME 1.15 diff --git a/src/main/java/com/replaymod/render/blend/exporters/EntityExporter.java b/src/main/java/com/replaymod/render/blend/exporters/EntityExporter.java index 8b6834b7..d4dfda21 100644 --- a/src/main/java/com/replaymod/render/blend/exporters/EntityExporter.java +++ b/src/main/java/com/replaymod/render/blend/exporters/EntityExporter.java @@ -1,4 +1,4 @@ -//#if MC>=10800 +//#if MC>=10800 && MC<11900 package com.replaymod.render.blend.exporters; import com.replaymod.core.versions.MCVer; diff --git a/src/main/java/com/replaymod/render/blend/exporters/ItemExporter.java b/src/main/java/com/replaymod/render/blend/exporters/ItemExporter.java index b25b6556..368e55b8 100644 --- a/src/main/java/com/replaymod/render/blend/exporters/ItemExporter.java +++ b/src/main/java/com/replaymod/render/blend/exporters/ItemExporter.java @@ -1,4 +1,4 @@ -//#if MC>=10800 +//#if MC>=10800 && MC<11900 package com.replaymod.render.blend.exporters; import com.replaymod.render.blend.BlendMeshBuilder; diff --git a/src/main/java/com/replaymod/render/blend/exporters/ModelRendererExporter.java b/src/main/java/com/replaymod/render/blend/exporters/ModelRendererExporter.java index 2320f236..9fba791c 100644 --- a/src/main/java/com/replaymod/render/blend/exporters/ModelRendererExporter.java +++ b/src/main/java/com/replaymod/render/blend/exporters/ModelRendererExporter.java @@ -1,4 +1,4 @@ -//#if MC>=10800 +//#if MC>=10800 && MC<11900 package com.replaymod.render.blend.exporters; import com.replaymod.render.blend.BlendMeshBuilder; diff --git a/src/main/java/com/replaymod/render/blend/exporters/ParticlesExporter.java b/src/main/java/com/replaymod/render/blend/exporters/ParticlesExporter.java index 2aecf1a4..ec164ed6 100644 --- a/src/main/java/com/replaymod/render/blend/exporters/ParticlesExporter.java +++ b/src/main/java/com/replaymod/render/blend/exporters/ParticlesExporter.java @@ -1,4 +1,4 @@ -//#if MC>=10800 +//#if MC>=10800 && MC<11900 package com.replaymod.render.blend.exporters; import com.replaymod.core.versions.MCVer; diff --git a/src/main/java/com/replaymod/render/blend/exporters/RenderState.java b/src/main/java/com/replaymod/render/blend/exporters/RenderState.java index 9f385ce3..72b7abb9 100644 --- a/src/main/java/com/replaymod/render/blend/exporters/RenderState.java +++ b/src/main/java/com/replaymod/render/blend/exporters/RenderState.java @@ -1,4 +1,4 @@ -//#if MC>=10800 +//#if MC>=10800 && MC<11900 package com.replaymod.render.blend.exporters; import com.replaymod.render.blend.BlendState; diff --git a/src/main/java/com/replaymod/render/blend/exporters/TileEntityExporter.java b/src/main/java/com/replaymod/render/blend/exporters/TileEntityExporter.java index 0fd15a81..18b0ce01 100644 --- a/src/main/java/com/replaymod/render/blend/exporters/TileEntityExporter.java +++ b/src/main/java/com/replaymod/render/blend/exporters/TileEntityExporter.java @@ -1,4 +1,4 @@ -//#if MC>=10800 +//#if MC>=10800 && MC<11900 package com.replaymod.render.blend.exporters; import com.replaymod.core.versions.MCVer; diff --git a/src/main/java/com/replaymod/render/blend/mixin/MixinRenderGlobal.java b/src/main/java/com/replaymod/render/blend/mixin/MixinRenderGlobal.java index af495f1d..683b33f1 100644 --- a/src/main/java/com/replaymod/render/blend/mixin/MixinRenderGlobal.java +++ b/src/main/java/com/replaymod/render/blend/mixin/MixinRenderGlobal.java @@ -1,4 +1,4 @@ -//#if MC>=10800 +//#if MC>=10800 && MC<11900 package com.replaymod.render.blend.mixin; import com.replaymod.render.blend.BlendState; diff --git a/src/main/java/com/replaymod/render/blend/mixin/MixinRenderItem.java b/src/main/java/com/replaymod/render/blend/mixin/MixinRenderItem.java index cf41e5e2..48a86d7f 100644 --- a/src/main/java/com/replaymod/render/blend/mixin/MixinRenderItem.java +++ b/src/main/java/com/replaymod/render/blend/mixin/MixinRenderItem.java @@ -1,4 +1,4 @@ -//#if MC>=10800 +//#if MC>=10800 && MC<11900 package com.replaymod.render.blend.mixin; import com.replaymod.render.blend.BlendState; diff --git a/src/main/java/com/replaymod/render/blend/mixin/MixinRenderLivingBase.java b/src/main/java/com/replaymod/render/blend/mixin/MixinRenderLivingBase.java index 04efa633..fa247467 100644 --- a/src/main/java/com/replaymod/render/blend/mixin/MixinRenderLivingBase.java +++ b/src/main/java/com/replaymod/render/blend/mixin/MixinRenderLivingBase.java @@ -1,4 +1,4 @@ -//#if MC>=10800 +//#if MC>=10800 && MC<11900 package com.replaymod.render.blend.mixin; import com.replaymod.render.blend.BlendState; diff --git a/src/main/java/com/replaymod/render/blend/mixin/MixinRenderManager.java b/src/main/java/com/replaymod/render/blend/mixin/MixinRenderManager.java index 57764995..1dfb808c 100644 --- a/src/main/java/com/replaymod/render/blend/mixin/MixinRenderManager.java +++ b/src/main/java/com/replaymod/render/blend/mixin/MixinRenderManager.java @@ -1,4 +1,4 @@ -//#if MC>=10800 +//#if MC>=10800 && MC<11900 package com.replaymod.render.blend.mixin; import com.replaymod.render.blend.BlendState; diff --git a/src/main/java/com/replaymod/render/mixin/MixinParticleManager.java b/src/main/java/com/replaymod/render/mixin/MixinParticleManager.java index 456dceac..98eafe37 100644 --- a/src/main/java/com/replaymod/render/mixin/MixinParticleManager.java +++ b/src/main/java/com/replaymod/render/mixin/MixinParticleManager.java @@ -3,7 +3,6 @@ package com.replaymod.render.mixin; //#if MC>=10904 import com.replaymod.core.versions.MCVer; import com.replaymod.render.blend.BlendState; -import com.replaymod.render.blend.exporters.ParticlesExporter; import com.replaymod.render.hooks.EntityRendererHandler; import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.ParticleManager; @@ -12,6 +11,11 @@ import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Redirect; +//#if MC>=11900 +//#else +import com.replaymod.render.blend.exporters.ParticlesExporter; +//#endif + //#if MC>=11500 import net.minecraft.client.render.VertexConsumer; import net.minecraft.util.math.Quaternion; @@ -52,10 +56,12 @@ public abstract class MixinParticleManager { } private void buildGeometry(Particle particle, VertexConsumer vertexConsumer, Camera camera, float partialTicks) { + //#if MC<11900 BlendState blendState = BlendState.getState(); if (blendState != null) { blendState.get(ParticlesExporter.class).onRender(particle, partialTicks); } + //#endif particle.buildGeometry(vertexConsumer, camera, partialTicks); } //#else diff --git a/src/main/java/com/replaymod/render/mixin/Mixin_WindowsWorkaroundForTinyEXRNatives.java b/src/main/java/com/replaymod/render/mixin/Mixin_WindowsWorkaroundForTinyEXRNatives.java deleted file mode 100644 index 3df81c08..00000000 --- a/src/main/java/com/replaymod/render/mixin/Mixin_WindowsWorkaroundForTinyEXRNatives.java +++ /dev/null @@ -1,81 +0,0 @@ -//#if MC>=11400 -package com.replaymod.render.mixin; - -import org.lwjgl.system.Library; -import org.lwjgl.system.Platform; -import org.lwjgl.util.tinyexr.TinyEXR; -import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.ModifyArg; - -import java.io.File; -import java.io.IOException; -import java.io.InputStream; -import java.net.URL; -import java.nio.file.Files; -import java.nio.file.Path; -import java.nio.file.Paths; -import java.nio.file.StandardCopyOption; -import java.util.function.Consumer; -import java.util.regex.Pattern; - -/** - * It appears like natives on Windows cannot be loaded if one of their dependencies has already been loaded by a - * different class loader. In our case we cannot load tinyexr (on the knot class loader) because lwjgl has already - * been loaded on the system class loader. - * - * If we force the tinyexr native to load on the system class loader (by calling `Library.loadSystem(absPath)`), - * it'll load but we'll get an error when we call any of the native methods. - * - * We can't really load TinyEXR itself via the system class loader because Java does not provide any methods for - * modifying the system class path at runtime and we'd have to use JVM-specific hacks. - * - * Strangely, if we use System.loadLibrary instead of System.load, then it all just works. This mixin implements - * that workaround by finding MC's natives folder, extracting the dll from our jar into that folder and then replacing - * the context class passed to Library.loadSystem (which it uses to find dlls in jars) with Library (which is on the - * system class loader) so it cannot find the dll in our jar and falls back to using System.loadLibrary. - */ -@Mixin(value = TinyEXR.class, remap = false) -public class Mixin_WindowsWorkaroundForTinyEXRNatives { - private static final String LOAD_SYSTEM_CONSUMERS = "Lorg/lwjgl/system/Library;loadSystem(Ljava/util/function/Consumer;Ljava/util/function/Consumer;Ljava/lang/Class;Ljava/lang/String;)V"; - - @ModifyArg(method = "", at = @At(value = "INVOKE", target = LOAD_SYSTEM_CONSUMERS)) - private static Class uglyWindowsHacks(Consumer load, Consumer loadLibrary, Class context, String name) throws IOException { - if (Platform.get() != Platform.WINDOWS) { - return context; // works out of the box on linux - } - - name = System.mapLibraryName(name); - - URL libURL = context.getClassLoader().getResource(name); - if (libURL == null) { - throw new UnsatisfiedLinkError("Failed to locate library: " + name); - } - - String lwjglLibName = Library.JNI_LIBRARY_NAME; - if (!lwjglLibName.endsWith(".dll")) { - lwjglLibName = System.mapLibraryName(lwjglLibName); - } - - String paths = System.getProperty("java.library.path"); - Path nativesDir = null; - for (String dir : Pattern.compile(File.pathSeparator).split(paths)) { - Path path = Paths.get(dir); - if (Files.isReadable(path.resolve(lwjglLibName))) { - nativesDir = path; - break; - } - } - if (nativesDir == null) { - throw new UnsatisfiedLinkError("Failed to locate natives folder in " + paths); - } - - Path libPath = nativesDir.resolve(name); - try (InputStream source = libURL.openStream()) { - Files.copy(source, libPath, StandardCopyOption.REPLACE_EXISTING); - } - - return Library.class; - } -} -//#endif diff --git a/src/main/java/com/replaymod/render/rendering/FrameConsumer.java b/src/main/java/com/replaymod/render/rendering/FrameConsumer.java index fd74586c..ae654ca0 100644 --- a/src/main/java/com/replaymod/render/rendering/FrameConsumer.java +++ b/src/main/java/com/replaymod/render/rendering/FrameConsumer.java @@ -7,4 +7,6 @@ public interface FrameConsumer

extends Closeable { void consume(Map channels); + boolean isParallelCapable(); + } diff --git a/src/main/java/com/replaymod/render/rendering/Pipeline.java b/src/main/java/com/replaymod/render/rendering/Pipeline.java index cf7f057d..8696fe29 100644 --- a/src/main/java/com/replaymod/render/rendering/Pipeline.java +++ b/src/main/java/com/replaymod/render/rendering/Pipeline.java @@ -10,6 +10,7 @@ import net.minecraft.util.crash.CrashException; import net.minecraft.util.crash.CrashReport; import org.lwjgl.glfw.GLFW; +import java.io.IOException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.ArrayBlockingQueue; @@ -25,8 +26,6 @@ public class Pipeline implements Runnable { private final FrameCapturer capturer; private final FrameProcessor processor; private final GlToAbsoluteDepthProcessor depthProcessor; - private int consumerNextFrame; - private final Object consumerLock = new Object(); private final FrameConsumer

consumer; private volatile boolean abort; @@ -35,7 +34,7 @@ public class Pipeline implements Runnable { this.worldRenderer = worldRenderer; this.capturer = capturer; this.processor = processor; - this.consumer = consumer; + this.consumer = new ParallelSafeConsumer<>(consumer); float near = 0.05f; float far = getMinecraft().options.viewDistance * 16 * 4; @@ -44,7 +43,6 @@ public class Pipeline implements Runnable { @Override public synchronized void run() { - consumerNextFrame = 0; int processors = Runtime.getRuntime().availableProcessors(); int processThreads = Math.max(1, processors - 2); // One processor for the main thread and one for ffmpeg, sorry OS :( ExecutorService processService = new ThreadPoolExecutor(processThreads, processThreads, @@ -106,7 +104,6 @@ public class Pipeline implements Runnable { @Override public void run() { try { - Integer frameId = null; Map processedChannels = new HashMap<>(); for (Map.Entry entry : rawChannels.entrySet()) { P processedFrame = processor.process(entry.getValue()); @@ -114,27 +111,57 @@ public class Pipeline implements Runnable { depthProcessor.process((BitmapFrame) processedFrame); } processedChannels.put(entry.getKey(), processedFrame); - frameId = processedFrame.getFrameId(); } - if (frameId == null) { + if (processedChannels.isEmpty()) { return; } - synchronized (consumerLock) { - while (consumerNextFrame != frameId) { - try { - consumerLock.wait(); - } catch (InterruptedException e) { - Thread.currentThread().interrupt(); - } - } - consumer.consume(processedChannels); - consumerNextFrame++; - consumerLock.notifyAll(); - } + consumer.consume(processedChannels); } catch (Throwable t) { CrashReport crashReport = CrashReport.create(t, "Processing frame"); MCVer.getMinecraft().setCrashReport(crashReport); } } } + + private static class ParallelSafeConsumer

implements FrameConsumer

{ + private final FrameConsumer

inner; + + private int nextFrame; + private final Object lock = new Object(); + + private ParallelSafeConsumer(FrameConsumer

inner) { + this.inner = inner; + } + + @Override + public void consume(Map channels) { + if (inner.isParallelCapable()) { + inner.consume(channels); + } else { + int frameId = channels.values().iterator().next().getFrameId(); + synchronized (lock) { + while (nextFrame != frameId) { + try { + lock.wait(); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + inner.consume(channels); + nextFrame++; + lock.notifyAll(); + } + } + } + + @Override + public boolean isParallelCapable() { + return true; + } + + @Override + public void close() throws IOException { + inner.close(); + } + } } diff --git a/src/main/java/com/replaymod/render/rendering/Pipelines.java b/src/main/java/com/replaymod/render/rendering/Pipelines.java index e012fb41..bc65fba2 100644 --- a/src/main/java/com/replaymod/render/rendering/Pipelines.java +++ b/src/main/java/com/replaymod/render/rendering/Pipelines.java @@ -128,6 +128,11 @@ public class Pipelines { @Override public void close() { } + + @Override + public boolean isParallelCapable() { + return true; + } }; return new Pipeline<>(worldRenderer, capturer, new DummyProcessor<>(), consumer); } diff --git a/src/main/java/com/replaymod/render/rendering/VideoRenderer.java b/src/main/java/com/replaymod/render/rendering/VideoRenderer.java index a6b31504..92021f8b 100644 --- a/src/main/java/com/replaymod/render/rendering/VideoRenderer.java +++ b/src/main/java/com/replaymod/render/rendering/VideoRenderer.java @@ -8,6 +8,7 @@ import com.replaymod.core.versions.MCVer; import com.replaymod.pathing.player.AbstractTimelinePlayer; import com.replaymod.pathing.properties.TimestampProperty; import com.replaymod.render.CameraPathExporter; +import com.replaymod.render.EXRWriter; import com.replaymod.render.PNGWriter; import com.replaymod.render.RenderSettings; import com.replaymod.render.ReplayModRender; @@ -55,7 +56,6 @@ import org.lwjgl.opengl.GL11; //#endif //#if MC>=11400 -import com.replaymod.render.EXRWriter; import net.minecraft.client.gui.screen.Screen; import java.util.concurrent.CompletableFuture; //#else @@ -129,11 +129,7 @@ public class VideoRenderer implements RenderInfo { } else { FrameConsumer frameConsumer; if (settings.getEncodingPreset() == RenderSettings.EncodingPreset.EXR) { - //#if MC>=11400 - frameConsumer = new EXRWriter(settings.getOutputFile().toPath(), settings.isIncludeAlphaChannel()); - //#else - //$$ throw new UnsupportedOperationException("EXR requires LWJGL3"); - //#endif + frameConsumer = EXRWriter.create(settings.getOutputFile().toPath(), settings.isIncludeAlphaChannel()); } else if (settings.getEncodingPreset() == RenderSettings.EncodingPreset.PNG) { frameConsumer = new PNGWriter(settings.getOutputFile().toPath(), settings.isIncludeAlphaChannel()); } else { @@ -141,11 +137,19 @@ public class VideoRenderer implements RenderInfo { } ffmpegWriter = frameConsumer instanceof FFmpegWriter ? (FFmpegWriter) frameConsumer : null; FrameConsumer previewingFrameConsumer = new FrameConsumer() { + private int lastFrameId = -1; + @Override public void consume(Map channels) { BitmapFrame bgra = channels.get(Channel.BRGA); if (bgra != null) { - gui.updatePreview(bgra.getByteBuffer(), bgra.getSize()); + synchronized (this) { + int frameId = bgra.getFrameId(); + if (lastFrameId < frameId) { + lastFrameId = frameId; + gui.updatePreview(bgra.getByteBuffer(), bgra.getSize()); + } + } } frameConsumer.consume(channels); } @@ -154,6 +158,11 @@ public class VideoRenderer implements RenderInfo { public void close() throws IOException { frameConsumer.close(); } + + @Override + public boolean isParallelCapable() { + return frameConsumer.isParallelCapable(); + } }; this.renderingPipeline = Pipelines.newPipeline(settings.getRenderMethod(), this, previewingFrameConsumer); } diff --git a/src/main/java/com/replaymod/render/utils/Lwjgl3Loader.java b/src/main/java/com/replaymod/render/utils/Lwjgl3Loader.java new file mode 100644 index 00000000..8c832acd --- /dev/null +++ b/src/main/java/com/replaymod/render/utils/Lwjgl3Loader.java @@ -0,0 +1,132 @@ +package com.replaymod.render.utils; + +import com.replaymod.core.ReplayMod; +import com.replaymod.render.rendering.Frame; +import com.replaymod.render.rendering.FrameConsumer; +import org.apache.commons.io.IOUtils; + +import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.Constructor; +import java.lang.reflect.Method; +import java.net.URL; +import java.net.URLClassLoader; +import java.nio.file.Files; +import java.nio.file.Path; +import java.security.ProtectionDomain; +import java.util.Set; +import java.util.concurrent.CopyOnWriteArraySet; + +import static java.nio.file.StandardCopyOption.REPLACE_EXISTING; + +public class Lwjgl3Loader extends URLClassLoader { + static { registerAsParallelCapable(); } + private static Path tempJarFile; + private static Lwjgl3Loader instance; + + private final Set implClasses = new CopyOnWriteArraySet<>(); + + private Lwjgl3Loader(Path jarFile) throws IOException, ReflectiveOperationException { + super(new URL[] { jarFile.toUri().toURL() }, Lwjgl3Loader.class.getClassLoader()); + + // Need to use a different directory for natives than MC because native files can only be loaded once + 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()); + } + + private boolean canBeSharedWithMc(String name) { + if (name.startsWith("org.lwjgl.")) { + return false; // MC may have a different version + } + for (String implClass : implClasses) { + if (name.startsWith(implClass)) { + return false; // depends on above lwjgl + } + } + return true; + } + + @Override + protected Class loadClass(String name, boolean resolve) throws ClassNotFoundException { + if (!canBeSharedWithMc(name)) { + synchronized (getClassLoadingLock(name)) { + Class cls = findLoadedClass(name); + if (cls == null) { + cls = findClass(name); + } + if (resolve) { + resolveClass(cls); + } + return cls; + } + } else { + return super.loadClass(name, resolve); + } + } + + @Override + protected Class findClass(String name) throws ClassNotFoundException { + try { + return super.findClass(name); + } catch (ClassNotFoundException e) { + String path = name.replace('.', '/').concat(".class"); + URL url = getParent().getResource(path); + if (url == null) { + throw e; + } + try { + byte[] bytes = IOUtils.toByteArray(url); + return defineClass(name, bytes, 0, bytes.length, (ProtectionDomain) null); + } catch (IOException e1) { + throw new ClassNotFoundException(name, e1); + } + } + } + + private static synchronized Path getJarFile() throws IOException { + if (tempJarFile == null) { + Path jarFile = Files.createTempFile("replaymod-lwjgl", ".jar"); + jarFile.toFile().deleteOnExit(); + try (InputStream in = Lwjgl3Loader.class.getResourceAsStream("lwjgl.jar")) { + if (in == null) { + throw new IOException("Failed to find embedded lwjgl.jar file."); + } + Files.copy(in, jarFile, REPLACE_EXISTING); + } + tempJarFile = jarFile; + } + return tempJarFile; + } + + public static synchronized Lwjgl3Loader instance() { + if (instance == null) { + try { + instance = new Lwjgl3Loader(getJarFile()); + } catch (IOException | ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } + return instance; + } + + @SuppressWarnings("unchecked") + public static

FrameConsumer

createFrameConsumer( + Class> implClass, + Class[] parameterTypes, + Object[] args + ) { + try { + Lwjgl3Loader loader = instance(); + loader.implClasses.add(implClass.getName()); + Class realClass = Class.forName(implClass.getName(), true, loader); + Constructor constructor = realClass.getConstructor(parameterTypes); + return (FrameConsumer

) constructor.newInstance(args); + } catch (ReflectiveOperationException e) { + throw new RuntimeException(e); + } + } +} diff --git a/src/main/java/com/replaymod/replay/FullReplaySender.java b/src/main/java/com/replaymod/replay/FullReplaySender.java index d03c7362..aee4dac1 100644 --- a/src/main/java/com/replaymod/replay/FullReplaySender.java +++ b/src/main/java/com/replaymod/replay/FullReplaySender.java @@ -44,8 +44,6 @@ import net.minecraft.network.packet.s2c.play.ScreenHandlerSlotUpdateS2CPacket; import net.minecraft.network.packet.s2c.play.ScreenHandlerPropertyUpdateS2CPacket; import net.minecraft.network.packet.s2c.play.HealthUpdateS2CPacket; import net.minecraft.network.packet.s2c.login.LoginSuccessS2CPacket; -import net.minecraft.network.packet.s2c.play.MobSpawnS2CPacket; -import net.minecraft.network.packet.s2c.play.PaintingSpawnS2CPacket; import net.minecraft.network.packet.s2c.play.ParticleS2CPacket; import net.minecraft.network.packet.s2c.play.PlayerAbilitiesS2CPacket; import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket; @@ -59,6 +57,12 @@ import net.minecraft.util.math.Vec3d; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; +//#if MC>=11900 +//#else +import net.minecraft.network.packet.s2c.play.MobSpawnS2CPacket; +import net.minecraft.network.packet.s2c.play.PaintingSpawnS2CPacket; +//#endif + //#if MC>=11600 //#else //$$ import net.minecraft.network.packet.s2c.play.EntitySpawnGlobalS2CPacket; @@ -472,11 +476,13 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend boolean relevantPacket = packet instanceof PlayerSpawnS2CPacket || packet instanceof EntitySpawnS2CPacket + //#if MC<11900 || packet instanceof MobSpawnS2CPacket + || packet instanceof PaintingSpawnS2CPacket + //#endif //#if MC<11600 //$$ || packet instanceof EntitySpawnGlobalS2CPacket //#endif - || packet instanceof PaintingSpawnS2CPacket || packet instanceof ExperienceOrbSpawnS2CPacket || packet instanceof EntitiesDestroyS2CPacket; if (!relevantPacket) { @@ -688,6 +694,9 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend , packet.isDebugWorld() , packet.isFlatWorld() //#endif + //#if MC>=11900 + //$$ , java.util.Optional.empty() + //#endif ); //#else //#if MC>=10800 @@ -744,6 +753,9 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend //$$ respawn.getGeneratorType(), //$$ GameMode.SPECTATOR //#endif + //#if MC>=11900 + //$$ , java.util.Optional.empty() + //#endif ); //#else //#if MC>=10809 diff --git a/src/main/java/com/replaymod/replay/camera/CameraEntity.java b/src/main/java/com/replaymod/replay/camera/CameraEntity.java index d990240e..797c0ed9 100644 --- a/src/main/java/com/replaymod/replay/camera/CameraEntity.java +++ b/src/main/java/com/replaymod/replay/camera/CameraEntity.java @@ -31,10 +31,6 @@ import net.minecraft.stat.StatHandler; import net.minecraft.util.Identifier; import net.minecraft.util.math.Box; -//#if MC>=11802 -//$$ import net.minecraft.tag.TagKey; -//#endif - //#if FABRIC>=1 //#else //$$ import net.minecraftforge.client.event.EntityViewRenderEvent; @@ -47,7 +43,11 @@ import net.minecraft.util.math.Box; //#if MC>=11400 import net.minecraft.client.world.ClientWorld; import net.minecraft.fluid.Fluid; +//#if MC>=11802 +//$$ import net.minecraft.tag.TagKey; +//#else import net.minecraft.tag.Tag; +//#endif import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.HitResult; //#else @@ -547,7 +547,11 @@ public class CameraEntity //#if MC>=11400 @Override + //#if MC>=11900 + //$$ public void onEquipStack(EquipmentSlot slot, ItemStack stack, ItemStack itemStack) { + //#else protected void onEquipStack(ItemStack itemStack_1) { + //#endif // Suppress equip sounds } //#endif diff --git a/src/main/java/com/replaymod/replay/camera/ClassicCameraController.java b/src/main/java/com/replaymod/replay/camera/ClassicCameraController.java index 316b4e25..26c5dfb0 100644 --- a/src/main/java/com/replaymod/replay/camera/ClassicCameraController.java +++ b/src/main/java/com/replaymod/replay/camera/ClassicCameraController.java @@ -9,9 +9,9 @@ import static net.minecraft.util.math.MathHelper.sin; // TODO: Marius is responsible for this. Please, someone clean it up. public class ClassicCameraController implements CameraController { - private static final double SPEED_CHANGE = 0.5; - private static final double LOWER_SPEED = 2; - private static final double UPPER_SPEED = 20; + private static final double LOWER_SPEED = 0.2; + private static final double UPPER_SPEED = 200; + private static final double SPEED_CHANGE = (UPPER_SPEED - LOWER_SPEED) / 20000; private final CameraEntity camera; diff --git a/src/main/java/com/replaymod/replay/camera/VanillaCameraController.java b/src/main/java/com/replaymod/replay/camera/VanillaCameraController.java index 10354be0..6561436b 100644 --- a/src/main/java/com/replaymod/replay/camera/VanillaCameraController.java +++ b/src/main/java/com/replaymod/replay/camera/VanillaCameraController.java @@ -9,7 +9,7 @@ import net.minecraft.client.options.KeyBinding; * Camera controller performing vanilla creative-like camera movements. */ public class VanillaCameraController implements CameraController { - private static final int MAX_SPEED = 1000; + private static final int MAX_SPEED = 2000; private static final int MIN_SPEED = -1000; private static final Vector3f[] DIRECTIONS = new Vector3f[]{ diff --git a/src/main/java/com/replaymod/replay/handler/GuiHandler.java b/src/main/java/com/replaymod/replay/handler/GuiHandler.java index 2bc2f79d..00bb3d9f 100644 --- a/src/main/java/com/replaymod/replay/handler/GuiHandler.java +++ b/src/main/java/com/replaymod/replay/handler/GuiHandler.java @@ -64,11 +64,11 @@ public class GuiHandler extends EventRegistrations { mod.getReplayHandler().getReplaySender().setReplaySpeed(0); //#if MC>=11600 - final TranslatableText BUTTON_OPTIONS = new TranslatableText("menu.options"); - final TranslatableText BUTTON_EXIT_SERVER = new TranslatableText("menu.disconnect"); - final TranslatableText BUTTON_ADVANCEMENTS = new TranslatableText("gui.advancements"); - final TranslatableText BUTTON_STATS = new TranslatableText("gui.stats"); - final TranslatableText BUTTON_OPEN_TO_LAN = new TranslatableText("menu.shareToLan"); + final Text BUTTON_OPTIONS = new TranslatableText("menu.options"); + final Text BUTTON_EXIT_SERVER = new TranslatableText("menu.disconnect"); + final Text BUTTON_ADVANCEMENTS = new TranslatableText("gui.advancements"); + final Text BUTTON_STATS = new TranslatableText("gui.stats"); + final Text BUTTON_OPEN_TO_LAN = new TranslatableText("menu.shareToLan"); //#else //#if MC>=11400 //$$ final String BUTTON_OPTIONS = I18n.translate("menu.options"); diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 73fe9d6e..ef5522ec 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -43,7 +43,7 @@ "depends": { "fabricloader": ">=0.7.0", "fabric-networking-v0": "*", - "fabric-keybindings-v0": "*", + "fabric-key-binding-api-v1": "*", "fabric-resource-loader-v0": "*" }, diff --git a/src/main/resources/mixins.core.replaymod.json b/src/main/resources/mixins.core.replaymod.json index d1232a20..a326dc8d 100644 --- a/src/main/resources/mixins.core.replaymod.json +++ b/src/main/resources/mixins.core.replaymod.json @@ -22,6 +22,9 @@ "GuiScreenAccessor", "KeyBindingAccessor", "MinecraftAccessor", + //#if MC>=11900 + //$$ "SimpleOptionAccessor", + //#endif "TimerAccessor" ], "compatibilityLevel": "JAVA_8", diff --git a/src/main/resources/mixins.recording.replaymod.json b/src/main/resources/mixins.recording.replaymod.json index 09545afc..5dec2e07 100644 --- a/src/main/resources/mixins.recording.replaymod.json +++ b/src/main/resources/mixins.recording.replaymod.json @@ -8,8 +8,11 @@ "EntityLivingBaseAccessor", "IntegratedServerAccessor", "NetworkManagerAccessor", - "SPacketSpawnMobAccessor", - "SPacketSpawnPlayerAccessor", + "MixinClientConnection", + //#if MC<11500 + //$$ "SPacketSpawnMobAccessor", + //$$ "SPacketSpawnPlayerAccessor", + //#endif "MixinServerInfo", //#if MC>=10800 "MixinDownloadingPackFinder", diff --git a/src/main/resources/mixins.render.blend.replaymod.json b/src/main/resources/mixins.render.blend.replaymod.json index abf980ae..156da80c 100644 --- a/src/main/resources/mixins.render.blend.replaymod.json +++ b/src/main/resources/mixins.render.blend.replaymod.json @@ -20,11 +20,13 @@ "ItemRendererAccessor", //#endif "ParticleAccessor", + //#if MC<11900 "MixinRenderGlobal", "MixinRenderItem", "MixinRenderLivingBase", "MixinRenderManager" //#endif + //#endif ], "compatibilityLevel": "JAVA_8", "minVersion": "0.6.11", diff --git a/src/main/resources/mixins.render.replaymod.json b/src/main/resources/mixins.render.replaymod.json index ff833a38..8bd2c8b6 100644 --- a/src/main/resources/mixins.render.replaymod.json +++ b/src/main/resources/mixins.render.replaymod.json @@ -36,7 +36,6 @@ //#endif //#if MC>=11400 "Mixin_PreserveDepthDuringHandRendering", - "Mixin_WindowsWorkaroundForTinyEXRNatives", //#endif "GameRendererAccessor", "MainWindowAccessor", diff --git a/version.txt b/version.txt index 57cf282e..338a5b5d 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.6.5 +2.6.6 diff --git a/versions/1.14.4-forge/mapping.txt b/versions/1.14.4-forge/mapping.txt index 52a22ceb..3bd9a84e 100644 --- a/versions/1.14.4-forge/mapping.txt +++ b/versions/1.14.4-forge/mapping.txt @@ -3,6 +3,8 @@ net.minecraft.potion.EffectInstance net.minecraft.potion.PotionEffect net.minecraft.client.gui.screen.AddServerScreen net.minecraft.client.gui.GuiScreenAddServer net.minecraft.resources.AbstractResourcePack net.minecraft.client.resources.AbstractResourcePack net.minecraft.resources.FolderPack net.minecraft.client.resources.FolderResourcePack +net.minecraft.resources.IResource net.minecraft.client.resources.IResource +net.minecraft.resources.IResourceManager net.minecraft.client.resources.IResourceManager net.minecraft.client.resources.DownloadingPackFinder net.minecraft.client.resources.ResourcePackRepository net.minecraft.client.renderer.chunk.ChunkRenderTask net.minecraft.client.renderer.chunk.ChunkCompileTaskGenerator net.minecraft.client.settings.AbstractOption net.minecraft.client.settings.GameSettings.Options diff --git a/versions/1.15.2/src/main/resources/fabric.mod.json b/versions/1.15.2/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..73fe9d6e --- /dev/null +++ b/versions/1.15.2/src/main/resources/fabric.mod.json @@ -0,0 +1,60 @@ +{ + "schemaVersion": 1, + "id": "replaymod", + "version": "${version}", + + "name": "Replay Mod", + "description": "A Mod which allows you to record, replay and share your Minecraft experience.", + "authors": [ + "CrushedPixel", + "johni0702" + ], + "contact": { + "homepage": "https://replaymod.com/", + "sources": "https://github.com/ReplayMod/ReplayMod" + }, + + "license": "GPL-3.0-or-later", + "icon": "assets/replaymod/favicon_logo.png", + + "environment": "client", + "entrypoints": { + "client": [ + "com.replaymod.core.ReplayModBackend" + ], + "modmenu": [ + "com.replaymod.core.gui.ModMenuApiImpl" + ], + "frex_flawless_frames": [ + "com.replaymod.render.utils.FlawlessFrames::registerConsumer" + ], + "preLaunch": [ + "com.replaymod.core.DummyChainLoadEntryPoint" + ], + "mm:early_risers": [ + "com.replaymod.core.ReplayModMMLauncher" + ] + }, + "mixins": [ + "mixins.jgui.json", + "mixins.nonmmlauncher.replaymod.json" + ], + + "depends": { + "fabricloader": ">=0.7.0", + "fabric-networking-v0": "*", + "fabric-keybindings-v0": "*", + "fabric-resource-loader-v0": "*" + }, + + "conflicts": { + "iris": "<1.1.3" + }, + + "custom": { + "mm:early_risers": [ + "com.replaymod.core.ReplayModMMLauncher" + ], + "modmenu:clientsideOnly": true + } +} diff --git a/versions/1.17/src/main/resources/fabric.mod.json b/versions/1.17/src/main/resources/fabric.mod.json new file mode 100644 index 00000000..dbb99550 --- /dev/null +++ b/versions/1.17/src/main/resources/fabric.mod.json @@ -0,0 +1,60 @@ +{ + "schemaVersion": 1, + "id": "replaymod", + "version": "${version}", + + "name": "Replay Mod", + "description": "A Mod which allows you to record, replay and share your Minecraft experience.", + "authors": [ + "CrushedPixel", + "johni0702" + ], + "contact": { + "homepage": "https://replaymod.com/", + "sources": "https://github.com/ReplayMod/ReplayMod" + }, + + "license": "GPL-3.0-or-later", + "icon": "assets/replaymod/favicon_logo.png", + + "environment": "client", + "entrypoints": { + "client": [ + "com.replaymod.core.ReplayModBackend" + ], + "modmenu": [ + "com.replaymod.core.gui.ModMenuApiImpl" + ], + "frex_flawless_frames": [ + "com.replaymod.render.utils.FlawlessFrames::registerConsumer" + ], + "preLaunch": [ + "com.replaymod.core.DummyChainLoadEntryPoint" + ], + "mm:early_risers": [ + "com.replaymod.core.ReplayModMMLauncher" + ] + }, + "mixins": [ + "mixins.jgui.json", + "mixins.nonmmlauncher.replaymod.json" + ], + + "depends": { + "fabricloader": ">=0.7.0", + "fabric-networking-api-v1": "*", + "fabric-key-binding-api-v1": "*", + "fabric-resource-loader-v0": "*" + }, + + "conflicts": { + "iris": "<1.1.3" + }, + + "custom": { + "mm:early_risers": [ + "com.replaymod.core.ReplayModMMLauncher" + ], + "modmenu:clientsideOnly": true + } +} diff --git a/versions/1.19/.gitkeep b/versions/1.19/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/versions/1.19/src/main/java/com/replaymod/core/mixin/SimpleOptionAccessor.java b/versions/1.19/src/main/java/com/replaymod/core/mixin/SimpleOptionAccessor.java new file mode 100644 index 00000000..e31c2de9 --- /dev/null +++ b/versions/1.19/src/main/java/com/replaymod/core/mixin/SimpleOptionAccessor.java @@ -0,0 +1,11 @@ +package com.replaymod.core.mixin; + +import net.minecraft.client.option.SimpleOption; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(SimpleOption.class) +public interface SimpleOptionAccessor { + @Accessor("value") + void setRawValue(T value); +} diff --git a/versions/mapping-fabric-1.19-1.18.2.txt b/versions/mapping-fabric-1.19-1.18.2.txt new file mode 100644 index 00000000..7ac37640 --- /dev/null +++ b/versions/mapping-fabric-1.19-1.18.2.txt @@ -0,0 +1,4 @@ +# FIXME remap should be able to map these without us explicitly declaring them +net.minecraft.client.render.WorldRenderer fullUpdateFuture field_34808 +net.minecraft.client.render.WorldRenderer updateFinished field_34809 +net.minecraft.client.render.WorldRenderer shouldUpdate field_34810