diff --git a/Jenkinsfile b/Jenkinsfile index a92bad02..1e1686cb 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -13,7 +13,7 @@ pipeline { [$class: 'ArbitraryFileCache', excludes: '', includes: '**/*', path: '.gradle/user_home/wrapper'], [$class: 'ArbitraryFileCache', excludes: '', includes: '**/*', path: '.gradle/loom-cache'], ]) { - sh './gradlew :jGui:1.7.10:setupCIWorkspace :1.7.10:setupCIWorkspace' + // sh './gradlew :jGui:1.7.10:setupCIWorkspace :1.7.10:setupCIWorkspace' sh './gradlew --parallel' } archiveArtifacts 'versions/*/build/libs/*.jar' diff --git a/README.md b/README.md index a05f2b8e..b8d5f000 100644 --- a/README.md +++ b/README.md @@ -7,8 +7,8 @@ Make sure your sub-projects are up-to-date: `git submodule update --init --recur For compiling 1.7.10, you must run `./gradlew :jGui:1.7.10:setupDecompWorkspace :1.7.10:setupDecompWorkspace` once after the initial clone. This may take quite some time. ### No IDE -You can build the mod by running `./gradlew build` (or just `./gradlew shadowJar`). You can then find the final jar files in `versions/$MCVERSION/build/libs/`. -You can also build single versions by running `./gradlew :1.8:build` (or just `./gradlew :1.8:shadowJar`) (builds the MC 1.8 version). +You can build the mod by running `./gradlew build` (or just `./gradlew bundleJar`). You can then find the final jar files in `versions/$MCVERSION/build/libs/`. +You can also build single versions by running `./gradlew :1.8:build` (or just `./gradlew :1.8:bundleJar`) (builds the MC 1.8 version). ### IntelliJ Ensure you have at least IDEA 2020.1. @@ -30,55 +30,9 @@ The `master` branch is solely to be used for the `version.json` file that contai used by the clients to check for updates of this mod. ### The Preprocessor -To support multiple Minecraft versions with the ReplayMod, a [JCP](https://github.com/raydac/java-comment-preprocessor)-inspired preprocessor is used: -```java - //#if MC>=11200 - // This is the block for MC >= 1.12.0 - category.addDetail(name, callable::call); - //#else - //$$ // This is the block for MC < 1.12.0 - //$$ category.setDetail(name, callable::call); - //#endif -``` -Any comments starting with `//$$` will automatically be introduced / removed based on the surrounding condition(s). -Normal comments are left untouched. The `//#else` branch is optional. - -Conditions can be nested arbitrarily but their indention shall always be equal to the indention of the code at the `//#if` line. -The `//$$` shall be aligned with the inner-most `//#if`. -```java - //#if MC>=10904 - public CPacketResourcePackStatus makeStatusPacket(String hash, Action action) { - //#if MC>=11002 - return new CPacketResourcePackStatus(action); - //#else - //$$ return new CPacketResourcePackStatus(hash, action); - //#endif - } - //#else - //$$ public C19PacketResourcePackStatus makeStatusPacket(String hash, Action action) { - //$$ return new C19PacketResourcePackStatus(hash, action); - //$$ } - //#endif -``` -Code for the more recent MC version shall be placed in the first branch of the if-else-construct. -Version-dependent import statements shall be placed separately from and after all other imports. -Common version dependent code (including the fml and forge event bus) are available as static methods/fields in the `MCVer` class. - -The source code in `src/main` is generally for the most recent Minecraft version and is automatically passed through the -preprocessor when any of the other versions are built (gradle projects `:1.8`, `:1.8.9`, etc.). -Do **NOT** edit any of the code in `versions/$MCVERSION/build/` as it is automatically generated and will be overwritten without warning. - -You can change the version of the code in `src/main` if you wish to develop/debug with another version of Minecraft: -```bash -./gradle :1.9.4:setCoreVersion # switches all sources in src/main to 1.9.4 -``` -If you do so, you'll also have to refresh the project in your IDE. - -Make sure to switch back to the most recent branch before committing! -Care should also be taken that switching to a different branch and back doesn't introduce any uncommitted changes (e.g. due to different indention, especially in case of nested conditions). - -Some files may use the same preprocessor with different keywords. -If required, more file extensions and keywords can be added in the `preprocess` block of the `versions/common.gradle` script. +To support multiple Minecraft versions with the ReplayMod, a [JCP](https://github.com/raydac/java-comment-preprocessor)-inspired preprocessor is used. +It has by now acquired a lot more sophisticated features to make it as noninvasive as possible. +Please read the [preprocessor's README](https://github.com/ReplayMod/preprocessor/blob/master/README.md) to understand how it works. ### Versioning The ReplayMod uses the versioning scheme outlined [here](http://mcforge.readthedocs.io/en/latest/conventions/versioning/) diff --git a/build.gradle b/build.gradle index e103e23f..c7085f95 100644 --- a/build.gradle +++ b/build.gradle @@ -21,7 +21,7 @@ buildscript { if (!fabric) { maven { name = "forge" - url = "https://files.minecraftforge.net/maven" + url = "https://maven.minecraftforge.net" } } maven { @@ -32,21 +32,21 @@ buildscript { } dependencies { - classpath 'com.github.jengelman.gradle.plugins:shadow:4.0.2' + classpath 'gradle.plugin.com.github.jengelman.gradle.plugins:shadow:7.0.0' if (fabric) { - classpath 'fabric-loom:fabric-loom.gradle.plugin:0.5-SNAPSHOT' + classpath 'fabric-loom:fabric-loom.gradle.plugin:0.8-SNAPSHOT' } else if (mcVersion >= 11400) { - classpath('net.minecraftforge.gradle:ForgeGradle:3.+'){ + 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 } } else if (mcVersion >= 10800) { classpath('com.github.ReplayMod:ForgeGradle:' + ( - mcVersion >= 11200 ? 'b64de9c' : // FG 2.3 - mcVersion >= 10904 ? '5bb7a53' : // FG 2.2 - 'fc1eabc' // FG 2.1 + mcVersion >= 11200 ? '34ab703' : // FG 2.3 + mcVersion >= 10904 ? '5d1e8d8' : // FG 2.2 + 'd1a7165' // FG 2.1 ) + ':all') } else { - classpath 'com.github.ReplayMod:ForgeGradle:5fca6853:all' // FG 1.2 + classpath 'com.github.ReplayMod:ForgeGradle:a8a9e0ca:all' // FG 1.2 } } } @@ -102,12 +102,16 @@ preprocess { ".vert": PreprocessTask.DEFAULT_KEYWORDS, ".frag": PreprocessTask.DEFAULT_KEYWORDS, ]) + + patternAnnotation.set("com.replaymod.gradle.remap.Pattern") } def mcVersionStr = "${(int)(mcVersion/10000)}.${(int)(mcVersion/100)%100}" + (mcVersion%100==0 ? '' : ".${mcVersion%100}") -sourceCompatibility = 1.8 -targetCompatibility = 1.8 +sourceCompatibility = targetCompatibility = mcVersion >= 11700 ? 16 : 1.8 +tasks.withType(JavaCompile).configureEach { + options.release = mcVersion >= 11700 ? 16 : 8 +} if (mcVersion >= 11400) { sourceSets { @@ -122,7 +126,9 @@ archivesBaseName = "replaymod" if (FABRIC) { minecraft { refmapName = 'mixins.replaymod.refmap.json' - autoGenIDERuns = true + runConfigs.all { + ideConfigGenerated = true + } } } else { minecraft { @@ -197,6 +203,12 @@ repositories { name = "fabric" url = "https://maven.fabricmc.net/" } + maven { + url 'https://maven.terraformersmc.com/releases/' + content { + includeGroup 'com.terraformersmc' + } + } maven { url 'https://jitpack.io' content { @@ -208,17 +220,17 @@ repositories { configurations { // Include dep in fat jar without relocation and, when forge supports it, without exploding (TODO) shade - compile.extendsFrom shade + implementation.extendsFrom shade // Include dep in fat jar with relocation and minimization shadow - compile.extendsFrom shadow + implementation.extendsFrom shadow } def shadeExclusions = { // Cannot just add these to the shade configuration because they'd be inherited by the compile configuration then exclude group: 'com.google.guava', module: 'guava-jdk5' exclude group: 'com.google.guava', module: 'guava' // provided by MC - exclude group: 'com.google.code.gson', module: 'gson' // provided by MC + exclude group: 'com.google.code.gson', module: 'gson' // provided by MC (or manually bundled for 1.11.2 and below) } dependencies { @@ -229,6 +241,8 @@ dependencies { 11601: '1.16.1', 11603: '1.16.3', 11604: '1.16.4', + 11700: '1.17', + 11701: '1.17.1', ][mcVersion] mappings 'net.fabricmc:yarn:' + [ 11404: '1.14.4+build.16', @@ -236,20 +250,18 @@ dependencies { 11601: '1.16.1+build.17:v2', 11603: '1.16.3+build.1:v2', 11604: '1.16.4+build.6:v2', + 11700: '1.17+build.13:v2', + 11701: '1.17.1+build.29:v2', ][mcVersion] - modCompile 'net.fabricmc:fabric-loader:' + [ - 11404: '0.7.8+build.189', - 11502: '0.7.8+build.189', - 11601: '0.8.8+build.202', - 11603: '0.9.1+build.205', - 11604: '0.10.6+build.214', - ][mcVersion] + modImplementation 'net.fabricmc:fabric-loader:0.11.6' def fabricApiVersion = [ 11404: '0.4.3+build.247-1.14', 11502: '0.5.1+build.294-1.15', 11601: '0.14.0+build.371-1.16', 11603: '0.17.1+build.394-1.16', 11604: '0.25.1+build.416-1.16', + 11700: '0.36.0+1.17', + 11701: '0.37.1+1.17', ][mcVersion] def fabricApiModules = [ "api-base", @@ -260,8 +272,11 @@ dependencies { if (mcVersion >= 11600) { fabricApiModules.add("key-binding-api-v1") } + if (mcVersion >= 11700) { + fabricApiModules.add("networking-api-v1") + } fabricApiModules.each { module -> - modCompile fabricApi.module("fabric-$module", fabricApiVersion) + modImplementation fabricApi.module("fabric-$module", fabricApiVersion) include fabricApi.module("fabric-$module", fabricApiVersion) } } @@ -302,6 +317,11 @@ dependencies { } } + if (mcVersion < 11200) { + // The version which MC ships is too old, we'll need to ship our own + shadow 'com.google.code.gson:gson:2.8.7' + } + shadow 'com.github.javagl.JglTF:jgltf-model:3af6de4' if (FABRIC) { @@ -312,7 +332,7 @@ dependencies { shadow 'com.github.ReplayMod.JavaBlend:2.79.0:a0696f8' - shadow "com.github.ReplayMod:ReplayStudio:a1f82a7", shadeExclusions + shadow "com.github.ReplayMod:ReplayStudio:a67fb11", shadeExclusions implementation(jGui){ transitive = false // FG 1.2 puts all MC deps into the compile configuration and we don't want to shade those @@ -320,17 +340,22 @@ dependencies { shadow 'com.github.ReplayMod:lwjgl-utils:27dcd66' if (FABRIC) { - if (mcVersion >= 11602) { - // TODO re-add to runtime once available - modCompileOnly 'io.github.prospector:modmenu:1.14.0+build.24' + if (mcVersion >= 11700) { + modImplementation 'com.terraformersmc:modmenu:2.0.0-beta.7' + } else if (mcVersion >= 11602) { + modImplementation 'com.terraformersmc:modmenu:1.16.8' } else if (mcVersion >= 11600) { - modCompile 'io.github.prospector:modmenu:1.14.0+build.24' + modImplementation 'io.github.prospector:modmenu:1.14.0+build.24' } else { - modCompile 'io.github.prospector.modmenu:ModMenu:1.6.2-92' + modImplementation 'io.github.prospector.modmenu:ModMenu:1.6.2-92' } } - testCompile 'junit:junit:4.11' + if (mcVersion >= 11600) { + modCompileOnly 'com.github.IrisShaders:Iris:1.0.0' + } + + testImplementation 'junit:junit:4.11' } if (mcVersion <= 10710) { @@ -422,8 +447,12 @@ task configureRelocation() { } } -tasks.removeByName('shadowJar') // we want to base our shadowed jar on the reobfJar output, not the sourceSet output -task shadowJar(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) { +// we want to base our shadowed jar on the reobfJar output, not the sourceSet output +// Tried tasks.replace but that does not actually seem to replace everything. +tasks.shadowJar.doFirst { + throw new GradleException("Wrong task! You want to run 'bundleJar' instead.") +} +tasks.register('bundleJar', com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar).configure { from { (FABRIC ? tasks.remapJar : tasks.jar).archiveFile.get() } dependsOn { FABRIC ? tasks.remapJar : (mcVersion >= 10800 ? tasks.reobfJar : tasks.reobf) } @@ -469,7 +498,7 @@ task shadowJar(type: com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar) exclude(dependency('.*spongepowered:mixin:.*')) } } -tasks.assemble.dependsOn tasks.shadowJar +tasks.assemble.dependsOn tasks.bundleJar jar { classifier = 'raw' @@ -484,6 +513,15 @@ jar { 'FMLAT': 'replaymod_at.cfg' } } + + if (mcVersion >= 11700) { + // Workaround a mixin bug which generates invalid refmaps for the `addDrawableChild` invoker + filesMatching("mixins.replaymod.refmap.json") { + it.filter { + it.replace("addDrawableChild(L/;)L/;", "method_37063(Lnet/minecraft/class_364;)Lnet/minecraft/class_364;") + } + } + } } processResources { diff --git a/docs/content.md b/docs/content.md index 139c7f5b..faea3ff2 100755 --- a/docs/content.md +++ b/docs/content.md @@ -358,7 +358,8 @@ There are **7 Encoding Presets** you can choose from: While these video files are of perfect quality, most **non-FFmpeg-based video players and video editing software** (e.g. QuickTime Player, Sony Vegas and Adobe Premiere) can't play these videos. Therefore, you should instead use the **MP4 - High Quality** preset in most cases. - **PNG Sequence** Exports the sequence as individual frames in the **PNG Format**. - **Warning:** This can create a huge amount of files, so make sure to save them in a separate folder. + The images are of perfect quality. + Depth map images are 32-bit float encoded in the 8-bit components in BGRA order and will need some kind of post-processing to be useful in most editors. - **OpenEXR Sequence** (Minecraft 1.14 and above) Exports the sequence as individual frames in the **OpenEXR Format**. The images are of perfect quality and may contain diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar index 0d4a9516..5c2d1cf0 100755 Binary files a/gradle/wrapper/gradle-wrapper.jar and b/gradle/wrapper/gradle-wrapper.jar differ diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 722328d2..0f80bbf5 100755 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionSha256Sum=7bdbad1e4f54f13c8a78abc00c26d44dd8709d4aedb704d913fb1bb78ac025dc -distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.0.2-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew index cccdd3d5..b0d6d0ab 100755 --- a/gradlew +++ b/gradlew @@ -1,5 +1,21 @@ #!/usr/bin/env sh +# +# Copyright 2015 the original author or authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + ############################################################################## ## ## Gradle start up script for UN*X @@ -28,7 +44,7 @@ APP_NAME="Gradle" APP_BASE_NAME=`basename "$0"` # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -DEFAULT_JVM_OPTS="" +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' # Use the maximum available, or set MAX_FD != -1 to use that value. MAX_FD="maximum" diff --git a/gradlew.bat b/gradlew.bat index e95643d6..15e1ee37 100755 --- a/gradlew.bat +++ b/gradlew.bat @@ -1,3 +1,19 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem http://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + @if "%DEBUG%" == "" @echo off @rem ########################################################################## @rem @@ -14,7 +30,7 @@ set APP_BASE_NAME=%~n0 set APP_HOME=%DIRNAME% @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. -set DEFAULT_JVM_OPTS= +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" @rem Find java.exe if defined JAVA_HOME goto findJavaFromJavaHome diff --git a/jGui b/jGui index 66418363..71d36174 160000 --- a/jGui +++ b/jGui @@ -1 +1 @@ -Subproject commit 6641836321f4583e6f56d8ceb59da452213396e7 +Subproject commit 71d361746ef7d3b9b2bd9a8029296a895498ad39 diff --git a/root.gradle.kts b/root.gradle.kts index 61825fc4..de90b92f 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.5-SNAPSHOT" apply false - id("com.replaymod.preprocess") version "24ac087" + id("fabric-loom") version "0.8-SNAPSHOT" apply false + id("com.replaymod.preprocess") version "123fb7a" id("com.github.hierynomus.license") version "0.15.0" } @@ -25,12 +25,7 @@ if (gitDescribe().endsWith("*")) { group = "com.replaymod" -// Loom tries to find the active mixin version by recursing up to the root project and checking each project's -// compileClasspath and build script classpath (in that order). Since we've loom in our root project's classpath, -// loom will only find it after checking the root project's compileClasspath (which doesn't exist by default). -configurations.register("compileClasspath") - -val shadowJar by tasks.creating(Copy::class) { +val bundleJar by tasks.creating(Copy::class) { into("$buildDir/libs") } @@ -42,10 +37,10 @@ subprojects { } afterEvaluate { - val projectShadowJar = project.tasks.findByName("shadowJar") - if (projectShadowJar != null && projectShadowJar.hasProperty("archivePath") && project.name != "core") { - shadowJar.dependsOn(projectShadowJar) - shadowJar.from(projectShadowJar.withGroovyBuilder { getProperty("archivePath") }) + val projectBundleJar = project.tasks.findByName("bundleJar") + if (projectBundleJar != null && projectBundleJar.hasProperty("archivePath") && project.name != "core") { + bundleJar.dependsOn(projectBundleJar) + bundleJar.from(projectBundleJar.withGroovyBuilder { getProperty("archivePath") }) } } } @@ -178,36 +173,41 @@ val doRelease by tasks.registering { } } -defaultTasks("shadowJar") +defaultTasks("bundleJar") preprocess { - "1.16.4"(11604, "yarn") { - "1.16.1"(11601, "yarn") { - "1.15.2"(11502, "yarn") { - "1.14.4"(11404, "yarn", file("versions/mapping-fabric-1.15.2-1.14.4.txt")) { - "1.14.4-forge"(11404, "srg", file("versions/mapping-1.14.4-fabric-forge.txt")) { - "1.12.2"(11202, "srg", file("versions/1.14.4-forge/mapping.txt")) { - "1.12.1"(11201, "srg") { - "1.12"(11200, "srg") { - "1.11.2"(11102, "srg", file("versions/1.12/mapping.txt")) { - "1.11"(11100, "srg", file("versions/1.11.2/mapping.txt")) { - "1.10.2"(11002, "srg", file("versions/1.11/mapping.txt")) { - "1.9.4"(10904, "srg") { - "1.8.9"(10809, "srg", file("versions/1.9.4/mapping.txt")) { - "1.8"(10800, "srg", file("versions/1.8.9/mapping.txt")) { - "1.7.10"(10710, "srg", file("versions/1.8/mapping.txt")) - } - } - } - } - } - } - } - } - } - } - } - } - } - } + val mc11701 = createNode("1.17.1", 11701, "yarn") + val mc11700 = createNode("1.17", 11700, "yarn") + val mc11604 = createNode("1.16.4", 11604, "yarn") + val mc11601 = createNode("1.16.1", 11601, "yarn") + val mc11502 = createNode("1.15.2", 11502, "yarn") + val mc11404 = createNode("1.14.4", 11404, "yarn") + val mc11404Forge = createNode("1.14.4-forge", 11404, "srg") + val mc11202 = createNode("1.12.2", 11202, "srg") + val mc11201 = createNode("1.12.1", 11201, "srg") + val mc11200 = createNode("1.12", 11200, "srg") + val mc11102 = createNode("1.11.2", 11102, "srg") + val mc11100 = createNode("1.11", 11100, "srg") + val mc11002 = createNode("1.10.2", 11002, "srg") + val mc10904 = createNode("1.9.4", 10904, "srg") + val mc10809 = createNode("1.8.9", 10809, "srg") + val mc10800 = createNode("1.8", 10800, "srg") + val mc10710 = createNode("1.7.10", 10710, "srg") + + mc11701.link(mc11700) + mc11700.link(mc11604, file("versions/mapping-fabric-1.17-1.16.4.txt")) + mc11604.link(mc11601) + mc11601.link(mc11502) + mc11502.link(mc11404, file("versions/mapping-fabric-1.15.2-1.14.4.txt")) + mc11404.link(mc11404Forge, file("versions/mapping-1.14.4-fabric-forge.txt")) + mc11404Forge.link(mc11202, file("versions/1.14.4-forge/mapping.txt")) + mc11202.link(mc11201) + mc11201.link(mc11200) + mc11200.link(mc11102, file("versions/1.12/mapping.txt")) + mc11102.link(mc11100, file("versions/1.11.2/mapping.txt")) + mc11100.link(mc11002, file("versions/1.11/mapping.txt")) + mc11002.link(mc10904) + mc10904.link(mc10809, file("versions/1.9.4/mapping.txt")) + mc10809.link(mc10800, file("versions/1.8.9/mapping.txt")) + mc10800.link(mc10710, file("versions/1.8/mapping.txt")) } diff --git a/settings.gradle.kts b/settings.gradle.kts index b99f2a4a..02c25403 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,7 +19,7 @@ pluginManagement { } val jGuiVersions = listOf( - "1.7.10", + // "1.7.10", "1.8", "1.8.9", "1.9.4", @@ -28,10 +28,12 @@ val jGuiVersions = listOf( "1.14.4", "1.15.2", "1.16.1", - "1.16.4" + "1.16.4", + "1.17", + "1.17.1", ) val replayModVersions = listOf( - "1.7.10", + // "1.7.10", "1.8", "1.8.9", "1.9.4", @@ -45,7 +47,9 @@ val replayModVersions = listOf( "1.14.4", "1.15.2", "1.16.1", - "1.16.4" + "1.16.4", + "1.17", + "1.17.1", ) rootProject.buildFileName = "root.gradle.kts" diff --git a/src/main/java/com/replaymod/compat/bettersprinting/DisableBetterSprinting.java b/src/main/java/com/replaymod/compat/bettersprinting/DisableBetterSprinting.java index 6d0ec973..fca4cd6c 100644 --- a/src/main/java/com/replaymod/compat/bettersprinting/DisableBetterSprinting.java +++ b/src/main/java/com/replaymod/compat/bettersprinting/DisableBetterSprinting.java @@ -1,185 +1 @@ -//#if MC<11400 -//$$ package com.replaymod.compat.bettersprinting; -//$$ -//$$ import com.replaymod.replay.ReplayModReplay; -//$$ import com.replaymod.replay.events.ReplayChatMessageEvent; -//$$ import net.minecraft.client.Minecraft; -//$$ import net.minecraft.client.multiplayer.PlayerControllerMP; -//$$ import net.minecraft.entity.Entity; -//$$ import net.minecraft.entity.player.EntityPlayer; -//$$ import net.minecraftforge.client.event.GuiOpenEvent; -//$$ import net.minecraftforge.common.MinecraftForge; -//$$ -//#if MC>=10904 -//$$ import net.minecraft.block.state.IBlockState; -//$$ import net.minecraft.util.SoundCategory; -//$$ import net.minecraft.util.SoundEvent; -//$$ import net.minecraft.util.math.BlockPos; -//$$ import net.minecraft.world.IWorldEventListener; -//$$ import net.minecraft.world.World; -//$$ -//$$ import javax.annotation.Nullable; -//#else -//#if MC>= 10800 -//$$ import net.minecraft.util.BlockPos; -//#endif -//$$ import net.minecraft.world.IWorldAccess; -//#endif -//$$ -//#if MC>=10800 -//$$ import net.minecraftforge.fml.common.Loader; -//$$ import net.minecraftforge.fml.common.ModContainer; -//$$ import net.minecraftforge.fml.common.eventhandler.EventPriority; -//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -//$$ import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion; -//$$ import net.minecraftforge.fml.common.versioning.Restriction; -//$$ import net.minecraftforge.fml.common.versioning.VersionRange; -//#else -//$$ import cpw.mods.fml.common.Loader; -//$$ import cpw.mods.fml.common.ModContainer; -//$$ import cpw.mods.fml.common.eventhandler.EventPriority; -//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; -//$$ import cpw.mods.fml.common.versioning.DefaultArtifactVersion; -//$$ import cpw.mods.fml.common.versioning.Restriction; -//$$ import cpw.mods.fml.common.versioning.VersionRange; -//#endif -//$$ -//$$ import java.util.Collections; -//$$ -//$$ import static com.replaymod.compat.ReplayModCompat.LOGGER; -//$$ import static com.replaymod.core.versions.MCVer.*; -//$$ -//$$ /** -//$$ * Old Better Sprinting versions replace the vanilla player with their own, overridden instance (replacing the camera entity). -//$$ * -//$$ * See: https://github.com/chylex/Better-Sprinting/blob/1.8/src/main/java/chylex/bettersprinting/client/player/impl/LogicImplOverride.java -//$$ */ -//$$ public class DisableBetterSprinting { -//$$ private static final VersionRange OLD_VERSION = VersionRange.newRange(null, -//$$ Collections.singletonList(new Restriction(null, false, new DefaultArtifactVersion("2.0.0"), false))); -//$$ private static final String LOGIC_CLASS_NAME = "chylex.bettersprinting.client.player.impl.LogicImplOverride"; -//$$ private static final String CONTROLLER_OVERRIDE_CLASS_NAME = LOGIC_CLASS_NAME + "$PlayerControllerMPOverride"; -//$$ -//$$ public static void register() { -//$$ Loader.instance().getModList().stream() -//$$ .filter(mod -> mod.getModId().equalsIgnoreCase("bettersprinting")) -//$$ .findFirst() -//$$ .map(ModContainer::getProcessedVersion).filter(OLD_VERSION::containsVersion) -//$$ .ifPresent($_ -> MinecraftForge.EVENT_BUS.register(new DisableBetterSprinting())); -//$$ } -//$$ -//$$ private DisableBetterSprinting() { -//$$ LOGGER.info("BetterSprinting workaround enabled"); -//$$ } -//$$ -//$$ private final Minecraft mc = Minecraft.getMinecraft(); -//$$ private PlayerControllerMP originalController; -//$$ private BetterSprintingWorldAccess worldAccessHook = new BetterSprintingWorldAccess(); -//$$ -//$$ @SubscribeEvent(priority = EventPriority.HIGH) -//$$ public void beforeGuiOpenEvent(GuiOpenEvent event) { -//$$ if (ReplayModReplay.instance.getReplayHandler() != null && mc.world != null) { -//$$ // During replay, get ready to revert BetterSprinting's overwritten playerController -//$$ originalController = mc.playerController; - //#if MC>=10904 - //$$ mc.world.addEventListener(worldAccessHook); - //#else - //$$ mc.theWorld.addWorldAccess(worldAccessHook); - //#endif -//$$ } -//$$ } -//$$ -//$$ @SubscribeEvent(priority = EventPriority.LOW) -//$$ public void afterGuiOpenEvent(GuiOpenEvent event) { -//$$ if (ReplayModReplay.instance.getReplayHandler() != null && mc.world != null) { - //#if MC>=10904 - //$$ mc.world.addEventListener(worldAccessHook); - //#else - //$$ mc.theWorld.addWorldAccess(worldAccessHook); - //#endif -//$$ } -//$$ } -//$$ -//$$ @SubscribeEvent -//$$ public void onReplayChatMessage(ReplayChatMessageEvent event) { -//$$ // Suppress this message if it's the Better Sprinting warning message -//$$ for (StackTraceElement elem : Thread.currentThread().getStackTrace()) { -//$$ if (LOGIC_CLASS_NAME.equals(elem.getClassName())) { -//$$ LOGGER.info("BetterSprinting warning message suppressed."); -//$$ event.setCanceled(true); -//$$ return; -//$$ } -//$$ } -//$$ } -//$$ -//$$ private class BetterSprintingWorldAccess - //#if MC>=10904 - //$$ implements IWorldEventListener - //#else - //$$ implements IWorldAccess - //#endif -//$$ { -//$$ @Override - //#if MC>=10800 - //$$ public void onEntityRemoved(Entity entityIn) { - //#else - //$$ public void onEntityDestroy(Entity entityIn) { - //#endif -//$$ if (mc.playerController != null && mc.playerController.getClass().getName().equals(CONTROLLER_OVERRIDE_CLASS_NAME)) { -//$$ // Someone has secretly swapped out the player controller and is about to substitute their own player entity. -//$$ // This is the right time to destroy their plan. -//$$ LOGGER.info("Preventing player controller {} from being replaced by BetterSprinting with {}.", originalController, mc.playerController); -//$$ mc.playerController = originalController; -//$$ } -//$$ } -//$$ -//$$ @Override public void markBlockRangeForRenderUpdate(int x1, int y1, int z1, int x2, int y2, int z2) {} - //#if MC>=10800 - //$$ @Override public void notifyLightSet(BlockPos pos) {} - //$$ @Override public void spawnParticle(int p_180442_1_, boolean p_180442_2_, double p_180442_3_, double p_180442_5_, double p_180442_7_, double p_180442_9_, double p_180442_11_, double p_180442_13_, int... p_180442_15_) {} - //$$ @Override public void onEntityAdded(Entity entityIn) {} - //$$ @Override public void broadcastSound(int p_180440_1_, BlockPos p_180440_2_, int p_180440_3_) {} - //$$ @Override public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress) {} - //#else - //$$ @Override public void markBlockForRenderUpdate(int x, int y, int z) {} - //$$ @Override public void spawnParticle(String p_72708_1_, double p_72708_2_, double p_72708_4_, double p_72708_6_, double p_72708_8_, double p_72708_10_, double p_72708_12_) {} - //$$ @Override public void onEntityCreate(Entity entityIn) {} - //$$ @Override public void broadcastSound(int p_180440_1_, int x, int y, int z, int p_180440_3_) {} - //$$ @Override public void destroyBlockPartially(int breakerId, int x, int y, int z, int progress) {} - //$$ @Override public void onStaticEntitiesChanged() {} - //#endif - //#if MC>=10904 - //$$ @Override public void notifyBlockUpdate(World worldIn, BlockPos pos, IBlockState oldState, IBlockState newState, int flags) {} - //$$ @Override public void playSoundToAllNearExcept(@Nullable EntityPlayer player, SoundEvent soundIn, SoundCategory category, double x, double y, double z, float volume, float pitch) {} - //$$ @Override public void playRecord(SoundEvent soundIn, BlockPos pos) {} - //$$ @Override public void playEvent(EntityPlayer player, int type, BlockPos blockPosIn, int data) {} - //#if MC>=11100 - //#if MC>=11102 - //$$ @Override public void spawnParticle(int p_190570_1_, boolean p_190570_2_, boolean p_190570_3_, double p_190570_4_, double p_190570_6_, double p_190570_8_, double p_190570_10_, double p_190570_12_, double p_190570_14_, int... p_190570_16_) {} - //#else - //$$ @Override public void func_190570_a(int p_190570_1_, boolean p_190570_2_, boolean p_190570_3_, double p_190570_4_, double p_190570_6_, double p_190570_8_, double p_190570_10_, double p_190570_12_, double p_190570_14_, int... p_190570_16_) {} - //#endif - //#endif - //#else - //#if MC>=10800 - //$$ @Override public void markBlockForUpdate(BlockPos pos) {} - //$$ @Override public void playRecord(String recordName, BlockPos blockPosIn) {} - //#else - //$$ @Override public void markBlockForUpdate(int x, int y, int z) {} - //$$ @Override public void playRecord(String recordName, int x, int y, int z) {} - //#endif - //$$ @Override public void playSound(String soundName, double x, double y, double z, float volume, float pitch) {} - //$$ @Override public void playSoundToNearExcept(EntityPlayer except, String soundName, double x, double y, double z, float volume, float pitch) {} - //#if MC>=10809 - //$$ @Override public void playAuxSFX(EntityPlayer p_180439_1_, int p_180439_2_, BlockPos blockPosIn, int p_180439_4_) {} - //#else - //#if MC>=10800 - //$$ @Override public void playAusSFX(EntityPlayer p_180439_1_, int p_180439_2_, BlockPos blockPosIn, int p_180439_4_) {} - //#else - //$$ @Override public void playAuxSFX(EntityPlayer p_72706_1_, int p_72706_2_, int p_72706_3_, int p_72706_4_, int p_72706_5_, int p_72706_6_) {} - //#endif - //#endif - //#endif -//$$ } -//$$ } -//#endif +// 1.12.2 and below diff --git a/src/main/java/com/replaymod/compat/mapwriter/mixin/MixinMinecraft.java b/src/main/java/com/replaymod/compat/mapwriter/mixin/MixinMinecraft.java index e9119205..fca4cd6c 100644 --- a/src/main/java/com/replaymod/compat/mapwriter/mixin/MixinMinecraft.java +++ b/src/main/java/com/replaymod/compat/mapwriter/mixin/MixinMinecraft.java @@ -1,46 +1 @@ -//#if MC<11400 -//$$ package com.replaymod.compat.mapwriter.mixin; -//$$ -//$$ import com.replaymod.replay.ReplayModReplay; -//$$ import net.minecraft.client.Minecraft; -//$$ import net.minecraft.client.multiplayer.ServerData; -//$$ 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.CallbackInfoReturnable; -//$$ -//#if MC>=10800 -//$$ import net.minecraftforge.fml.common.Loader; -//#else -//$$ import cpw.mods.fml.common.Loader; -//#endif -//$$ -//$$ /** -//$$ * Approximately this for <1.12: https://github.com/Vectron/mapwriter/commit/68234520c7a3a0ae8201a085d7e66369900586ac -//$$ */ -//$$ @Mixin(Minecraft.class) -//$$ public abstract class MixinMinecraft { -//$$ -//$$ @Shadow -//$$ private ServerData currentServerData; -//$$ -//$$ @Inject(method = "getCurrentServerData", cancellable = true, at = @At("HEAD")) -//$$ private void replayModCompat_fixBug96(CallbackInfoReturnable ci) { -//$$ if (currentServerData == null -//$$ && (Loader.isModLoaded("mapwriter") || Loader.isModLoaded("MapWriter")) -//$$ && ReplayModReplay.instance.getReplayHandler() != null) { -//$$ for (StackTraceElement elem : Thread.currentThread().getStackTrace()) { -//$$ if ("mapwriter.util.Utils".equals(elem.getClassName()) && "getWorldName".equals(elem.getMethodName())) { - //#if MC>=10809 - //$$ ci.setReturnValue(new ServerData(null, "replay", false)); - //#else - //$$ ci.setReturnValue(new ServerData(null, "replay")); - //#endif -//$$ return; -//$$ } -//$$ } -//$$ } -//$$ } -//$$ } -//#endif +// 1.12.2 and below diff --git a/src/main/java/com/replaymod/compat/oranges17animations/HideInvisibleEntities.java b/src/main/java/com/replaymod/compat/oranges17animations/HideInvisibleEntities.java index 75ac3000..fca4cd6c 100644 --- a/src/main/java/com/replaymod/compat/oranges17animations/HideInvisibleEntities.java +++ b/src/main/java/com/replaymod/compat/oranges17animations/HideInvisibleEntities.java @@ -1,32 +1 @@ -//#if MC<11400 -//$$ package com.replaymod.compat.oranges17animations; -//$$ -//$$ import com.replaymod.replay.camera.CameraEntity; -//$$ import de.johni0702.minecraft.gui.utils.EventRegistrations; -//$$ import net.minecraft.client.Minecraft; -//$$ import net.minecraftforge.client.event.RenderLivingEvent; -//$$ import net.minecraftforge.fml.common.eventhandler.EventPriority; -//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -//$$ -//$$ import static com.replaymod.core.versions.MCVer.*; -//$$ -//$$ /** -//$$ * Orange seems to have copied vast parts of the RendererLivingEntity into their ArmorAnimation class which cancels the RenderLivingEvent.Pre and calls its own code instead. -//$$ * This breaks our mixin which assures that, even though the camera is in spectator mode, it cannot see invisible entities. -//$$ * -//$$ * To fix this issue, we simply cancel the RenderLivingEvent.Pre before it gets to ArmorAnimation if the entity is invisible. -//$$ */ -//$$ public class HideInvisibleEntities extends EventRegistrations { -//$$ private final Minecraft mc = Minecraft.getMinecraft(); -//$$ private final boolean modLoaded = isModLoaded("animations"); -//$$ -//$$ @SubscribeEvent(priority = EventPriority.HIGH) -//$$ public void preRenderLiving(RenderLivingEvent.Pre event) { -//$$ if (modLoaded) { -//$$ if (mc.player instanceof CameraEntity && getEntity(event).isInvisible()) { -//$$ event.setCanceled(true); -//$$ } -//$$ } -//$$ } -//$$ } -//#endif +// 1.12.2 and below diff --git a/src/main/java/com/replaymod/compat/shaders/ShaderBeginRender.java b/src/main/java/com/replaymod/compat/shaders/ShaderBeginRender.java index f9fd2cf5..8a782e8b 100644 --- a/src/main/java/com/replaymod/compat/shaders/ShaderBeginRender.java +++ b/src/main/java/com/replaymod/compat/shaders/ShaderBeginRender.java @@ -1,21 +1,13 @@ //#if MC>=10800 package com.replaymod.compat.shaders; +import com.replaymod.core.events.PreRenderCallback; import com.replaymod.render.hooks.EntityRendererHandler; import de.johni0702.minecraft.gui.utils.EventRegistrations; import net.minecraft.client.MinecraftClient; import java.lang.reflect.InvocationTargetException; -import static com.replaymod.core.versions.MCVer.getRenderPartialTicks; - -//#if FABRIC>=1 -import com.replaymod.core.events.PreRenderCallback; -//#else -//$$ import net.minecraftforge.eventbus.api.SubscribeEvent; -//$$ import net.minecraftforge.event.TickEvent; -//#endif - public class ShaderBeginRender extends EventRegistrations { private final MinecraftClient mc = MinecraftClient.getInstance(); @@ -25,14 +17,8 @@ public class ShaderBeginRender extends EventRegistrations { * as this would usually get called by EntityRenderer#renderWorld, * which we're not calling during rendering. */ - //#if FABRIC>=1 { on(PreRenderCallback.EVENT, this::onRenderTickStart); } private void onRenderTickStart() { - //#else - //$$ @SubscribeEvent - //$$ public void onRenderTickStart(TickEvent.RenderTickEvent event) { - //$$ if (event.phase != TickEvent.Phase.START) return; - //#endif if (ShaderReflection.shaders_beginRender == null) return; if (ShaderReflection.config_isShaders == null) return; @@ -48,7 +34,7 @@ public class ShaderBeginRender extends EventRegistrations { //#if MC>=11400 mc.gameRenderer.getCamera(), //#endif - getRenderPartialTicks(), 0); + mc.getTickDelta(), 0); } catch (IllegalAccessException | InvocationTargetException e) { e.printStackTrace(); } diff --git a/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaders.java b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaders.java index d14764d2..743815c4 100644 --- a/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaders.java +++ b/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaders.java @@ -1,52 +1 @@ -//#if MC<=10710 -//$$ package com.replaymod.compat.shaders.mixin; -//$$ -//$$ import com.replaymod.render.hooks.EntityRendererHandler; -//$$ import com.replaymod.replay.ReplayHandler; -//$$ import com.replaymod.replay.ReplayModReplay; -//$$ import net.minecraft.client.Minecraft; -//$$ import org.spongepowered.asm.mixin.Mixin; -//$$ import org.spongepowered.asm.mixin.Pseudo; -//$$ import org.spongepowered.asm.mixin.Shadow; -//$$ import org.spongepowered.asm.mixin.injection.At; -//$$ import org.spongepowered.asm.mixin.injection.Redirect; -//$$ -//$$ @Pseudo -//$$ @Mixin(targets = "shadersmod/client/Shaders", remap = false) -//$$ public abstract class MixinShaders { -//$$ -//$$ @Shadow static Minecraft mc; -//$$ @Shadow static long systemTime; -//$$ @Shadow static long lastSystemTime; -//$$ @Shadow static long diffSystemTime; -//$$ @Shadow static int frameCounter; -//$$ @Shadow static float frameTime; -//$$ @Shadow static float frameTimeCounter; -//$$ -//$$ @Redirect(method = "beginRender", at = @At(value = "INVOKE", target = "Ljava/lang/System;currentTimeMillis()J")) -//$$ private static long replayModCompat_currentTimeMillis() { -//$$ ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler(); -//$$ if (replayHandler != null) { -//$$ systemTime = replayHandler.getReplaySender().currentTimeStamp(); -//$$ -//$$ // We need to manipulate all the other previous-frame-time-based variables too -//$$ -//$$ lastSystemTime = 0; // Draw all frames as if they were the first ones -//$$ // diffSystemTime will be set to 0 by Shaders -//$$ // frameTime will be set to 0 by Shaders -//$$ frameTimeCounter = systemTime / 1000f; // will be %= 3600f by Shaders -//$$ -//$$ // Set frameCounter only if rendering is in progress -//$$ EntityRendererHandler entityRendererHandler = -//$$ ((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler(); -//$$ if (entityRendererHandler != null) { -//$$ frameCounter = entityRendererHandler.getRenderInfo().getFramesDone(); -//$$ } -//$$ -//$$ return systemTime; -//$$ } -//$$ return System.currentTimeMillis(); -//$$ } -//$$ -//$$ } -//#endif +// 1.7.10 and below diff --git a/src/main/java/com/replaymod/core/KeyBindingRegistry.java b/src/main/java/com/replaymod/core/KeyBindingRegistry.java index 3c6d2d14..6223717c 100644 --- a/src/main/java/com/replaymod/core/KeyBindingRegistry.java +++ b/src/main/java/com/replaymod/core/KeyBindingRegistry.java @@ -2,11 +2,11 @@ package com.replaymod.core; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; +import com.replaymod.core.events.PreRenderCallback; import com.replaymod.core.mixin.KeyBindingAccessor; import de.johni0702.minecraft.gui.utils.EventRegistrations; import com.replaymod.core.events.KeyBindingEventCallback; import com.replaymod.core.events.KeyEventCallback; -import com.replaymod.core.versions.MCVer; import net.minecraft.client.options.KeyBinding; import net.minecraft.util.crash.CrashReport; import net.minecraft.util.crash.CrashReportSection; @@ -22,15 +22,6 @@ import static com.replaymod.core.ReplayMod.MOD_ID; //$$ import net.minecraftforge.fml.client.registry.ClientRegistry; //#endif -//#if MC>=11400 -import com.replaymod.core.events.PreRenderCallback; -//#else -//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -//$$ import net.minecraftforge.fml.common.gameevent.TickEvent; -//$$ import org.lwjgl.input.Keyboard; -//$$ import static com.replaymod.core.versions.MCVer.FML_BUS; -//#endif - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -102,15 +93,7 @@ public class KeyBindingRegistry extends EventRegistrations { return Collections.unmodifiableSet(onlyInReplay); } - //#if MC>=11400 { on(PreRenderCallback.EVENT, this::handleRepeatedKeyBindings); } - //#else - //$$ @SubscribeEvent - //$$ public void onTick(TickEvent.RenderTickEvent event) { - //$$ if (event.phase != TickEvent.Phase.START) return; - //$$ handleRepeatedKeyBindings(); - //$$ } - //#endif public void handleRepeatedKeyBindings() { for (Binding binding : bindings.values()) { @@ -137,8 +120,8 @@ public class KeyBindingRegistry extends EventRegistrations { } catch (Throwable cause) { CrashReport crashReport = CrashReport.create(cause, "Handling Key Binding"); CrashReportSection category = crashReport.addElement("Key Binding"); - MCVer.addDetail(category, "Key Binding", () -> binding.name); - MCVer.addDetail(category, "Handler", runnable::toString); + category.add("Key Binding", () -> binding.name); + category.add("Handler", runnable::toString); throw new CrashException(crashReport); } } @@ -155,8 +138,8 @@ public class KeyBindingRegistry extends EventRegistrations { } catch (Throwable cause) { CrashReport crashReport = CrashReport.create(cause, "Handling Raw Key Binding"); CrashReportSection category = crashReport.addElement("Key Binding"); - MCVer.addDetail(category, "Key Code", () -> "" + keyCode); - MCVer.addDetail(category, "Handler", handler::toString); + category.add("Key Code", () -> "" + keyCode); + category.add("Handler", handler::toString); throw new CrashException(crashReport); } } @@ -177,7 +160,12 @@ public class KeyBindingRegistry extends EventRegistrations { } public String getBoundKey() { - return MCVer.getBoundKey(keyBinding); + try { + return keyBinding.getBoundKeyLocalizedText().getString(); + } catch (ArrayIndexOutOfBoundsException e) { + // Apparently windows likes to press strange keys, see https://www.replaymod.com/forum/thread/55 + return "Unknown"; + } } public boolean isBound() { diff --git a/src/main/java/com/replaymod/core/LoadingPlugin.java b/src/main/java/com/replaymod/core/LoadingPlugin.java index 5598ca01..fca4cd6c 100644 --- a/src/main/java/com/replaymod/core/LoadingPlugin.java +++ b/src/main/java/com/replaymod/core/LoadingPlugin.java @@ -1,104 +1 @@ -package com.replaymod.core; - -//#if MC<11400 -//$$ import net.minecraft.launchwrapper.Launch; -//$$ import org.apache.logging.log4j.LogManager; -//$$ import org.spongepowered.asm.launch.MixinBootstrap; -//$$ import org.spongepowered.asm.mixin.Mixins; -//$$ -//$$ import net.minecraftforge.fml.relauncher.CoreModManager; -//$$ import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; -//#if MC<10800 -//$$ import com.replaymod.core.asm.GLErrorTransformer; -//$$ import com.replaymod.core.asm.GLStateTrackerTransformer; -//$$ -//$$ import java.util.ArrayList; -//$$ import java.util.List; -//#endif -//$$ -//$$ import java.io.File; -//$$ import java.net.URISyntaxException; -//$$ import java.net.URL; -//$$ import java.security.CodeSource; -//$$ import java.util.Map; -//$$ -//$$ @IFMLLoadingPlugin.TransformerExclusions("com.replaymod.core.asm.") -//$$ public class LoadingPlugin implements IFMLLoadingPlugin { -//$$ -//$$ public LoadingPlugin() { -//$$ if (Launch.blackboard.get("fml.deobfuscatedEnvironment") != Boolean.FALSE) { -//$$ // Outside of the dev env, this is the job of the tweaker -//$$ MixinBootstrap.init(); -//$$ } -//$$ -//$$ Mixins.addConfiguration("mixins.core.replaymod.json"); -//$$ Mixins.addConfiguration("mixins.recording.replaymod.json"); -//$$ Mixins.addConfiguration("mixins.render.replaymod.json"); -//$$ Mixins.addConfiguration("mixins.render.blend.replaymod.json"); -//$$ Mixins.addConfiguration("mixins.replay.replaymod.json"); -//$$ Mixins.addConfiguration("mixins.compat.mapwriter.replaymod.json"); -//$$ Mixins.addConfiguration("mixins.compat.shaders.replaymod.json"); -//$$ Mixins.addConfiguration("mixins.extras.playeroverview.replaymod.json"); -//$$ -//$$ CodeSource codeSource = getClass().getProtectionDomain().getCodeSource(); -//$$ if (codeSource != null) { -//$$ URL location = codeSource.getLocation(); -//$$ try { -//$$ File file = new File(location.toURI()); -//$$ if (file.isFile()) { -//$$ // This forces forge to reexamine the jar file for FML mods -//$$ // Should eventually be handled by Mixin itself, maybe? - //#if MC>=10809 - //$$ CoreModManager.getIgnoredMods().remove(file.getName()); - //#else - //$$ CoreModManager.getLoadedCoremods().remove(file.getName()); - //#if MC<=10710 - //$$ CoreModManager.getReparseableCoremods().add(file.getName()); - //#endif - //#endif -//$$ } -//$$ } catch (URISyntaxException e) { -//$$ e.printStackTrace(); -//$$ } -//$$ } else { -//$$ LogManager.getLogger().warn("No CodeSource, if this is not a development environment we might run into problems!"); -//$$ LogManager.getLogger().warn(getClass().getProtectionDomain()); -//$$ } -//$$ } -//$$ -//$$ @Override -//$$ public String[] getASMTransformerClass() { - //#if MC>=10800 - //$$ return new String[]{ - //$$ }; - //#else - //$$ List transformers = new ArrayList<>(); - //$$ if ("true".equals(System.getProperty("replaymod.glerrors", "false"))) { - //$$ transformers.add(GLErrorTransformer.class.getName()); - //$$ } - //$$ transformers.add(GLStateTrackerTransformer.class.getName()); - //$$ return transformers.stream().toArray(String[]::new); - //#endif -//$$ } -//$$ -//$$ @Override -//$$ public String getModContainerClass() { -//$$ return null; -//$$ } -//$$ -//$$ @Override -//$$ public String getSetupClass() { -//$$ return null; -//$$ } -//$$ -//$$ @Override -//$$ public void injectData(Map data) { -//$$ } -//$$ -//$$ @Override -//$$ public String getAccessTransformerClass() { -//$$ return null; -//$$ } -//$$ -//$$ } -//#endif +// 1.12.2 and below diff --git a/src/main/java/com/replaymod/core/ReplayMod.java b/src/main/java/com/replaymod/core/ReplayMod.java index 6afd4bdd..8176cadc 100644 --- a/src/main/java/com/replaymod/core/ReplayMod.java +++ b/src/main/java/com/replaymod/core/ReplayMod.java @@ -5,87 +5,32 @@ import com.replaymod.compat.ReplayModCompat; import com.replaymod.core.gui.GuiBackgroundProcesses; import com.replaymod.core.gui.GuiReplaySettings; import com.replaymod.core.gui.RestoreReplayGui; -import com.replaymod.core.mixin.MinecraftAccessor; import com.replaymod.core.versions.MCVer; +import com.replaymod.core.versions.scheduler.Scheduler; +import com.replaymod.core.versions.scheduler.SchedulerImpl; import com.replaymod.editor.ReplayModEditor; import com.replaymod.extras.ReplayModExtras; import com.replaymod.recording.ReplayModRecording; import com.replaymod.render.ReplayModRender; import com.replaymod.replay.ReplayModReplay; +import com.replaymod.replaystudio.lib.viaversion.api.protocol.version.ProtocolVersion; import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ZipReplayFile; import com.replaymod.replaystudio.studio.ReplayStudio; -import com.replaymod.replaystudio.us.myles.ViaVersion.api.protocol.ProtocolVersion; import com.replaymod.replaystudio.util.I18n; -import com.replaymod.replaystudio.viaversion.ViaVersionPacketConverter; import com.replaymod.simplepathing.ReplayModSimplePathing; import de.johni0702.minecraft.gui.container.GuiScreen; import net.minecraft.client.MinecraftClient; -import net.minecraft.util.Identifier; +import net.minecraft.client.options.Option; import net.minecraft.resource.DirectoryResourcePack; -import net.minecraft.text.Text; -import net.minecraft.text.Style; import net.minecraft.text.LiteralText; +import net.minecraft.text.Style; +import net.minecraft.text.Text; import net.minecraft.text.TranslatableText; import net.minecraft.util.Formatting; -import net.minecraft.util.crash.CrashException; -import org.apache.commons.io.FilenameUtils; +import net.minecraft.util.Identifier; import org.apache.commons.io.FileUtils; - -//#if MC>=11400 -import net.minecraft.client.options.Option; -import net.minecraft.util.thread.ReentrantThreadExecutor; -//#endif - -//#if FABRIC>=1 -import net.fabricmc.api.ClientModInitializer; -import net.fabricmc.loader.api.FabricLoader; -//#else -//$$ import com.google.common.util.concurrent.ListenableFutureTask; -//$$ import net.minecraftforge.eventbus.api.SubscribeEvent; -//$$ import java.util.Queue; -//$$ import java.util.concurrent.FutureTask; -//$$ -//#if MC>=11400 -//$$ import com.replaymod.core.versions.LangResourcePack; -//$$ import net.minecraft.resources.IPackFinder; -//$$ import net.minecraft.resources.ResourcePackInfo; -//$$ import net.minecraftforge.fml.DeferredWorkQueue; -//$$ import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent; -//$$ import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; -//$$ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; -//$$ import net.minecraftforge.versions.mcp.MCPVersion; -//#else -//$$ import static com.replaymod.core.versions.MCVer.FML_BUS; -//$$ import net.minecraft.client.resources.IResourcePack; -//$$ import net.minecraftforge.common.config.Configuration; -//#endif -//$$ -//#if MC>=10800 -//$$ import net.minecraft.client.GameSettings; -//#endif -//$$ -//#if MC>=11400 -//$$ import net.minecraftforge.fml.ModList; -//#else -//$$ import net.minecraftforge.fml.common.Loader; -//$$ import net.minecraftforge.fml.common.Mod.EventHandler; -//$$ import net.minecraftforge.fml.common.Mod.Instance; -//$$ import net.minecraftforge.fml.common.event.FMLInitializationEvent; -//$$ import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; -//#if MC>=10800 -//$$ import net.minecraftforge.fml.client.FMLClientHandler; -//#else -//$$ import com.replaymod.replay.InputReplayTimer; -//$$ -//$$ import java.util.ArrayDeque; -//#endif -//#endif -//$$ import net.minecraftforge.fml.common.Mod; -//#if MC<11400 -//$$ import net.minecraftforge.fml.common.gameevent.TickEvent; -//#endif -//#endif +import org.apache.commons.io.FilenameUtils; import java.io.ByteArrayInputStream; import java.io.File; @@ -102,43 +47,9 @@ import java.nio.file.attribute.BasicFileAttributes; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -//#if FABRIC<=0 -//#if MC>=11400 -//$$ @Mod(ReplayMod.MOD_ID) -//#else -//$$ @Mod(modid = ReplayMod.MOD_ID, -//$$ useMetadata = true, -//$$ version = "@MOD_VERSION@", -//$$ acceptedMinecraftVersions = "@MC_VERSION@", -//$$ acceptableRemoteVersions = "*", - //#if MC>=10800 - //$$ clientSideOnly = true, - //$$ updateJSON = "https://raw.githubusercontent.com/ReplayMod/ReplayMod/master/versions.json", - //#endif -//$$ guiFactory = "com.replaymod.core.gui.GuiFactory") -//#endif -//#endif -public class ReplayMod implements - //#if FABRIC>=1 - ClientModInitializer, - //#endif - Module -{ - - public static String getMinecraftVersion() { - //#if MC>=11400 - return MinecraftClient.getInstance().getGame().getVersion().getName(); - //#else - //#if MC>=11400 - //$$ return MCPVersion.getMCVersion(); - //#else - //$$ return Loader.MC_VERSION; - //#endif - //#endif - } +public class ReplayMod implements Module, Scheduler { public static final String MOD_ID = "replaymod"; @@ -148,23 +59,15 @@ public class ReplayMod implements private static final MinecraftClient mc = MCVer.getMinecraft(); - //#if MC<11400 - //$$ @Deprecated - //$$ public static Configuration config; - //#endif - + private final ReplayModBackend backend; + private final SchedulerImpl scheduler = new SchedulerImpl(); private final KeyBindingRegistry keyBindingRegistry = new KeyBindingRegistry(); private final SettingsRegistry settingsRegistry = new SettingsRegistry(); { settingsRegistry.register(Setting.class); } - // The instance of your mod that Forge uses. - //#if MC>=11400 { instance = this; } - //#else - //$$ @Instance(MOD_ID) - //#endif public static ReplayMod instance; private final List modules = new ArrayList<>(); @@ -181,15 +84,15 @@ public class ReplayMod implements */ private boolean minimalMode; - public ReplayMod() { + public ReplayMod(ReplayModBackend backend) { + this.backend = backend; + I18n.setI18n(net.minecraft.client.resource.language.I18n::translate); - //#if MC>=11400 // Check Minecraft protocol version for compatibility if (!ProtocolVersion.isRegistered(MCVer.getProtocolVersion()) && !Boolean.parseBoolean(System.getProperty("replaymod.skipversioncheck", "false"))) { minimalMode = true; } - //#endif // Register all RM modules modules.add(this); @@ -202,21 +105,9 @@ public class ReplayMod implements modules.add(new ReplayModExtras(this)); modules.add(new ReplayModCompat()); - //#if MC>=11400 settingsRegistry.register(); - //#endif } - //#if MC<=11400 - //$$ @EventHandler - //$$ public void init(FMLPreInitializationEvent event) { - //$$ config = new Configuration(event.getSuggestedConfigurationFile()); - //$$ config.load(); - //$$ settingsRegistry.setConfiguration(config); - //$$ settingsRegistry.save(); // Save default values to disk - //$$ } - //#endif - public KeyBindingRegistry getKeyBindingRegistry() { return keyBindingRegistry; } @@ -256,6 +147,8 @@ public class ReplayMod implements try { Files.setAttribute(path, "dos:hidden", true); } catch (UnsupportedOperationException ignored) { + } catch (Exception e) { + e.printStackTrace(); } return path; } @@ -275,19 +168,14 @@ public class ReplayMod implements return replayFolder.resolve(relative); } - public static final DirectoryResourcePack jGuiResourcePack; + public static final DirectoryResourcePack jGuiResourcePack = createJGuiResourcePack(); public static final String JGUI_RESOURCE_PACK_NAME = "replaymod_jgui"; - static { // Note: even preInit is too late and we'd have to issue another resource reload - jGuiResourcePack = initJGuiResourcePack(); - } - - private static DirectoryResourcePack initJGuiResourcePack() { + private static DirectoryResourcePack createJGuiResourcePack() { File folder = new File("../jGui/src/main/resources"); if (!folder.exists()) { return null; } - //noinspection UnnecessaryLocalVariable - DirectoryResourcePack jGuiResourcePack = new DirectoryResourcePack(folder) { + return new DirectoryResourcePack(folder) { @Override //#if MC>=11400 public String getName() { @@ -315,56 +203,13 @@ public class ReplayMod implements } } }; - - //#if MC<=11202 - //$$ @SuppressWarnings("unchecked") - //$$ List defaultResourcePacks = ((MinecraftAccessor) mc).getDefaultResourcePacks(); - //$$ defaultResourcePacks.add(jGuiResourcePack); - //#endif - - //#if MC<=10710 - //$$ FolderResourcePack mainResourcePack = new FolderResourcePack(new File("../src/main/resources")) { - //$$ @Override - //$$ protected InputStream getInputStreamByName(String resourceName) throws IOException { - //$$ try { - //$$ return super.getInputStreamByName(resourceName); - //$$ } catch (IOException e) { - //$$ if ("pack.mcmeta".equals(resourceName)) { - //$$ return new ByteArrayInputStream(("{\"pack\": {\"description\": \"dummy pack for mod resources in dev-env\", \"pack_format\": 1}}").getBytes(StandardCharsets.UTF_8)); - //$$ } - //$$ throw e; - //$$ } - //$$ } - //$$ }; - //$$ defaultResourcePacks.add(mainResourcePack); - //#endif - - return jGuiResourcePack; } - //#if FABRIC>=1 - @Override - public void onInitializeClient() { + void initModules() { modules.forEach(Module::initCommon); modules.forEach(Module::initClient); modules.forEach(m -> m.registerKeyBindings(keyBindingRegistry)); } - //#else - //#if MC>=11400 - //$$ { - //$$ FMLJavaModLoadingContext.get().getModEventBus().addListener((FMLCommonSetupEvent event) -> modules.forEach(Module::initCommon)); - //$$ FMLJavaModLoadingContext.get().getModEventBus().addListener((FMLClientSetupEvent event) -> modules.forEach(Module::initClient)); - //$$ FMLJavaModLoadingContext.get().getModEventBus().addListener((FMLClientSetupEvent event) -> modules.forEach(m -> m.registerKeyBindings(keyBindingRegistry))); - //$$ } - //#else - //$$ @EventHandler - //$$ public void init(FMLInitializationEvent event) { - //$$ modules.forEach(Module::initCommon); - //$$ modules.forEach(Module::initClient); - //$$ modules.forEach(m -> m.registerKeyBindings(keyBindingRegistry)); - //$$ } - //#endif - //#endif @Override public void registerKeyBindings(KeyBindingRegistry registry) { @@ -375,26 +220,16 @@ public class ReplayMod implements @Override public void initClient() { - //#if MC<=10710 - //$$ FML_BUS.register(this); // For runLater(Runnable) - //#endif - backgroundProcesses.register(); keyBindingRegistry.register(); // 1.7.10 crashes when render distance > 16 //#if MC>=10800 if (!MCVer.hasOptifine()) { - //#if MC>=11400 Option.RENDER_DISTANCE.setMax(64f); - //#else - //$$ GameSettings.Options.RENDER_DISTANCE.setValueMax(64f); - //#endif } //#endif - testIfMoeshAndExitMinecraft(); - runPostStartup(() -> { final long DAYS = 24 * 60 * 60 * 1000; @@ -493,231 +328,41 @@ public class ReplayMod implements }); } - /** - * Execute the given runnable on the main client thread, returning only after it has been run (or after 30 seconds). - */ + @Override public void runSync(Runnable runnable) throws InterruptedException, ExecutionException, TimeoutException { - //#if MC>=11400 - if (mc.isOnThread()) { - runnable.run(); - } else { - executor.submit(() -> { - runnable.run(); - return null; - }).get(30, TimeUnit.SECONDS); - } - //#else - //$$ if (mc.isCallingFromMinecraftThread()) { - //$$ runnable.run(); - //$$ } else { - //$$ FutureTask future = new FutureTask<>(runnable, null); - //$$ runLater(future); - //$$ future.get(30, TimeUnit.SECONDS); - //$$ } - //#endif + scheduler.runSync(runnable); } - /** - * Execute the given runnable after game has started (once the overlay has been closed). - * Most importantly, it will run after resources (including language keys!) have been loaded. - * Below 1.14, this is equivalent to {@link #runLater(Runnable)}. - */ + @Override public void runPostStartup(Runnable runnable) { - runLater(new Runnable() { - @Override - public void run() { - //#if MC>=11400 - if (getMinecraft().overlay != null) { - // delay until after resources have been loaded - runLater(this); - return; - } - //#endif - runnable.run(); - } - }); + scheduler.runPostStartup(runnable); } - /** - * Set when the currently running code has been scheduled by runLater. - * If this is the case, subsequent calls to runLater have to be delayed until all scheduled tasks have been - * processed, otherwise a livelock may occur. - */ - private boolean inRunLater = false; - //#if MC>=11400 - private boolean inRenderTaskQueue = false; - // Starting 1.14 MC clears the queue of scheduled tasks on disconnect. - // This works fine for MC since it uses the queue only for packet handling but breaks our assumption that - // stuff submitted via runLater is actually always run (e.g. recording might not be fully stopped because parts - // of that are run via runLater and stopping the recording happens right around the time MC clears the queue). - // Luckily, that's also the version where MC pulled out the executor implementation, so we can just spin up our own. - public static class ReplayModExecutor extends ReentrantThreadExecutor { - private final Thread mcThread = Thread.currentThread(); - // Fail-fast in case we ever switch to async loading and forget to change this - // (except for fabric 1.15+ because it loads the mod before the client thread is set) - //#if FABRIC<1 || MC<11500 - //$$ { if (!MinecraftClient.getInstance().isOnThread()) throw new RuntimeException(); } - //#endif - - private ReplayModExecutor(String string_1) { - super(string_1); - } - - @Override - protected Runnable createTask(Runnable runnable) { - return runnable; - } - - @Override - protected boolean canExecute(Runnable runnable) { - return true; - } - - @Override - protected Thread getThread() { - return mcThread; - } - - @Override - public void runTasks() { - super.runTasks(); - } - } - public final ReplayModExecutor executor = new ReplayModExecutor("Client/ReplayMod"); - //#endif - - // Pre-1.14 MC would hold the lock on the scheduledTasks queue while executing its tasks - // such that no new tasks could be submitted while one of them was running. - // This would cause issues with long-running tasks (e.g. video rendering) as it would - // block all async tasks (e.g. skin loading). + @Override public void runLaterWithoutLock(Runnable runnable) { - //#if MC>=11400 - // MC 1.14+ no longer synchronizes on the queue while running its tasks - runLater(runnable); - //#else - //$$ runLater(() -> runLaterWithoutLock(runnable), runnable); - //#endif + scheduler.runLaterWithoutLock(runnable); } + @Override public void runLater(Runnable runnable) { - runLater(runnable, () -> runLater(runnable)); + scheduler.runLater(runnable); } - private void runLater(Runnable runnable, Runnable defer) { - //#if MC>=11400 - if (mc.isOnThread() && inRunLater && !inRenderTaskQueue) { - ((MinecraftAccessor) mc).getRenderTaskQueue().offer(() -> { - inRenderTaskQueue = true; - try { - defer.run(); - } finally { - inRenderTaskQueue = false; - } - }); - } else { - executor.send(() -> { - inRunLater = true; - try { - runnable.run(); - } catch (CrashException e) { - e.printStackTrace(); - System.err.println(e.getReport().asString()); - mc.setCrashReport(e.getReport()); - } finally { - inRunLater = false; - } - }); - } - //#else - //$$ if (mc.isCallingFromMinecraftThread() && inRunLater) { - //#if MC>=10800 - //$$ FML_BUS.register(new Object() { - //$$ @SubscribeEvent - //$$ public void onRenderTick(TickEvent.RenderTickEvent event) { - //$$ if (event.phase == TickEvent.Phase.START) { - //$$ FML_BUS.unregister(this); - //$$ defer.run(); - //$$ } - //$$ } - //$$ }); - //#else - //$$ FML_BUS.register(new RunLaterHelper(defer)); - //#endif - //$$ return; - //$$ } - //#if MC>=10800 - //$$ Queue> tasks = ((MinecraftAccessor) mc).getScheduledTasks(); - //$$ //noinspection SynchronizationOnLocalVariableOrMethodParameter - //$$ synchronized (tasks) { - //#else - //$$ Queue> tasks = scheduledTasks; - //$$ synchronized (scheduledTasks) { - //#endif - //$$ tasks.add(ListenableFutureTask.create(() -> { - //$$ inRunLater = true; - //$$ try { - //$$ runnable.run(); - //$$ } catch (ReportedException e) { - //$$ e.printStackTrace(); - //$$ System.err.println(e.getCrashReport().getCompleteReport()); - //$$ mc.crashed(e.getCrashReport()); - //$$ } finally { - //$$ inRunLater = false; - //$$ } - //$$ }, null)); - //$$ } - //#endif + @Override + public void runTasks() { + scheduler.runTasks(); } - //#if MC<=10710 - //$$ // 1.7.10: Cannot use MC's because it is processed only during ticks (so not at all when replay is paused) - //$$ private final Queue> scheduledTasks = new ArrayDeque<>(); - //$$ - //$$ // in 1.7.10 apparently events can't be delivered to anonymous classes - //$$ public class RunLaterHelper { - //$$ private final Runnable defer; - //$$ - //$$ private RunLaterHelper(Runnable defer) { - //$$ this.defer = defer; - //$$ } - //$$ - //$$ @SubscribeEvent - //$$ public void onRenderTick(TickEvent.RenderTickEvent event) { - //$$ if (event.phase == TickEvent.Phase.START) { - //$$ FML_BUS.unregister(this); - //$$ defer.run(); - //$$ } - //$$ } - //$$ } - //$$ - //$$ @SubscribeEvent - //$$ public void runScheduledTasks(InputReplayTimer.RunScheduledTasks event) { - //$$ synchronized (scheduledTasks) { - //$$ while (!scheduledTasks.isEmpty()) { - //$$ scheduledTasks.poll().run(); - //$$ } - //$$ } - //$$ } - //#endif - public String getVersion() { - //#if FABRIC>=1 - return FabricLoader.getInstance().getModContainer(MOD_ID) - .orElseThrow(IllegalStateException::new) - .getMetadata().getVersion().toString(); - //#else - //#if MC>=11400 - //$$ return ModList.get().getModContainerById(MOD_ID).get().getModInfo().getVersion().toString(); - //#else - //$$ return Loader.instance().getIndexedModList().get(MOD_ID).getVersion(); - //#endif - //#endif + return backend.getVersion(); } - private void testIfMoeshAndExitMinecraft() { - if("currentPlayer".equals("Moesh")) { - System.exit(-1); - } + public String getMinecraftVersion() { + return backend.getMinecraftVersion(); + } + + public boolean isModLoaded(String id) { + return backend.isModLoaded(id); } public MinecraftClient getMinecraft() { diff --git a/src/main/java/com/replaymod/core/ReplayModBackend.java b/src/main/java/com/replaymod/core/ReplayModBackend.java new file mode 100644 index 00000000..d6313852 --- /dev/null +++ b/src/main/java/com/replaymod/core/ReplayModBackend.java @@ -0,0 +1,29 @@ +package com.replaymod.core; + +import net.fabricmc.api.ClientModInitializer; +import net.fabricmc.loader.api.FabricLoader; + +import static com.replaymod.core.ReplayMod.MOD_ID; + +public class ReplayModBackend implements ClientModInitializer { + private final ReplayMod mod = new ReplayMod(this); + + @Override + public void onInitializeClient() { + mod.initModules(); + } + + public String getVersion() { + return FabricLoader.getInstance().getModContainer(MOD_ID) + .orElseThrow(IllegalStateException::new) + .getMetadata().getVersion().toString(); + } + + public String getMinecraftVersion() { + return mod.getMinecraft().getGame().getVersion().getName(); + } + + public boolean isModLoaded(String id) { + return FabricLoader.getInstance().isModLoaded(id.toLowerCase()); + } +} diff --git a/src/main/java/com/replaymod/core/ReplayModMMLauncher.java b/src/main/java/com/replaymod/core/ReplayModMMLauncher.java index e8de4c90..782d0fde 100644 --- a/src/main/java/com/replaymod/core/ReplayModMMLauncher.java +++ b/src/main/java/com/replaymod/core/ReplayModMMLauncher.java @@ -1,14 +1,8 @@ //#if FABRIC>=1 package com.replaymod.core; -import com.replaymod.extras.modcore.ModCoreInstaller; -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.ModContainer; import org.spongepowered.asm.mixin.Mixins; -import java.io.File; -import java.util.Optional; - // We need to wait for MM to call us, otherwise we might initialize our mixins before it calls optifabric which would // result in OF classes not existing while our mixins get resolved. public class ReplayModMMLauncher implements Runnable { @@ -32,43 +26,6 @@ public class ReplayModMMLauncher implements Runnable { Mixins.addConfiguration("mixins.render.blend.replaymod.json"); Mixins.addConfiguration("mixins.render.replaymod.json"); Mixins.addConfiguration("mixins.replay.replaymod.json"); - - try { - initModCore(); - } catch (Throwable t) { - System.err.println("ReplayMod caught error during ModCore init:"); - t.printStackTrace(); - } - } - - // Forge equivalent is in ReplayModTweaker - private void initModCore() { - if (System.getProperty("REPLAYMOD_SKIP_MODCORE", "false").equalsIgnoreCase("true")) { - System.out.println("ReplayMod not initializing ModCore because REPLAYMOD_SKIP_MODCORE is true."); - return; - } - - if (FabricLoader.getInstance().isDevelopmentEnvironment()) { - System.out.println("ReplayMod not initializing ModCore because we're in a development environment."); - return; - } - - File gameDir = FabricLoader.getInstance().getGameDirectory(); - - Optional minecraft = FabricLoader.getInstance().getModContainer("minecraft"); - if (!minecraft.isPresent()) { - System.err.println("ReplayMod could not determine Minecraft version, skipping ModCore."); - return; - } - String mcVer = minecraft.get().getMetadata().getVersion().getFriendlyString(); - - int result = ModCoreInstaller.initialize(gameDir, mcVer + "_fabric"); - if (result != -2) { // Don't even bother logging the result if there's no ModCore for this version. - System.out.println("ReplayMod ModCore init result: " + result); - } - if (ModCoreInstaller.isErrored()) { - System.err.println(ModCoreInstaller.getError()); - } } } //#endif diff --git a/src/main/java/com/replaymod/core/SettingsRegistry.java b/src/main/java/com/replaymod/core/SettingsRegistry.java index ff911a1d..79bbcb60 100644 --- a/src/main/java/com/replaymod/core/SettingsRegistry.java +++ b/src/main/java/com/replaymod/core/SettingsRegistry.java @@ -1,130 +1,18 @@ package com.replaymod.core; import com.replaymod.core.events.SettingsChangedCallback; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.*; -//#if MC>=11400 -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonPrimitive; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.nio.file.Files; -import java.nio.file.Path; -//#else -//#if MC>=11400 -//$$ import net.minecraftforge.common.ForgeConfigSpec; -//$$ import net.minecraftforge.fml.ModLoadingContext; -//$$ import net.minecraftforge.fml.config.ModConfig; -//$$ import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; -//#else -//$$ import net.minecraftforge.common.config.Configuration; -//#endif -//#endif - -import static com.replaymod.core.versions.MCVer.*; - public class SettingsRegistry { - private static Logger LOGGER = LogManager.getLogger(); - private Map, Object> settings = Collections.synchronizedMap(new LinkedHashMap<>()); - //#if MC>=11400 - private final Path configFile = getMinecraft().runDirectory.toPath().resolve("config/replaymod.json"); - //#else - //$$ private static final Object NULL_OBJECT = new Object(); - //#if MC>=11400 - //$$ private ForgeConfigSpec spec; - //$$ private ModConfig config; - //#else - //$$ private Configuration configuration; - //#endif - //#endif + private final Map, Object> settings = Collections.synchronizedMap(new LinkedHashMap<>()); + final SettingsRegistryBackend backend = new SettingsRegistryBackend(settings); - //#if MC>=11400 public void register() { - String config; - if (Files.exists(configFile)) { - try { - config = new String(Files.readAllBytes(configFile), StandardCharsets.UTF_8); - } catch (IOException e) { - e.printStackTrace(); - return; - } - } else { - save(); - return; - } - Gson gson = new Gson(); - JsonObject root = gson.fromJson(config, JsonObject.class); - if (root == null) { - LOGGER.error("Config file {} appears corrupted: {}", configFile, config); - save(); - return; - } - for (Map.Entry, Object> entry : settings.entrySet()) { - SettingKey key = entry.getKey(); - JsonElement category = root.get(key.getCategory()); - if (category != null && category.isJsonObject()) { - JsonElement valueElem = category.getAsJsonObject().get(key.getKey()); - if (valueElem == null || !valueElem.isJsonPrimitive()) continue; - JsonPrimitive value = valueElem.getAsJsonPrimitive(); - if (key.getDefault() instanceof Boolean && value.isBoolean()) { - entry.setValue(value.getAsBoolean()); - } - if (key.getDefault() instanceof Integer && value.isNumber()) { - entry.setValue(value.getAsNumber().intValue()); - } - if (key.getDefault() instanceof Double && value.isNumber()) { - entry.setValue(value.getAsNumber().doubleValue()); - } - if (key.getDefault() instanceof String && value.isString()) { - entry.setValue(value.getAsString()); - } - } - } + backend.register(); } - //#else - //#if MC>=11400 - //$$ public void register() { - //$$ if (spec == null) { - //$$ ForgeConfigSpec.Builder builder = new ForgeConfigSpec.Builder(); - //$$ for (SettingKey key : settings.keySet()) { - //$$ builder - //$$ .translation(key.getDisplayString()) - //$$ .define(key.getCategory() + "." + key.getKey(), key.getDefault()); - //$$ } - //$$ spec = builder.build(); - //$$ } - //$$ FMLJavaModLoadingContext.get().getModEventBus().addListener(this::load); - //$$ ModLoadingContext.get().registerConfig(ModConfig.Type.CLIENT, spec); - //$$ } - //$$ - //$$ private void load(ModConfig.Loading event) { - //$$ config = event.getConfig(); - //$$ for (Map.Entry, Object> entry : settings.entrySet()) { - //$$ SettingKey key = entry.getKey(); - //$$ Object value = config.getConfigData().get(key.getCategory() + "." + key.getKey()); - //$$ entry.setValue(value == null ? key.getDefault() : value); - //$$ } - //$$ } - //#else - //$$ public void setConfiguration(Configuration configuration) { - //$$ this.configuration = configuration; - //$$ - //$$ List> keys = new ArrayList<>(settings.keySet()); - //$$ settings.clear(); - //$$ for (SettingKey key : keys) { - //$$ register(key); - //$$ } - //$$ } - //#endif - //#endif public void register(Class settingsClass) { for (Field field : settingsClass.getDeclaredFields()) { @@ -140,34 +28,8 @@ public class SettingsRegistry { } public void register(SettingKey key) { - //#if MC>=11400 settings.put(key, key.getDefault()); - //#else - //#if MC>=11400 - //$$ if (spec != null) { - //$$ throw new IllegalStateException("Cannot register more settings are spec has been built."); - //$$ } - //$$ settings.put(key, NULL_OBJECT); - //#else - //$$ Object value; - //$$ if (configuration != null) { - //$$ if (key.getDefault() instanceof Boolean) { - //$$ value = configuration.get(key.getCategory(), key.getKey(), (Boolean) key.getDefault()).getBoolean(); - //$$ } else if (key.getDefault() instanceof Integer) { - //$$ value = configuration.get(key.getCategory(), key.getKey(), (Integer) key.getDefault()).getInt(); - //$$ } else if (key.getDefault() instanceof Double) { - //$$ value = configuration.get(key.getCategory(), key.getKey(), (Double) key.getDefault()).getDouble(); - //$$ } else if (key.getDefault() instanceof String) { - //$$ value = configuration.get(key.getCategory(), key.getKey(), (String) key.getDefault()).getString(); - //$$ } else { - //$$ throw new IllegalArgumentException("Default type " + key.getDefault().getClass() + " not supported."); - //$$ } - //$$ } else { - //$$ value = NULL_OBJECT; - //$$ } - //$$ settings.put(key, value); - //#endif - //#endif + backend.register(key); } public Set> getSettings() { @@ -183,68 +45,13 @@ public class SettingsRegistry { } public void set(SettingKey key, T value) { - //#if MC<11400 - //#if MC>=11400 - //$$ if (config != null) { - //$$ config.getConfigData().set(key.getCategory() + "." + key.getKey(), value); - //$$ } - //#else - //$$ if (key.getDefault() instanceof Boolean) { - //$$ configuration.get(key.getCategory(), key.getKey(), (Boolean) key.getDefault()).set((Boolean) value); - //$$ } else if (key.getDefault() instanceof Integer) { - //$$ configuration.get(key.getCategory(), key.getKey(), (Integer) key.getDefault()).set((Integer) value); - //$$ } else if (key.getDefault() instanceof Double) { - //$$ configuration.get(key.getCategory(), key.getKey(), (Double) key.getDefault()).set((Double) value); - //$$ } else if (key.getDefault() instanceof String) { - //$$ configuration.get(key.getCategory(), key.getKey(), (String) key.getDefault()).set((String) value); - //$$ } else { - //$$ throw new IllegalArgumentException("Default type " + key.getDefault().getClass() + " not supported."); - //$$ } - //#endif - //#endif + backend.update(key, value); settings.put(key, value); SettingsChangedCallback.EVENT.invoker().onSettingsChanged(this, key); } public void save() { - //#if MC>=11400 - JsonObject root = new JsonObject(); - for (Map.Entry, Object> entry : settings.entrySet()) { - SettingKey key = entry.getKey(); - JsonObject category = root.getAsJsonObject(key.getCategory()); - if (category == null) { - category = new JsonObject(); - root.add(key.getCategory(), category); - } - - Object value = entry.getValue(); - if (value instanceof Boolean) { - category.addProperty(key.getKey(), (Boolean) value); - } - if (value instanceof Number) { - category.addProperty(key.getKey(), (Number) value); - } - if (value instanceof String) { - category.addProperty(key.getKey(), (String) value); - } - } - Gson gson = new GsonBuilder().setPrettyPrinting().create(); - String config = gson.toJson(root); - try { - Files.createDirectories(configFile.getParent()); - Files.write(configFile, config.getBytes(StandardCharsets.UTF_8)); - } catch (IOException e) { - e.printStackTrace(); - } - //#else - //#if MC>=11400 - //$$ if (config != null) { - //$$ config.save(); - //$$ } - //#else - //$$ configuration.save(); - //#endif - //#endif + backend.save(); } public interface SettingKey { diff --git a/src/main/java/com/replaymod/core/SettingsRegistryBackend.java b/src/main/java/com/replaymod/core/SettingsRegistryBackend.java new file mode 100644 index 00000000..eb996915 --- /dev/null +++ b/src/main/java/com/replaymod/core/SettingsRegistryBackend.java @@ -0,0 +1,188 @@ +package com.replaymod.core; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.JsonArray; +import com.google.gson.JsonElement; +import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; +import com.replaymod.core.events.SettingsChangedCallback; +import net.minecraft.client.MinecraftClient; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardWatchEventKinds; +import java.nio.file.WatchEvent; +import java.nio.file.WatchKey; +import java.nio.file.WatchService; +import java.util.List; +import java.util.Map; + +import static com.replaymod.core.versions.MCVer.getMinecraft; + +class SettingsRegistryBackend { + private static final Logger LOGGER = LogManager.getLogger(); + private final Map, Object> settings; + + private final Path configFile = getMinecraft().runDirectory.toPath().resolve("config/replaymod.json"); + + SettingsRegistryBackend(Map, Object> settings) { + this.settings = settings; + } + + public void register() { + load(true); + try { + registerWatcher(); + } catch (IOException e) { + LOGGER.warn("Failed to setup file watcher for {}, live-reloading is disabled. Cause: {}", configFile, e); + } + } + + private void load(boolean createIfMissingOrBroken) { + String config; + if (Files.exists(configFile)) { + try { + config = new String(Files.readAllBytes(configFile), StandardCharsets.UTF_8); + } catch (IOException e) { + e.printStackTrace(); + return; + } + } else { + if (createIfMissingOrBroken) { + save(); + } + return; + } + Gson gson = new Gson(); + JsonObject root = gson.fromJson(config, JsonObject.class); + if (root == null) { + LOGGER.error("Config file {} appears corrupted: {}", configFile, config); + if (createIfMissingOrBroken) { + save(); + } + return; + } + for (Map.Entry, Object> entry : settings.entrySet()) { + SettingsRegistry.SettingKey key = entry.getKey(); + JsonElement category = root.get(key.getCategory()); + if (category != null && category.isJsonObject()) { + JsonElement valueElem = category.getAsJsonObject().get(key.getKey()); + if (valueElem == null || !valueElem.isJsonPrimitive()) continue; + JsonPrimitive value = valueElem.getAsJsonPrimitive(); + if (key.getDefault() instanceof Boolean && value.isBoolean()) { + entry.setValue(value.getAsBoolean()); + } + if (key.getDefault() instanceof Integer && value.isNumber()) { + entry.setValue(value.getAsNumber().intValue()); + } + if (key.getDefault() instanceof Double && value.isNumber()) { + entry.setValue(value.getAsNumber().doubleValue()); + } + if (key.getDefault() instanceof String && value.isString()) { + String valueStr = value.getAsString(); + if (entry instanceof SettingsRegistry.MultipleChoiceSettingKey) { + @SuppressWarnings("unchecked") + List choices = ((SettingsRegistry.MultipleChoiceSettingKey) entry).getChoices(); + if (choices.stream().noneMatch(valueStr::equals)) { + continue; + } + } + entry.setValue(valueStr); + } + } + } + } + + private void registerWatcher() throws IOException { + WatchService watchService = configFile.getFileSystem().newWatchService(); + configFile.getParent().register(watchService, StandardWatchEventKinds.ENTRY_CREATE, StandardWatchEventKinds.ENTRY_MODIFY); + Thread thread = new Thread(() -> { + while (true) { + WatchKey nextKey; + try { + nextKey = watchService.take(); + } catch (InterruptedException e) { + return; + } + for (WatchEvent event : nextKey.pollEvents()) { + WatchEvent.Kind kind = event.kind(); + if (kind == StandardWatchEventKinds.OVERFLOW) { + continue; + } + Path fileName = ((Path) event.context()); + if (fileName.equals(configFile.getFileName())) { + MinecraftClient.getInstance().send(this::reload); + } + } + if (!nextKey.reset()) { + return; + } + } + }); + thread.setName("replaymod-config-watcher"); + thread.setDaemon(true); + thread.start(); + } + + private void reload() { + load(false); + + SettingsRegistry settingsRegistry = ReplayMod.instance.getSettingsRegistry(); + for (SettingsRegistry.SettingKey key : settings.keySet()) { + SettingsChangedCallback.EVENT.invoker().onSettingsChanged(settingsRegistry, key); + } + } + + @SuppressWarnings("unused") + public void register(SettingsRegistry.SettingKey key) { + } + + @SuppressWarnings("unused") + public void update(SettingsRegistry.SettingKey key, T value) { + } + + public void save() { + JsonObject root = new JsonObject(); + for (Map.Entry, Object> entry : settings.entrySet()) { + SettingsRegistry.SettingKey key = entry.getKey(); + JsonObject category = root.getAsJsonObject(key.getCategory()); + if (category == null) { + category = new JsonObject(); + root.add(key.getCategory(), category); + } + + Object value = entry.getValue(); + if (value instanceof Boolean) { + category.addProperty(key.getKey(), (Boolean) value); + } + if (value instanceof Number) { + category.addProperty(key.getKey(), (Number) value); + } + if (value instanceof String) { + category.addProperty(key.getKey(), (String) value); + if (key instanceof SettingsRegistry.MultipleChoiceSettingKey) { + @SuppressWarnings("unchecked") + List choices = ((SettingsRegistry.MultipleChoiceSettingKey) key).getChoices(); + JsonArray array = new JsonArray(); + for (String choice : choices) { + array.add(choice); + } + category.add(key.getKey() + "_valid_values", array); + } + } + } + Gson gson = new GsonBuilder().setPrettyPrinting().create(); + String config = gson.toJson(root); + try { + Files.createDirectories(configFile.getParent()); + Files.write(configFile, config.getBytes(StandardCharsets.UTF_8)); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/src/main/java/com/replaymod/core/asm/GLErrorTransformer.java b/src/main/java/com/replaymod/core/asm/GLErrorTransformer.java index dac9dde9..743815c4 100644 --- a/src/main/java/com/replaymod/core/asm/GLErrorTransformer.java +++ b/src/main/java/com/replaymod/core/asm/GLErrorTransformer.java @@ -1,95 +1 @@ -//#if MC<=10710 -//$$ package com.replaymod.core.asm; -//$$ -//$$ import net.minecraft.launchwrapper.IClassTransformer; -//$$ import org.lwjgl.opengl.GL11; -//$$ import org.lwjgl.util.glu.GLU; -//$$ import org.objectweb.asm.ClassReader; -//$$ import org.objectweb.asm.ClassVisitor; -//$$ import org.objectweb.asm.ClassWriter; -//$$ import org.objectweb.asm.MethodVisitor; -//$$ import org.objectweb.asm.Opcodes; -//$$ -//$$ /** -//$$ * Insert glGetError checks after all calls to any method in any GL*, ARB* and EXT* classes -//$$ */ -//$$ public class GLErrorTransformer implements IClassTransformer { -//$$ private static final String GL_CLASS = "org.lwjgl.opengl.GL"; -//$$ private static final String GL = GL_CLASS.replace('.', '/'); -//$$ private static final String ARB_CLASS = "org.lwjgl.opengl.ARB"; -//$$ private static final String ARB = ARB_CLASS.replace('.', '/'); -//$$ private static final String EXT_CLASS = "org.lwjgl.opengl.EXT"; -//$$ private static final String EXT = EXT_CLASS.replace('.', '/'); -//$$ private static final String GLErrorTransformer_CLASS = GLErrorTransformer.class.getName(); -//$$ private static final String GLErrorTransformer = GLErrorTransformer_CLASS.replace('.', '/'); -//$$ -//$$ @Override -//$$ public byte[] transform(String name, String transformedName, byte[] basicClass) { -//$$ if (basicClass == null) return null; -//$$ -//$$ ClassReader reader = new ClassReader(basicClass); -//$$ ClassWriter writer = new ClassWriter(reader, 0); -//$$ reader.accept(new ClassVisitor(Opcodes.ASM5, writer) { -//$$ @Override -//$$ public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { -//$$ MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); -//$$ return new MethodVisitor(Opcodes.ASM5, mv) { -//$$ @Override -//$$ public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { -//$$ super.visitMethodInsn(opcode, owner, name, desc, itf); -//$$ if (owner.startsWith(GL) || owner.startsWith(ARB) || owner.startsWith(EXT)) { -//$$ visitLdcInsn(owner.replace('/', '.')); -//$$ visitLdcInsn(name); -//$$ super.visitMethodInsn(Opcodes.INVOKESTATIC, GLErrorTransformer, "glErrorCheck", "(Ljava/lang/String;Ljava/lang/String;)V", false); -//$$ } -//$$ } -//$$ -//$$ @Override -//$$ public void visitMaxs(int maxStack, int maxLocals) { -//$$ super.visitMaxs(maxStack + 2, maxLocals); -//$$ } -//$$ }; -//$$ } -//$$ }, 0); -//$$ return writer.toByteArray(); -//$$ } -//$$ -//$$ private static boolean inGlBegin; -//$$ -//$$ @SuppressWarnings("unused") // Called via ASM -//$$ public static void glErrorCheck(String cls, String method) { -//$$ if (method.equals("glBegin") && cls.equals(GL_CLASS + "11")) { -//$$ if (inGlBegin) { -//$$ // glBegin within glBegin -//$$ throw new GLError(GL11.GL_INVALID_OPERATION, cls, method); -//$$ } -//$$ inGlBegin = true; -//$$ return; -//$$ } -//$$ if (inGlBegin && method.equals("glEnd") && cls.equals(GL_CLASS + "11")) { -//$$ inGlBegin = false; -//$$ } -//$$ if (inGlBegin) { -//$$ return; // Cannot call glGetError in between of glBegin and glEnd -//$$ } -//$$ int err = GL11.glGetError(); -//$$ if (err != 0) { -//$$ GLError e = new GLError(err, cls, method); -//$$ if ("true".equals(System.getProperty("replaymod.glerrors.throw", "true"))) { -//$$ throw e; -//$$ } else { -//$$ e.printStackTrace(); -//$$ } -//$$ } -//$$ } -//$$ -//$$ public static class GLError extends RuntimeException { -//$$ private GLError(int err, String cls, String method) { -//$$ super(GLU.gluErrorString(err) + " (" + err + ")"); -//$$ StackTraceElement[] stack = getStackTrace(); -//$$ stack[0] = new StackTraceElement(cls, method, stack[0].getFileName(), stack[0].getLineNumber()); -//$$ setStackTrace(stack); -//$$ } -//$$ } -//$$ } -//#endif +// 1.7.10 and below diff --git a/src/main/java/com/replaymod/core/asm/GLStateTrackerTransformer.java b/src/main/java/com/replaymod/core/asm/GLStateTrackerTransformer.java index 26129095..743815c4 100644 --- a/src/main/java/com/replaymod/core/asm/GLStateTrackerTransformer.java +++ b/src/main/java/com/replaymod/core/asm/GLStateTrackerTransformer.java @@ -1,106 +1 @@ -//#if MC<=10710 -//$$ package com.replaymod.core.asm; -//$$ -//$$ import net.minecraft.launchwrapper.IClassTransformer; -//$$ import org.objectweb.asm.ClassReader; -//$$ import org.objectweb.asm.ClassVisitor; -//$$ import org.objectweb.asm.ClassWriter; -//$$ import org.objectweb.asm.MethodVisitor; -//$$ import org.objectweb.asm.Opcodes; -//$$ -//$$ import java.util.HashSet; -//$$ import java.util.Set; -//$$ -//$$ /** -//$$ * Redirect all calls to GL11.glEnable and GL11.glDisable to the GLStateTracker (excluding that class itself). -//$$ */ -//$$ public class GLStateTrackerTransformer implements IClassTransformer { -//$$ private static final String GL11_CLASS = "org.lwjgl.opengl.GL11"; -//$$ private static final String GL11 = GL11_CLASS.replace('.', '/'); -//$$ private static final String glEnable = "glEnable"; -//$$ private static final String glDisable = "glDisable"; -//$$ private static final String GLStateTracker_CLASS = "com.replaymod.render.hooks.GLStateTracker"; -//$$ private static final String GLStateTracker = GLStateTracker_CLASS.replace('.', '/'); -//$$ private static final String hook_glEnable = "hook_glEnable"; -//$$ private static final String hook_glDisable = "hook_glDisable"; -//$$ -//$$ @Override -//$$ public byte[] transform(String name, String transformedName, byte[] basicClass) { -//$$ if (basicClass == null) return null; -//$$ // Ignore the state tracker itself -//$$ if (name.equals(GLStateTracker_CLASS)) return basicClass; -//$$ -//$$ ClassReader reader = new ClassReader(basicClass); -//$$ Set eligibleMethods = findEligibleMethods(reader); -//$$ if (eligibleMethods.isEmpty()) { -//$$ return basicClass; -//$$ } -//$$ -//$$ ClassWriter writer = new ClassWriter(reader, 0); -//$$ reader.accept(new ClassVisitor(Opcodes.ASM5, writer) { -//$$ @Override -//$$ public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { -//$$ MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); -//$$ if (!eligibleMethods.contains(new Method(name, desc))) { -//$$ return mv; -//$$ } -//$$ return new MethodVisitor(Opcodes.ASM5, mv) { -//$$ @Override -//$$ public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { -//$$ if (owner.equals(GL11)) { -//$$ if (name.equals(glEnable)) { -//$$ owner = GLStateTracker; -//$$ name = hook_glEnable; -//$$ } else if (name.equals(glDisable)) { -//$$ owner = GLStateTracker; -//$$ name = hook_glDisable; -//$$ } -//$$ } -//$$ super.visitMethodInsn(opcode, owner, name, desc, itf); -//$$ } -//$$ }; -//$$ } -//$$ }, 0); -//$$ return writer.toByteArray(); -//$$ } -//$$ -//$$ private Set findEligibleMethods(ClassReader reader) { -//$$ Set eligibleMethods = new HashSet<>(); -//$$ reader.accept(new ClassVisitor(Opcodes.ASM5) { -//$$ @Override -//$$ public MethodVisitor visitMethod(int access, String methodName, String methodDesc, String signature, String[] exceptions) { -//$$ return new MethodVisitor(Opcodes.ASM5) { -//$$ @Override -//$$ public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { -//$$ if (owner.equals(GL11) && (name.equals(glEnable) || name.equals(glDisable))) { -//$$ eligibleMethods.add(new Method(methodName, methodDesc)); -//$$ } -//$$ } -//$$ }; -//$$ } -//$$ }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); -//$$ return eligibleMethods; -//$$ } -//$$ -//$$ private static class Method { -//$$ private final String name, desc; -//$$ -//$$ private Method(String name, String desc) { -//$$ this.name = name; -//$$ this.desc = desc; -//$$ } -//$$ -//$$ @Override -//$$ public boolean equals(Object o) { -//$$ if (o == null || getClass() != o.getClass()) return false; -//$$ Method method = (Method) o; -//$$ return name.equals(method.name) && desc.equals(method.desc); -//$$ } -//$$ -//$$ @Override -//$$ public int hashCode() { -//$$ return name.hashCode() ^ desc.hashCode(); -//$$ } -//$$ } -//$$ } -//#endif +// 1.7.10 and below diff --git a/src/main/java/com/replaymod/core/events/PostRenderCallback.java b/src/main/java/com/replaymod/core/events/PostRenderCallback.java index c508b949..a72f0d6c 100644 --- a/src/main/java/com/replaymod/core/events/PostRenderCallback.java +++ b/src/main/java/com/replaymod/core/events/PostRenderCallback.java @@ -1,4 +1,3 @@ -//#if MC>=11400 package com.replaymod.core.events; import de.johni0702.minecraft.gui.utils.Event; @@ -14,4 +13,3 @@ public interface PostRenderCallback { void postRender(); } -//#endif diff --git a/src/main/java/com/replaymod/core/events/PostRenderWorldCallback.java b/src/main/java/com/replaymod/core/events/PostRenderWorldCallback.java index 9e8cb335..1445ae43 100644 --- a/src/main/java/com/replaymod/core/events/PostRenderWorldCallback.java +++ b/src/main/java/com/replaymod/core/events/PostRenderWorldCallback.java @@ -1,33 +1,16 @@ -//#if MC>=11400 package com.replaymod.core.events; import de.johni0702.minecraft.gui.utils.Event; - -//#if MC>=11500 import net.minecraft.client.util.math.MatrixStack; -//#endif public interface PostRenderWorldCallback { Event EVENT = Event.create((listeners) -> - //#if MC>=11500 (MatrixStack matrixStack) -> { - //#else - //$$ () -> { - //#endif for (PostRenderWorldCallback listener : listeners) { - listener.postRenderWorld( - //#if MC>=11500 - matrixStack - //#endif - ); + listener.postRenderWorld(matrixStack); } } ); - void postRenderWorld( - //#if MC>=11500 - MatrixStack matrixStack - //#endif - ); + void postRenderWorld(MatrixStack matrixStack); } -//#endif diff --git a/src/main/java/com/replaymod/core/events/PreRenderCallback.java b/src/main/java/com/replaymod/core/events/PreRenderCallback.java index 65afa3e3..907ed0cb 100644 --- a/src/main/java/com/replaymod/core/events/PreRenderCallback.java +++ b/src/main/java/com/replaymod/core/events/PreRenderCallback.java @@ -1,4 +1,3 @@ -//#if MC>=11400 package com.replaymod.core.events; import de.johni0702.minecraft.gui.utils.Event; @@ -14,4 +13,3 @@ public interface PreRenderCallback { void preRender(); } -//#endif diff --git a/src/main/java/com/replaymod/core/events/PreRenderHandCallback.java b/src/main/java/com/replaymod/core/events/PreRenderHandCallback.java index d5d8d268..ea231cf6 100644 --- a/src/main/java/com/replaymod/core/events/PreRenderHandCallback.java +++ b/src/main/java/com/replaymod/core/events/PreRenderHandCallback.java @@ -1,4 +1,3 @@ -//#if MC>=11400 package com.replaymod.core.events; import de.johni0702.minecraft.gui.utils.Event; @@ -17,4 +16,3 @@ public interface PreRenderHandCallback { boolean preRenderHand(); } -//#endif diff --git a/src/main/java/com/replaymod/core/gui/GuiBackgroundProcesses.java b/src/main/java/com/replaymod/core/gui/GuiBackgroundProcesses.java index 39b71973..27f6834d 100644 --- a/src/main/java/com/replaymod/core/gui/GuiBackgroundProcesses.java +++ b/src/main/java/com/replaymod/core/gui/GuiBackgroundProcesses.java @@ -12,30 +12,15 @@ import de.johni0702.minecraft.gui.layout.VerticalLayout; import de.johni0702.minecraft.gui.utils.EventRegistrations; import de.johni0702.minecraft.gui.utils.lwjgl.Dimension; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; - -//#if FABRIC>=1 import de.johni0702.minecraft.gui.versions.callbacks.InitScreenCallback; -import net.minecraft.client.gui.screen.Screen; -//#else -//$$ import net.minecraftforge.client.event.GuiScreenEvent; -//$$ import net.minecraftforge.common.MinecraftForge; -//$$ import net.minecraftforge.eventbus.api.SubscribeEvent; -//$$ import static com.replaymod.core.versions.MCVer.getGui; -//#endif import static com.replaymod.core.versions.MCVer.getMinecraft; public class GuiBackgroundProcesses extends EventRegistrations { private GuiPanel panel = new GuiPanel().setLayout(new VerticalLayout().setSpacing(10)); - //#if FABRIC>=1 { on(InitScreenCallback.EVENT, (screen, buttons) -> onGuiInit(screen)); } - private void onGuiInit(Screen guiScreen) { - //#else - //$$ @SubscribeEvent - //$$ public void onGuiInit(GuiScreenEvent.InitGuiEvent.Post event) { - //$$ net.minecraft.client.gui.screen.Screen guiScreen = getGui(event); - //#endif + private void onGuiInit(net.minecraft.client.gui.screen.Screen guiScreen) { if (guiScreen != getMinecraft().currentScreen) return; // people tend to construct GuiScreens without opening them VanillaGuiScreen vanillaGui = VanillaGuiScreen.wrap(guiScreen); diff --git a/src/main/java/com/replaymod/core/gui/GuiFactory.java b/src/main/java/com/replaymod/core/gui/GuiFactory.java index 31ff16a9..fca4cd6c 100644 --- a/src/main/java/com/replaymod/core/gui/GuiFactory.java +++ b/src/main/java/com/replaymod/core/gui/GuiFactory.java @@ -1,63 +1 @@ -//#if MC<11400 -//$$ package com.replaymod.core.gui; -//$$ -//$$ import com.replaymod.core.ReplayMod; -//$$ import net.minecraft.client.Minecraft; -//$$ import net.minecraft.client.gui.GuiScreen; -//$$ -//#if MC>=10800 -//$$ import net.minecraftforge.fml.client.IModGuiFactory; -//#else -//$$ import cpw.mods.fml.client.IModGuiFactory; -//#endif -//$$ -//$$ import java.util.Set; -//$$ -//$$ @SuppressWarnings("unused") -//$$ public class GuiFactory implements IModGuiFactory { -//$$ @Override -//$$ public void initialize(Minecraft minecraftInstance) { -//$$ -//$$ } -//$$ - //#if MC>=11200 - //$$ @Override - //$$ public boolean hasConfigGui() { - //$$ return true; - //$$ } - //$$ - //$$ @Override - //$$ public GuiScreen createConfigGui(GuiScreen parentScreen) { - //$$ return new GuiReplaySettings(parentScreen, ReplayMod.instance.getSettingsRegistry()).toMinecraft(); - //$$ } - //#else - //$$ @Override - //$$ public Class mainConfigGuiClass() { - //$$ return ConfigGuiWrapper.class; - //$$ } - //$$ - //$$ @Override - //$$ public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) { - //$$ return null; - //$$ } - //$$ - //$$ public static class ConfigGuiWrapper extends GuiScreen { - //$$ private final GuiScreen parent; - //$$ - //$$ public ConfigGuiWrapper(GuiScreen parent) { - //$$ this.parent = parent; - //$$ } - //$$ - //$$ @Override - //$$ public void initGui() { - //$$ new GuiReplaySettings(parent, ReplayMod.instance.getSettingsRegistry()).display(); - //$$ } - //$$ } - //#endif -//$$ -//$$ @Override -//$$ public Set runtimeGuiCategories() { -//$$ return null; -//$$ } -//$$ } -//#endif +// 1.12.2 and below diff --git a/src/main/java/com/replaymod/core/gui/GuiReplayButton.java b/src/main/java/com/replaymod/core/gui/GuiReplayButton.java index 9bd3476f..2ee4d9e4 100644 --- a/src/main/java/com/replaymod/core/gui/GuiReplayButton.java +++ b/src/main/java/com/replaymod/core/gui/GuiReplayButton.java @@ -1,17 +1,19 @@ package com.replaymod.core.gui; -import com.replaymod.core.ReplayMod; import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.RenderInfo; import de.johni0702.minecraft.gui.element.GuiButton; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; +import net.minecraft.util.Identifier; public class GuiReplayButton extends GuiButton { + public static final Identifier ICON = new Identifier("replaymod", "logo_button.png"); + @Override public void draw(GuiRenderer renderer, ReadableDimension size, RenderInfo renderInfo) { super.draw(renderer, size, renderInfo); - renderer.bindTexture(ReplayMod.LOGO_FAVICON); + renderer.bindTexture(ICON); renderer.drawTexturedRect(3, 3, 0, 0, size.getWidth() - 6, size.getHeight() - 6, 1, 1, 1, 1); } } diff --git a/src/main/java/com/replaymod/core/gui/ModMenuApiImpl.java b/src/main/java/com/replaymod/core/gui/ModMenuApiImpl.java index 666a3174..46f75da0 100644 --- a/src/main/java/com/replaymod/core/gui/ModMenuApiImpl.java +++ b/src/main/java/com/replaymod/core/gui/ModMenuApiImpl.java @@ -2,19 +2,31 @@ package com.replaymod.core.gui; import com.replaymod.core.ReplayMod; -import io.github.prospector.modmenu.api.ModMenuApi; import net.minecraft.client.gui.screen.Screen; import java.util.function.Function; +//#if MC>=11700 +//$$ import com.terraformersmc.modmenu.api.ConfigScreenFactory; +//$$ import com.terraformersmc.modmenu.api.ModMenuApi; +//#else +import io.github.prospector.modmenu.api.ModMenuApi; +//#endif + public class ModMenuApiImpl implements ModMenuApi { + //#if MC<11700 @Override public String getModId() { return ReplayMod.MOD_ID; } + //#endif @Override + //#if MC>=11700 + //$$ public ConfigScreenFactory getModConfigScreenFactory() { + //#else public Function getConfigScreenFactory() { + //#endif return parent -> new GuiReplaySettings(parent, ReplayMod.instance.getSettingsRegistry()).toMinecraft(); } } diff --git a/src/main/java/com/replaymod/core/mixin/GuiScreenAccessor.java b/src/main/java/com/replaymod/core/mixin/GuiScreenAccessor.java index d55529b2..9bdc5323 100644 --- a/src/main/java/com/replaymod/core/mixin/GuiScreenAccessor.java +++ b/src/main/java/com/replaymod/core/mixin/GuiScreenAccessor.java @@ -2,32 +2,33 @@ package com.replaymod.core.mixin; import net.minecraft.client.gui.screen.Screen; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.gen.Accessor; -import java.util.List; - -//#if MC>=11400 -import net.minecraft.client.gui.widget.AbstractButtonWidget; +//#if MC>=11700 +//$$ import net.minecraft.client.gui.Drawable; +//$$ import net.minecraft.client.gui.Element; +//$$ import net.minecraft.client.gui.Selectable; //#else -//$$ import net.minecraft.client.gui.GuiButton; //#endif //#if MC>=11400 -import net.minecraft.client.gui.Element; +import net.minecraft.client.gui.widget.AbstractButtonWidget; +import org.spongepowered.asm.mixin.gen.Invoker; +//#else +//$$ import net.minecraft.client.gui.GuiButton; +//$$ import org.spongepowered.asm.mixin.gen.Accessor; +//$$ import java.util.List; //#endif @Mixin(Screen.class) public interface GuiScreenAccessor { - //#if MC>=11400 - @Accessor - List getButtons(); + //#if MC>=11700 + //$$ @Invoker("addDrawableChild") + //$$ T invokeAddButton(T drawableElement); + //#elseif MC>=11400 + @Invoker + T invokeAddButton(T button); //#else //$$ @Accessor("buttonList") //$$ List getButtons(); //#endif - - //#if MC>=11400 - @Accessor - List getChildren(); - //#endif } diff --git a/src/main/java/com/replaymod/core/mixin/MinecraftAccessor.java b/src/main/java/com/replaymod/core/mixin/MinecraftAccessor.java index 1ec58296..8f26e4f5 100644 --- a/src/main/java/com/replaymod/core/mixin/MinecraftAccessor.java +++ b/src/main/java/com/replaymod/core/mixin/MinecraftAccessor.java @@ -1,9 +1,11 @@ package com.replaymod.core.mixin; import net.minecraft.client.MinecraftClient; +import net.minecraft.network.ClientConnection; import net.minecraft.util.crash.CrashReport; import net.minecraft.client.render.RenderTickCounter; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.gen.Accessor; import java.util.Queue; @@ -26,6 +28,9 @@ public interface MinecraftAccessor { @Accessor("renderTickCounter") RenderTickCounter getTimer(); @Accessor("renderTickCounter") + //#if MC>=11200 + @Mutable + //#endif void setTimer(RenderTickCounter value); //#if MC>=11400 @@ -50,4 +55,9 @@ public interface MinecraftAccessor { //$$ @Accessor //$$ List getDefaultResourcePacks(); //#endif + + //#if MC>=11400 + @Accessor + void setConnection(ClientConnection connection); + //#endif } diff --git a/src/main/java/com/replaymod/core/mixin/MixinGameRenderer.java b/src/main/java/com/replaymod/core/mixin/MixinGameRenderer.java index aefceb3b..a7c817a9 100644 --- a/src/main/java/com/replaymod/core/mixin/MixinGameRenderer.java +++ b/src/main/java/com/replaymod/core/mixin/MixinGameRenderer.java @@ -5,15 +5,12 @@ import com.replaymod.core.events.PostRenderWorldCallback; import com.replaymod.core.events.PreRenderHandCallback; import net.minecraft.client.render.Camera; import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.util.math.MatrixStack; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -//#if MC>=11500 -import net.minecraft.client.util.math.MatrixStack; -//#endif - @Mixin(GameRenderer.class) public class MixinGameRenderer { @Inject( @@ -30,11 +27,10 @@ public class MixinGameRenderer { MatrixStack matrixStack, //#endif CallbackInfo ci) { - PostRenderWorldCallback.EVENT.invoker().postRenderWorld( - //#if MC>=11500 - matrixStack - //#endif - ); + //#if MC<11500 + //$$ MatrixStack matrixStack = new MatrixStack(); + //#endif + PostRenderWorldCallback.EVENT.invoker().postRenderWorld(matrixStack); } @Inject(method = "renderHand", at = @At("HEAD"), cancellable = true) diff --git a/src/main/java/com/replaymod/core/mixin/MixinKeyboardListener.java b/src/main/java/com/replaymod/core/mixin/MixinKeyboardListener.java index 3a7c680c..3c218b89 100644 --- a/src/main/java/com/replaymod/core/mixin/MixinKeyboardListener.java +++ b/src/main/java/com/replaymod/core/mixin/MixinKeyboardListener.java @@ -2,19 +2,12 @@ package com.replaymod.core.mixin; import com.replaymod.core.events.KeyBindingEventCallback; import com.replaymod.core.events.KeyEventCallback; +import net.minecraft.client.Keyboard; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -//#if MC>=11400 -import net.minecraft.client.Keyboard; -//#else -//$$ import net.minecraft.client.settings.KeyBinding; -//$$ import org.lwjgl.input.Keyboard; -//#endif - -//#if MC>=11400 @Mixin(Keyboard.class) public class MixinKeyboardListener { private static final String ON_KEY_PRESSED = "Lnet/minecraft/client/options/KeyBinding;onKeyPressed(Lnet/minecraft/client/util/InputUtil$Key;)V"; @@ -31,21 +24,3 @@ public class MixinKeyboardListener { KeyBindingEventCallback.EVENT.invoker().onKeybindingEvent(); } } -//#else -//$$ @Mixin(KeyBinding.class) -//$$ public class MixinKeyboardListener { -//$$ @Inject(method = "onTick", at = @At("HEAD"), cancellable = true) -//$$ private static void beforeKeyBindingTick(CallbackInfo ci) { -//$$ int keyCode = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() + 256 : Keyboard.getEventKey(); -//$$ int action = Keyboard.getEventKeyState() ? KeyEventCallback.ACTION_PRESS : KeyEventCallback.ACTION_RELEASE; -//$$ if (KeyEventCallback.EVENT.invoker().onKeyEvent(keyCode, 0, action, 0)) { -//$$ ci.cancel(); -//$$ } -//$$ } -//$$ -//$$ @Inject(method = "onTick", at = @At("RETURN")) -//$$ private static void afterKeyBindingTick(CallbackInfo ci) { -//$$ KeyBindingEventCallback.EVENT.invoker().onKeybindingEvent(); -//$$ } -//$$ } -//#endif diff --git a/src/main/java/com/replaymod/core/mixin/Mixin_ContextualKeyBindings.java b/src/main/java/com/replaymod/core/mixin/Mixin_ContextualKeyBindings.java index ad7b8093..6b246c3f 100644 --- a/src/main/java/com/replaymod/core/mixin/Mixin_ContextualKeyBindings.java +++ b/src/main/java/com/replaymod/core/mixin/Mixin_ContextualKeyBindings.java @@ -1,4 +1,3 @@ -//#if MC>=11400 package com.replaymod.core.mixin; import com.replaymod.core.ReplayMod; @@ -13,6 +12,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import java.util.ArrayList; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Set; @@ -24,16 +24,26 @@ import java.util.Set; */ @Mixin(KeyBinding.class) public class Mixin_ContextualKeyBindings { + //#if MC>=11200 @Shadow @Final private static Map keysById; + @Unique private static Collection keyBindings() { return Mixin_ContextualKeyBindings.keysById.values(); } + //#else + //$$ @Shadow @Final private static List KEYBIND_ARRAY; + //$$ @Unique private static Collection keyBindings() { return Mixin_ContextualKeyBindings.KEYBIND_ARRAY; } + //#endif @Unique private static final List temporarilyRemoved = new ArrayList<>(); @Inject(method = "updateKeysByCode", at = @At("HEAD")) private static void preContextualKeyBindings(CallbackInfo ci) { - Set onlyInReplay = ReplayMod.instance.getKeyBindingRegistry().getOnlyInReplay(); + ReplayMod mod = ReplayMod.instance; + if (mod == null) { + return; + } + Set onlyInReplay = mod.getKeyBindingRegistry().getOnlyInReplay(); if (ReplayModReplay.instance.getReplayHandler() != null) { // In replay, remove any conflicting key bindings, so that ours are guaranteed in - Mixin_ContextualKeyBindings.keysById.values().removeIf(keyBinding -> { + keyBindings().removeIf(keyBinding -> { for (KeyBinding exclusiveBinding : onlyInReplay) { if (keyBinding.equals(exclusiveBinding) && keyBinding != exclusiveBinding) { temporarilyRemoved.add(keyBinding); @@ -44,20 +54,25 @@ public class Mixin_ContextualKeyBindings { }); } else { // Not in a replay, remove all replay-exclusive keybindings - for (KeyBinding keyBinding : onlyInReplay) { - if (Mixin_ContextualKeyBindings.keysById.remove(keyBinding.getTranslationKey()) != null) { + keyBindings().removeIf(keyBinding -> { + if (onlyInReplay.contains(keyBinding)) { temporarilyRemoved.add(keyBinding); + return true; } - } + return false; + }); } } @Inject(method = "updateKeysByCode", at = @At("RETURN")) private static void postContextualKeyBindings(CallbackInfo ci) { for (KeyBinding keyBinding : temporarilyRemoved) { + //#if MC>=11200 Mixin_ContextualKeyBindings.keysById.put(keyBinding.getTranslationKey(), keyBinding); + //#else + //$$ keyBindings().add(keyBinding); + //#endif } temporarilyRemoved.clear(); } } -//#endif diff --git a/src/main/java/com/replaymod/core/mixin/ResourcePackRepositoryAccessor.java b/src/main/java/com/replaymod/core/mixin/ResourcePackRepositoryAccessor.java index 81a00bcf..743815c4 100644 --- a/src/main/java/com/replaymod/core/mixin/ResourcePackRepositoryAccessor.java +++ b/src/main/java/com/replaymod/core/mixin/ResourcePackRepositoryAccessor.java @@ -1,22 +1 @@ -//#if MC<10800 -//$$ package com.replaymod.core.mixin; -//$$ -//$$ import net.minecraft.client.resources.IResourcePack; -//$$ import net.minecraft.client.resources.ResourcePackRepository; -//$$ import org.spongepowered.asm.mixin.Mixin; -//$$ import org.spongepowered.asm.mixin.gen.Accessor; -//$$ -//$$ import java.io.File; -//$$ -//$$ @Mixin(ResourcePackRepository.class) -//$$ public interface ResourcePackRepositoryAccessor { -//$$ @Accessor("field_148533_g") -//$$ boolean isActive(); -//$$ @Accessor("field_148533_g") -//$$ void setActive(boolean value); -//$$ @Accessor("field_148532_f") -//$$ void setPack(IResourcePack value); -//$$ @Accessor("field_148534_e") -//$$ File getCacheDir(); -//$$ } -//#endif +// 1.7.10 and below diff --git a/src/main/java/com/replaymod/core/mixin/TimerAccessor.java b/src/main/java/com/replaymod/core/mixin/TimerAccessor.java index 0f10f261..78f7da1a 100644 --- a/src/main/java/com/replaymod/core/mixin/TimerAccessor.java +++ b/src/main/java/com/replaymod/core/mixin/TimerAccessor.java @@ -2,6 +2,7 @@ package com.replaymod.core.mixin; import net.minecraft.client.render.RenderTickCounter; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(RenderTickCounter.class) @@ -15,6 +16,7 @@ public interface TimerAccessor { @Accessor("tickTime") float getTickLength(); @Accessor("tickTime") + @Mutable void setTickLength(float value); //#else //$$ @Accessor diff --git a/src/main/java/com/replaymod/core/tweaker/ReplayModTweaker.java b/src/main/java/com/replaymod/core/tweaker/ReplayModTweaker.java index 403ead3d..c690defc 100644 --- a/src/main/java/com/replaymod/core/tweaker/ReplayModTweaker.java +++ b/src/main/java/com/replaymod/core/tweaker/ReplayModTweaker.java @@ -1,127 +1 @@ -//#if FABRIC>=1 -// Fabric equivalent is in ReplayModMMLauncher -//#else -//#if MC>=11400 -//$$ // Generally not supported by RM -//#else -//$$ package com.replaymod.core.tweaker; -//$$ -//$$ import com.replaymod.extras.modcore.ModCoreInstaller; -//$$ import net.minecraft.launchwrapper.ITweaker; -//$$ import net.minecraft.launchwrapper.Launch; -//$$ import net.minecraft.launchwrapper.LaunchClassLoader; -//$$ import org.spongepowered.asm.launch.GlobalProperties; -//$$ import org.spongepowered.asm.launch.MixinBootstrap; -//$$ import org.spongepowered.asm.launch.platform.MixinPlatformManager; -//$$ -//$$ import java.io.File; -//$$ import java.net.URI; -//$$ import java.net.URISyntaxException; -//$$ import java.util.List; -//$$ -//#if MC>=11200 -//$$ import org.spongepowered.asm.launch.platform.container.ContainerHandleURI; -//#endif -//$$ -//$$ public class ReplayModTweaker implements ITweaker { -//$$ -//$$ private static final String MIXIN_TWEAKER = "org.spongepowered.asm.launch.MixinTweaker"; -//$$ -//$$ public ReplayModTweaker() { -//$$ injectMixinTweaker(); -//$$ } -//$$ -//$$ private void injectMixinTweaker() { -//$$ @SuppressWarnings("unchecked") -//$$ List tweakClasses = (List) Launch.blackboard.get("TweakClasses"); -//$$ -//$$ // If the MixinTweaker is already queued (because of another mod), then there's nothing we need to to -//$$ if (tweakClasses.contains(MIXIN_TWEAKER)) { -//$$ return; -//$$ } -//$$ -//$$ // If it is already booted, we're also good to go -//$$ if (GlobalProperties.get(GlobalProperties.Keys.INIT) != null) { -//$$ return; -//$$ } -//$$ -//$$ System.out.println("Injecting MixinTweaker from ReplayModTweaker"); -//$$ -//$$ // Otherwise, we need to take things into our own hands because the normal way to chainload a tweaker -//$$ // (by adding it to the TweakClasses list during injectIntoClassLoader) is too late for Mixin. -//$$ // Instead we instantiate the MixinTweaker on our own and add it to the current Tweaks list immediately. -//$$ Launch.classLoader.addClassLoaderExclusion(MIXIN_TWEAKER.substring(0, MIXIN_TWEAKER.lastIndexOf('.'))); -//$$ @SuppressWarnings("unchecked") -//$$ List tweaks = (List) Launch.blackboard.get("Tweaks"); -//$$ try { -//$$ tweaks.add((ITweaker) Class.forName(MIXIN_TWEAKER, true, Launch.classLoader).newInstance()); -//$$ } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { -//$$ throw new RuntimeException(e); -//$$ } -//$$ } -//$$ -//$$ @Override -//$$ public void acceptOptions(List args, File gameDir, File assetsDir, String profile) { -//$$ } -//$$ -//$$ @Override -//$$ public void injectIntoClassLoader(LaunchClassLoader classLoader) { -//$$ // Add our jar as a Mixin container (cause normally Mixin detects those via the TweakClass manifest entry) -//$$ URI uri; -//$$ try { -//$$ uri = this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI(); -//$$ } catch (URISyntaxException e) { -//$$ throw new RuntimeException(e); -//$$ } -//$$ MixinPlatformManager platform = MixinBootstrap.getPlatform(); - //#if MC>=11200 - //$$ platform.addContainer(new ContainerHandleURI(uri)); - //#else - //$$ platform.addContainer(uri); - //#endif -//$$ - //#if MC>=11202 && MC<=11202 - //$$ initModCore("1.12.2"); - //#endif - //#if MC>=10809 && MC<=10809 - //$$ initModCore("1.8.9"); - //#endif -//$$ } -//$$ -//$$ @Override -//$$ public String getLaunchTarget() { -//$$ return null; -//$$ } -//$$ -//$$ @Override -//$$ public String[] getLaunchArguments() { -//$$ return new String[0]; -//$$ } -//$$ -//$$ private void initModCore(String mcVer) { -//$$ try { -//$$ if (System.getProperty("REPLAYMOD_SKIP_MODCORE", "false").equalsIgnoreCase("true")) { -//$$ System.out.println("ReplayMod not initializing ModCore because REPLAYMOD_SKIP_MODCORE is true."); -//$$ return; -//$$ } -//$$ -//$$ if ((Boolean) Launch.blackboard.get("fml.deobfuscatedEnvironment")) { -//$$ System.out.println("ReplayMod not initializing ModCore because we're in a development environment."); -//$$ return; -//$$ } -//$$ -//$$ int result = ModCoreInstaller.initialize(Launch.minecraftHome, mcVer + "_forge"); -//$$ if (result != -2) { // Don't even bother logging the result if there's no ModCore for this version. -//$$ System.out.println("ReplayMod ModCore init result: " + result); -//$$ } -//$$ if (ModCoreInstaller.isErrored()) { -//$$ System.err.println(ModCoreInstaller.getError()); -//$$ } -//$$ } catch (Throwable t) { -//$$ System.err.println("ReplayMod caught error during ModCore init:"); -//$$ t.printStackTrace(); -//$$ } -//$$ } -//$$ } -//#endif -//#endif +// Forge only diff --git a/src/main/java/com/replaymod/core/utils/FileTypeAdapter.java b/src/main/java/com/replaymod/core/utils/FileTypeAdapter.java new file mode 100644 index 00000000..78d76f90 --- /dev/null +++ b/src/main/java/com/replaymod/core/utils/FileTypeAdapter.java @@ -0,0 +1,30 @@ +package com.replaymod.core.utils; + +import com.google.gson.TypeAdapter; +import com.google.gson.stream.JsonReader; +import com.google.gson.stream.JsonToken; +import com.google.gson.stream.JsonWriter; + +import java.io.File; +import java.io.IOException; + +public class FileTypeAdapter extends TypeAdapter { + @Override + public void write(JsonWriter out, File value) throws IOException { + out.value(value.getPath()); + } + + @Override + public File read(JsonReader in) throws IOException { + String path; + if (in.peek() == JsonToken.BEGIN_OBJECT) { + in.beginObject(); + in.nextName(); + path = in.nextString(); + in.endObject(); + } else { + path = in.nextString(); + } + return new File(path); + } +} diff --git a/src/main/java/com/replaymod/core/utils/ModCompat.java b/src/main/java/com/replaymod/core/utils/ModCompat.java index 453011bd..44a6173b 100644 --- a/src/main/java/com/replaymod/core/utils/ModCompat.java +++ b/src/main/java/com/replaymod/core/utils/ModCompat.java @@ -1,89 +1,12 @@ package com.replaymod.core.utils; import com.replaymod.replaystudio.data.ModInfo; -import net.minecraft.util.Identifier; - -//#if FABRIC>=1 -import net.fabricmc.loader.api.FabricLoader; -import net.fabricmc.loader.api.ModContainer; -import net.minecraft.util.registry.Registry; -//#else -//#if MC>=11200 -//$$ import net.minecraftforge.registries.ForgeRegistry; -//$$ import net.minecraftforge.registries.RegistryManager; -//#else -//$$ import net.minecraftforge.fml.common.registry.GameData; -//$$ import java.util.stream.Stream; -//#endif -//$$ -//#if MC>=11400 -//$$ import net.minecraftforge.fml.ModList; -//#else -//$$ import net.minecraftforge.fml.common.Loader; -//$$ import net.minecraftforge.fml.common.ModContainer; -//#endif -//#endif import java.util.*; -import java.util.function.Function; -import java.util.stream.Collectors; public class ModCompat { - @SuppressWarnings("unchecked") public static Collection getInstalledNetworkMods() { - //#if MC>=11400 - //#if FABRIC>=1 - Map modInfoMap = FabricLoader.getInstance().getAllMods().stream() - .map(ModContainer::getMetadata) - .map(m -> new ModInfo(m.getId(), m.getName(), m.getVersion().toString())) - .collect(Collectors.toMap(ModInfo::getId, Function.identity())); - return Registry.REGISTRIES.stream() - .map(Registry::getIds).flatMap(Set::stream) - //#else - //$$ Map modInfoMap = ModList.get().getMods().stream() - //$$ .map(m -> new ModInfo(m.getModId(), m.getDisplayName(), m.getVersion().toString())) - //$$ .collect(Collectors.toMap(ModInfo::getId, Function.identity())); - //$$ return RegistryManager.ACTIVE.takeSnapshot(false).keySet().stream() - //$$ .map(RegistryManager.ACTIVE::getRegistry) - //$$ .map(ForgeRegistry::getKeys).flatMap(Set::stream) - //#endif - .map(Identifier::getNamespace).filter(s -> !s.equals("minecraft")).distinct() - .map(modInfoMap::get).filter(Objects::nonNull) - .collect(Collectors.toList()); - //#else - //$$ Map ignoreCaseMap = Loader.instance().getModList().stream() - //$$ .collect(Collectors.toMap(m -> m.getModId().toLowerCase(), Function.identity())); - //#if MC>=11200 - //$$ return RegistryManager.ACTIVE.takeSnapshot(false).keySet().stream() - //$$ .map(RegistryManager.ACTIVE::getRegistry) - //$$ .map(ForgeRegistry::getKeys).flatMap(Set::stream) - //$$ .map(ResourceLocation::getResourceDomain).filter(s -> !s.equals("minecraft")).distinct() - //$$ .map(String::toLowerCase).map(ignoreCaseMap::get).filter(mod -> mod != null) - //$$ .map(mod -> new ModInfo(mod.getModId(), mod.getName(), mod.getVersion())) - //$$ .collect(Collectors.toList()); - //#else - //#if MC>=10800 - //$$ return Stream.concat( - //$$ ((Set) GameData.getBlockRegistry().getKeys()).stream(), - //$$ ((Set) GameData.getItemRegistry().getKeys()).stream() - //$$ ).map(ResourceLocation::getResourceDomain).filter(s -> !s.equals("minecraft")).distinct() - //$$ .map(String::toLowerCase).map(ignoreCaseMap::get).filter(mod -> mod != null) - //$$ .map(mod -> new ModInfo(mod.getModId(), mod.getName(), mod.getVersion())) - //$$ .collect(Collectors.toList()); - //#else - //$$ return Stream.concat( - //$$ ((Set) GameData.getBlockRegistry().getKeys()).stream(), - //$$ ((Set) GameData.getItemRegistry().getKeys()).stream() - //$$ ).map(name -> { - //$$ if (!name.contains(":")) return null; // Still using old names without namespace, can't do anything, ignore - //$$ return name.split(":", 2)[0]; - //$$ }).filter(s -> !s.equals("minecraft")).distinct() - //$$ .map(String::toLowerCase).map(ignoreCaseMap::get).filter(mod -> mod != null) - //$$ .map(mod -> new ModInfo(mod.getModId(), mod.getName(), mod.getVersion())) - //$$ .collect(Collectors.toList()); - //#endif - //#endif - //#endif + return ModInfoGetter.getInstalledNetworkMods(); } public static final class ModInfoDifference { diff --git a/src/main/java/com/replaymod/core/utils/ModInfoGetter.java b/src/main/java/com/replaymod/core/utils/ModInfoGetter.java new file mode 100644 index 00000000..d4bbe156 --- /dev/null +++ b/src/main/java/com/replaymod/core/utils/ModInfoGetter.java @@ -0,0 +1,25 @@ +package com.replaymod.core.utils; +import com.replaymod.replaystudio.data.ModInfo; +import net.minecraft.util.Identifier; + +import net.fabricmc.loader.api.FabricLoader; +import net.fabricmc.loader.api.ModContainer; +import net.minecraft.util.registry.Registry; + +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +class ModInfoGetter { + static Collection getInstalledNetworkMods() { + Map modInfoMap = FabricLoader.getInstance().getAllMods().stream() + .map(ModContainer::getMetadata) + .map(m -> new ModInfo(m.getId(), m.getName(), m.getVersion().toString())) + .collect(Collectors.toMap(ModInfo::getId, Function.identity())); + return Registry.REGISTRIES.stream() + .map(Registry::getIds).flatMap(Set::stream) + .map(Identifier::getNamespace).filter(s -> !s.equals("minecraft")).distinct() + .map(modInfoMap::get).filter(Objects::nonNull) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/replaymod/core/utils/Restrictions.java b/src/main/java/com/replaymod/core/utils/Restrictions.java index 02c3eb3e..359eaf77 100644 --- a/src/main/java/com/replaymod/core/utils/Restrictions.java +++ b/src/main/java/com/replaymod/core/utils/Restrictions.java @@ -10,8 +10,6 @@ import net.minecraft.util.Identifier; //$$ import io.netty.buffer.Unpooled; //#endif -import static com.replaymod.core.versions.MCVer.readString; - /** * Restrictions set by the server, * @see Replay Restrictions Gist @@ -34,7 +32,7 @@ public class Restrictions { //$$ PacketBuffer buffer = new PacketBuffer(Unpooled.wrappedBuffer(packet.func_149168_d())); //#endif while (buffer.isReadable()) { - String name = readString(buffer, 64); + String name = buffer.readString(64); boolean active = buffer.readBoolean(); // if ("no_xray".equals(name)) { // noXray = active; diff --git a/src/main/java/com/replaymod/core/utils/Result.java b/src/main/java/com/replaymod/core/utils/Result.java new file mode 100644 index 00000000..d2c70c2f --- /dev/null +++ b/src/main/java/com/replaymod/core/utils/Result.java @@ -0,0 +1,142 @@ +package com.replaymod.core.utils; + +import java.util.function.Consumer; +import java.util.function.Function; + +public abstract class Result { + + public static OkImpl ok(Ok value) { + return new OkImpl<>(value); + } + + public static ErrImpl err(Err value) { + return new ErrImpl<>(value); + } + + public abstract boolean isOk(); + public abstract boolean isErr(); + public abstract Ok okOrNull(); + public abstract Err errOrNull(); + public abstract void ifOk(Consumer consumer); + public abstract void ifErr(Consumer consumer); + public abstract Ok okOrElse(Function orElse); + public abstract Err errOrElse(Function orElse); + public abstract Result mapOk(Function func); + public abstract Result mapErr(Function func); + + private static class OkImpl extends Result { + private final Ok value; + + public OkImpl(Ok value) { + this.value = value; + } + + @Override + public boolean isOk() { + return true; + } + + @Override + public boolean isErr() { + return false; + } + + @Override + public Ok okOrNull() { + return this.value; + } + + @Override + public Err errOrNull() { + return null; + } + + @Override + public void ifOk(Consumer consumer) { + consumer.accept(this.value); + } + + @Override + public void ifErr(Consumer consumer) { + } + + @Override + public Ok okOrElse(Function orElse) { + return this.value; + } + + @Override + public Err errOrElse(Function orElse) { + return orElse.apply(this.value); + } + + @Override + public Result mapOk(Function func) { + return ok(func.apply(this.value)); + } + + @SuppressWarnings("unchecked") + @Override + public Result mapErr(Function func) { + return (Result) this; + } + } + + private static class ErrImpl extends Result { + private final Err value; + + public ErrImpl(Err value) { + this.value = value; + } + + @Override + public boolean isOk() { + return false; + } + + @Override + public boolean isErr() { + return true; + } + + @Override + public Ok okOrNull() { + return null; + } + + @Override + public Err errOrNull() { + return this.value; + } + + @Override + public void ifOk(Consumer consumer) { + } + + @Override + public void ifErr(Consumer consumer) { + consumer.accept(this.value); + } + + @Override + public Ok okOrElse(Function orElse) { + return orElse.apply(this.value); + } + + @Override + public Err errOrElse(Function orElse) { + return this.value; + } + + @SuppressWarnings("unchecked") + @Override + public Result mapOk(Function func) { + return (Result) this; + } + + @Override + public Result mapErr(Function func) { + return err(func.apply(this.value)); + } + } +} diff --git a/src/main/java/com/replaymod/core/utils/Utils.java b/src/main/java/com/replaymod/core/utils/Utils.java index a318c793..50c328c8 100644 --- a/src/main/java/com/replaymod/core/utils/Utils.java +++ b/src/main/java/com/replaymod/core/utils/Utils.java @@ -7,7 +7,7 @@ import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.replaymod.core.ReplayMod; -import com.replaymod.replaystudio.us.myles.ViaVersion.api.protocol.ProtocolVersion; +import com.replaymod.replaystudio.lib.viaversion.api.protocol.version.ProtocolVersion; import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.RenderInfo; import de.johni0702.minecraft.gui.container.AbstractGuiScrollable; @@ -245,18 +245,10 @@ public class Utils { logger.trace("Opening crash report popup GUI"); GuiCrashReportPopup popup = new GuiCrashReportPopup(container, crashReportStr); - Futures.addCallback(popup.getFuture(), new FutureCallback() { - @Override - public void onSuccess(@Nullable Void result) { - logger.trace("Crash report popup closed"); - if (onClose != null) { - onClose.run(); - } - } - - @Override - public void onFailure(Throwable t) { - logger.error("During error popup:", t); + popup.onClosed(() -> { + logger.trace("Crash report popup closed"); + if (onClose != null) { + onClose.run(); } }); return popup; @@ -329,18 +321,10 @@ public class Utils { LOGGER.trace("Minimal mode active, denying action, opening popup"); MinimalModeUnsupportedPopup popup = new MinimalModeUnsupportedPopup(container); - Futures.addCallback(popup.getFuture(), new FutureCallback() { - @Override - public void onSuccess(@Nullable Void result) { - LOGGER.trace("Minimal mode popup closed"); - if (onPopupClosed != null) { - onPopupClosed.run(); - } - } - - @Override - public void onFailure(Throwable t) { - LOGGER.error("During minimal mode popup:", t); + popup.onClosed(() -> { + LOGGER.trace("Minimal mode popup closed"); + if (onPopupClosed != null) { + onPopupClosed.run(); } }); return false; @@ -353,7 +337,7 @@ public class Utils { ProtocolVersion latestVersion = ProtocolVersion.getProtocols() .stream() - .max(Comparator.comparing(ProtocolVersion::getId)) + .max(Comparator.comparing(ProtocolVersion::getVersion)) .orElseThrow(RuntimeException::new); getInfo().addElements(new VerticalLayout.Data(0.5), new GuiLabel() diff --git a/src/main/java/com/replaymod/core/versions/BufferBuilder.java b/src/main/java/com/replaymod/core/versions/BufferBuilder.java new file mode 100644 index 00000000..743815c4 --- /dev/null +++ b/src/main/java/com/replaymod/core/versions/BufferBuilder.java @@ -0,0 +1 @@ +// 1.7.10 and below diff --git a/src/main/java/com/replaymod/core/versions/GLFW.java b/src/main/java/com/replaymod/core/versions/GLFW.java new file mode 100644 index 00000000..fca4cd6c --- /dev/null +++ b/src/main/java/com/replaymod/core/versions/GLFW.java @@ -0,0 +1 @@ +// 1.12.2 and below diff --git a/src/main/java/com/replaymod/core/versions/MCVer.java b/src/main/java/com/replaymod/core/versions/MCVer.java index 75d17f50..ac4dd754 100644 --- a/src/main/java/com/replaymod/core/versions/MCVer.java +++ b/src/main/java/com/replaymod/core/versions/MCVer.java @@ -1,43 +1,27 @@ package com.replaymod.core.versions; +import com.mojang.blaze3d.platform.GlStateManager; import com.replaymod.core.mixin.GuiScreenAccessor; -import com.replaymod.core.mixin.MinecraftAccessor; +import com.replaymod.replaystudio.lib.viaversion.api.protocol.packet.State; +import com.replaymod.replaystudio.lib.viaversion.api.protocol.version.ProtocolVersion; import com.replaymod.replaystudio.protocol.PacketTypeRegistry; -import com.replaymod.replaystudio.us.myles.ViaVersion.api.protocol.ProtocolVersion; -import com.replaymod.replaystudio.us.myles.ViaVersion.packets.State; +import de.johni0702.minecraft.gui.MinecraftGuiRenderer; +import de.johni0702.minecraft.gui.utils.lwjgl.vector.Vector2f; +import de.johni0702.minecraft.gui.utils.lwjgl.vector.Vector3f; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.options.KeyBinding; -import net.minecraft.client.world.ClientWorld; -import net.minecraft.client.render.Tessellator; -import net.minecraft.client.render.entity.EntityRenderDispatcher; -import net.minecraft.client.model.ModelPart; -import net.minecraft.util.crash.CrashReportSection; -import net.minecraft.entity.Entity; -import net.minecraft.entity.player.PlayerEntity; -import net.minecraft.network.PacketByteBuf; +import net.minecraft.client.render.BufferBuilder; import net.minecraft.util.Identifier; import net.minecraft.util.Util; -import net.minecraft.util.math.MathHelper; -import net.minecraft.world.World; -import net.minecraft.world.chunk.WorldChunk; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; //#if MC>=11600 import net.minecraft.resource.ResourcePackSource; //#endif -//#if MC>=11500 -import net.minecraft.client.model.ModelPart.Cuboid; -import java.util.ArrayList; -//#else -//$$ import net.minecraft.client.model.Box; -//#endif - //#if MC>=11400 -import com.replaymod.core.mixin.AbstractButtonWidgetAccessor; +import com.replaymod.render.mixin.MainWindowAccessor; import net.minecraft.SharedConstants; +import net.minecraft.client.gl.Framebuffer; import net.minecraft.client.gui.widget.ButtonWidget; import net.minecraft.client.gui.widget.AbstractButtonWidget; @@ -56,22 +40,10 @@ import net.minecraft.text.TranslatableText; //$$ import net.minecraft.realms.RealmsSharedConstants; //#endif -//#if FABRIC>=1 -//#else -//$$ import net.minecraft.entity.LivingEntity; -//$$ import net.minecraftforge.client.event.GuiScreenEvent; -//$$ import net.minecraftforge.client.event.RenderGameOverlayEvent; -//$$ import net.minecraftforge.client.event.RenderLivingEvent; -//$$ import net.minecraftforge.common.MinecraftForge; -//$$ import net.minecraftforge.eventbus.api.IEventBus; -//#endif - //#if MC>=11400 -import net.minecraft.client.util.Window; import net.minecraft.client.util.InputUtil; import org.lwjgl.glfw.GLFW; //#else -//$$ import net.minecraft.client.gui.ScaledResolution; //$$ import net.minecraft.client.resources.ResourcePackRepository; //$$ import net.minecraftforge.fml.client.FMLClientHandler; //$$ import org.apache.logging.log4j.LogManager; @@ -80,28 +52,13 @@ import org.lwjgl.glfw.GLFW; //$$ import java.io.IOException; //#endif -//#if MC>=11400 -import net.minecraft.client.sound.PositionedSoundInstance; -//#else -//$$ import net.minecraft.client.audio.PositionedSoundRecord; -//#endif - //#if MC>=10904 import com.replaymod.render.blend.mixin.ParticleAccessor; import net.minecraft.client.particle.Particle; -import net.minecraft.sound.SoundEvent; import net.minecraft.util.math.Vec3d; //#endif -//#if MC>=10809 -import net.minecraft.client.render.VertexFormats; -//#else -//$$ import net.minecraftforge.fml.common.FMLCommonHandler; -//#endif - //#if MC>=10800 -import net.minecraft.client.render.BufferBuilder; -import com.mojang.blaze3d.platform.GlStateManager; import net.minecraft.client.render.VertexFormat; import net.minecraft.client.render.VertexFormatElement; //#if MC<11500 @@ -109,28 +66,17 @@ import net.minecraft.client.render.VertexFormatElement; //#endif //#else //$$ import com.replaymod.core.mixin.ResourcePackRepositoryAccessor; -//$$ import com.google.common.util.concurrent.Futures; //$$ import io.netty.handler.codec.DecoderException; +//$$ import net.minecraft.client.renderer.entity.RenderManager; //$$ import net.minecraft.client.resources.FileResourcePack; +//$$ import net.minecraft.network.PacketBuffer; //$$ //$$ import static org.lwjgl.opengl.GL11.*; //#endif -//#if FABRIC>=1 -import net.fabricmc.loader.api.FabricLoader; -//#else -//#if MC>=11400 -//$$ import net.minecraftforge.fml.ModList; -//#else -//$$ import net.minecraftforge.fml.common.Loader; -//#endif -//#endif - import java.io.File; import java.net.URI; -import java.util.Collection; import java.util.List; -import java.util.concurrent.Callable; import java.util.function.Consumer; import java.util.Optional; @@ -138,29 +84,6 @@ import java.util.Optional; * Abstraction over things that have changed between different MC versions. */ public class MCVer { - private static Logger LOGGER = LogManager.getLogger(); - - //#if FABRIC<=0 - //$$ public static IEventBus FORGE_BUS = MinecraftForge.EVENT_BUS; - //#if MC>=10809 - //$$ public static IEventBus FML_BUS = FORGE_BUS; - //#else - //$$ public static EventBus FML_BUS = FMLCommonHandler.instance().bus(); - //#endif - //#endif - - public static boolean isModLoaded(String id) { - //#if FABRIC>=1 - return FabricLoader.getInstance().isModLoaded(id.toLowerCase()); - //#else - //#if MC>=11400 - //$$ return ModList.get().isLoaded(id.toLowerCase()); - //#else - //$$ return Loader.isModLoaded(id); - //#endif - //#endif - } - public static int getProtocolVersion() { //#if MC>=11400 return SharedConstants.getGameVersion().getProtocolVersion(); @@ -176,271 +99,33 @@ public class MCVer { ); } - public static void addDetail(CrashReportSection category, String name, Callable callable) { - //#if MC>=10904 - //#if MC>=11200 - category.add(name, callable::call); - //#else - //$$ category.setDetail(name, callable::call); - //#endif - //#else - //$$ category.addCrashSectionCallable(name, callable); - //#endif - } - - public static double Entity_getX(Entity entity) { - //#if MC>=11500 - return entity.getX(); - //#else - //$$ return entity.x; - //#endif - } - - public static double Entity_getY(Entity entity) { - //#if MC>=11500 - return entity.getY(); - //#else - //$$ return entity.y; - //#endif - } - - public static double Entity_getZ(Entity entity) { - //#if MC>=11500 - return entity.getZ(); - //#else - //$$ return entity.z; - //#endif - } - - public static void Entity_setPos(Entity entity, double x, double y, double z) { - //#if MC>=11500 - entity.setPos(x, y, z); - //#else - //$$ entity.x = x; - //$$ entity.y = y; - //$$ entity.z = z; - //#endif - } - - //#if MC>=11400 - public static void width(AbstractButtonWidget button, int value) { - button.setWidth(value); - } - - public static int width(AbstractButtonWidget button) { - return button.getWidth(); - } - - public static int height(AbstractButtonWidget button) { - return ((AbstractButtonWidgetAccessor) button).getHeight(); - } - //#else - //$$ public static void width(GuiButton button, int value) { - //$$ button.width = value; - //$$ } - //$$ - //$$ public static int width(GuiButton button) { - //$$ return button.width; - //$$ } - //$$ - //$$ public static int height(GuiButton button) { - //$$ return button.height; - //$$ } - //#endif - - //#if FABRIC<=0 - //#if MC>=11400 - //$$ public static void addButton(GuiScreenEvent.InitGuiEvent event, Widget button) { - //$$ event.addWidget(button); - //$$ } - //$$ - //$$ public static void removeButton(GuiScreenEvent.InitGuiEvent event, Widget button) { - //$$ event.removeWidget(button); - //$$ } - //$$ - //$$ public static List getButtonList(GuiScreenEvent.InitGuiEvent event) { - //$$ return event.getWidgetList(); - //$$ } - //$$ - //$$ public static Widget getButton(GuiScreenEvent.ActionPerformedEvent event) { - //$$ return event.getButton(); - //$$ } - //#else - //$$ public static void addButton(GuiScreenEvent.InitGuiEvent event, GuiButton button) { + public static void resizeMainWindow(MinecraftClient mc, int width, int height) { //#if MC>=11400 - //$$ event.addButton(button); - //#else - //$$ getButtonList(event).add(button); + Framebuffer fb = mc.getFramebuffer(); + if (fb.viewportWidth != width || fb.viewportHeight != height) { + fb.resize(width, height, false); + } + //noinspection ConstantConditions + MainWindowAccessor mainWindow = (MainWindowAccessor) (Object) mc.getWindow(); + mainWindow.setFramebufferWidth(width); + mainWindow.setFramebufferHeight(height); + //#if MC>=11500 + mc.gameRenderer.onResized(width, height); //#endif - //$$ } - //$$ - //$$ public static void removeButton(GuiScreenEvent.InitGuiEvent event, GuiButton button) { - //#if MC>=11400 - //$$ event.removeButton(button); //#else - //$$ getButtonList(event).remove(button); - //#endif - //$$ } - //$$ - //$$ @SuppressWarnings("unchecked") - //$$ public static List getButtonList(GuiScreenEvent.InitGuiEvent event) { - //#if MC>=10904 - //$$ return event.getButtonList(); - //#else - //$$ return event.buttonList; - //#endif - //$$ } - //$$ - //$$ public static GuiButton getButton(GuiScreenEvent.ActionPerformedEvent event) { - //#if MC>=10904 - //$$ return event.getButton(); - //#else - //$$ return event.button; - //#endif - //$$ } - //#endif - //$$ - //$$ public static Screen getGui(GuiScreenEvent event) { - //#if MC>=10904 - //$$ return event.getGui(); - //#else - //$$ return event.gui; - //#endif - //$$ } - //$$ - //$$ public static LivingEntity getEntity(RenderLivingEvent event) { - //#if MC>=10904 - //$$ return event.getEntity(); - //#else - //$$ return event.entity; - //#endif - //$$ } - //#endif - - public static String readString(PacketByteBuf buffer, int max) { - //#if MC>=10800 - return buffer.readString(max); - //#else - //$$ try { - //$$ return buffer.readStringFromBuffer(max); - //$$ } catch (IOException e) { - //$$ throw new DecoderException(e); + //$$ if (width != mc.displayWidth || height != mc.displayHeight) { + //$$ mc.resize(width, height); //$$ } //#endif } - //#if FABRIC<=0 - //$$ public static RenderGameOverlayEvent.ElementType getType(RenderGameOverlayEvent event) { - //#if MC>=10904 - //$$ return event.getType(); - //#else - //$$ return event.type; - //#endif - //$$ } - //#endif - - //#if MC>=10800 - public static Entity getRenderViewEntity(MinecraftClient mc) { - return mc.getCameraEntity(); - } - //#else - //$$ public static EntityLivingBase getRenderViewEntity(Minecraft mc) { - //$$ return mc.renderViewEntity; - //$$ } - //#endif - - //#if MC>=10800 - public static void setRenderViewEntity(MinecraftClient mc, Entity entity) { - mc.setCameraEntity(entity); - } - //#else - //$$ public static void setRenderViewEntity(Minecraft mc, EntityLivingBase entity) { - //$$ mc.renderViewEntity = entity; - //$$ } - //#endif - - public static Entity getRiddenEntity(Entity ridden) { - //#if MC>=10904 - return ridden.getVehicle(); - //#else - //$$ return ridden.ridingEntity; - //#endif - } - - public static Iterable loadedEntityList(ClientWorld world) { - //#if MC>=11400 - return world.getEntities(); - //#else - //$$ return world.loadedEntityList; - //#endif - } - - @SuppressWarnings("unchecked") - public static Collection[] getEntityLists(WorldChunk chunk) { - //#if MC>=10800 - return chunk.getEntitySectionArray(); - //#else - //$$ return chunk.entityLists; - //#endif - } - - @SuppressWarnings("unchecked") - //#if MC>=11500 - public static List cubeList(ModelPart modelRenderer) { - return new ArrayList<>(); // FIXME 1.15 - } - //#else - //$$ public static List cubeList(ModelPart modelRenderer) { - //$$ return modelRenderer.boxes; - //$$ } - //#endif - - @SuppressWarnings("unchecked") - public static List playerEntities(World world) { - //#if MC>=11400 - return (List) world.getPlayers(); - //#else - //$$ return world.playerEntities; - //#endif - } - - public static boolean isOnMainThread() { - //#if MC>=11400 - return getMinecraft().isOnThread(); - //#else - //$$ return getMinecraft().isCallingFromMinecraftThread(); - //#endif - } - - public static void scheduleOnMainThread(Runnable runnable) { - //#if MC>=11400 - getMinecraft().send(runnable); - //#else - //$$ getMinecraft().addScheduledTask(runnable); - //#endif - } - - //#if MC>=11400 - public static Window getWindow(MinecraftClient mc) { - //#if MC>=11500 - return mc.getWindow(); - //#else - //$$ return mc.window; - //#endif - } - //#endif - - //#if MC>=11400 - public static Window newScaledResolution(MinecraftClient mc) { - return getWindow(mc); - } - //#else - //$$ public static ScaledResolution newScaledResolution(Minecraft mc) { - //#if MC>=10809 - //$$ return new ScaledResolution(mc); - //#else - //$$ return new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); - //#endif + //#if MC<10800 + //$$ public static String tryReadString(PacketBuffer buffer, int max) { + //$$ try { + //$$ return buffer.readStringFromBuffer(max); + //$$ } catch (IOException e) { + //$$ throw new DecoderException(e); + //$$ } //$$ } //#endif @@ -501,69 +186,6 @@ public class MCVer { //#endif } - //#if MC>=10800 - public static BufferBuilder Tessellator_getBufferBuilder() { - return Tessellator.getInstance().getBuffer(); - } - //#else - //$$ public static Tessellator Tessellator_getBufferBuilder() { - //$$ return Tessellator.instance; - //$$ } - //#endif - - public static void BufferBuilder_beginPosCol(int mode) { - Tessellator_getBufferBuilder().begin( - mode - //#if MC>=10809 - , VertexFormats.POSITION_COLOR - //#endif - ); - } - - public static void BufferBuilder_addPosCol(double x, double y, double z, int r, int g, int b, int a) { - //#if MC>=10809 - Tessellator_getBufferBuilder().vertex(x, y, z).color(r, g, b, a).next(); - //#else - //$$ Tessellator_getBufferBuilder().setColorRGBA(r, g, b, a); - //$$ Tessellator_getBufferBuilder().addVertex(x, y, z); - //#endif - } - - public static void BufferBuilder_beginPosTex(int mode) { - Tessellator_getBufferBuilder().begin( - mode - //#if MC>=10809 - , VertexFormats.POSITION_TEXTURE - //#endif - ); - } - - public static void BufferBuilder_addPosTex(double x, double y, double z, float u, float v) { - //#if MC>=10809 - Tessellator_getBufferBuilder().vertex(x, y, z).texture(u, v).next(); - //#else - //$$ Tessellator_getBufferBuilder().addVertexWithUV(x, y, z, u, v); - //#endif - } - - public static void BufferBuilder_beginPosTexCol(int mode) { - Tessellator_getBufferBuilder().begin( - mode - //#if MC>=10809 - , VertexFormats.POSITION_TEXTURE_COLOR - //#endif - ); - } - - public static void BufferBuilder_addPosTexCol(double x, double y, double z, float u, float v, int r, int g, int b, int a) { - //#if MC>=10809 - Tessellator_getBufferBuilder().vertex(x, y, z).texture(u, v).color(r, g, b, a).next(); - //#else - //$$ Tessellator_getBufferBuilder().setColorRGBA(r, g, b, a); - //$$ Tessellator_getBufferBuilder().addVertexWithUV(x, y, z, u, v); - //#endif - } - //#if MC>=10800 @SuppressWarnings("unchecked") public static List getElements(VertexFormat vertexFormat) { @@ -571,30 +193,16 @@ public class MCVer { } //#endif - public static Tessellator Tessellator_getInstance() { - //#if MC>=10800 - return Tessellator.getInstance(); - //#else - //$$ return Tessellator.instance; - //#endif - } - - public static EntityRenderDispatcher getRenderManager() { - //#if MC>=10800 - return getMinecraft().getEntityRenderDispatcher(); - //#else - //$$ return RenderManager.instance; - //#endif - } + //#if MC<10800 + //$$ public static RenderManager getRenderManager(@SuppressWarnings("unused") Minecraft mc) { + //$$ return RenderManager.instance; + //$$ } + //#endif public static MinecraftClient getMinecraft() { return MinecraftClient.getInstance(); } - public static float getRenderPartialTicks() { - return ((MinecraftAccessor) getMinecraft()).getTimer().tickDelta; - } - public static void addButton( Screen screen, //#if MC>=11400 @@ -604,14 +212,15 @@ public class MCVer { //#endif ) { GuiScreenAccessor acc = (GuiScreenAccessor) screen; - acc.getButtons().add(button); //#if MC>=11400 - acc.getChildren().add(button); + acc.invokeAddButton(button); + //#else + //$$ acc.getButtons().add(button); //#endif } //#if MC>=11400 - public static Optional findButton(List buttonList, @SuppressWarnings("unused") String text, @SuppressWarnings("unused") int id) { + public static Optional findButton(Iterable buttonList, @SuppressWarnings("unused") String text, @SuppressWarnings("unused") int id) { //#if MC>=11600 final TranslatableText message = new TranslatableText(text); //#else @@ -621,11 +230,17 @@ public class MCVer { if (message.equals(b.getMessage())) { return Optional.of(b); } + //#if MC>=11600 + // Fuzzy match (copy does not include children) + if (b.getMessage() != null && b.getMessage().copy().equals(message)) { + return Optional.of(b); + } + //#endif } return Optional.empty(); } //#else - //$$ public static Optional findButton(List buttonList, @SuppressWarnings("unused") String text, int id) { + //$$ public static Optional findButton(Iterable buttonList, @SuppressWarnings("unused") String text, int id) { //$$ for (GuiButton b : buttonList) { //$$ if (b.id == id) { //$$ return Optional.of(b); @@ -671,26 +286,6 @@ public class MCVer { //#endif } - public static void bindTexture(Identifier texture) { - //#if MC>=11500 - getMinecraft().getTextureManager().bindTexture(texture); - //#else - //#if MC>=11400 - //$$ getMinecraft().getTextureManager().bindTexture(texture); - //#else - //$$ getMinecraft().renderEngine.bindTexture(texture); - //#endif - //#endif - } - - public static float cos(float val) { - return MathHelper.cos(val); - } - - public static float sin(float val) { - return MathHelper.sin(val); - } - //#if MC>=10904 // TODO: this can be inlined once https://github.com/SpongePowered/Mixin/issues/305 is fixed public static Vec3d getPosition(Particle particle, float partialTicks) { @@ -733,52 +328,68 @@ public class MCVer { } public static void openURL(URI url) { - //#if MC>=11400 //#if MC>=11400 Util.getOperatingSystem().open(url); //#else - //$$ Util.getOSType().openURI(url); - //#endif - //#else //$$ try { //$$ Desktop.getDesktop().browse(url); //$$ } catch (Throwable e) { - //$$ LOGGER.error("Failed to open URL: ", e); + //$$ LogManager.getLogger().error("Failed to open URL: ", e); //$$ } //#endif } - public static String getBoundKey(KeyBinding keyBinding) { - try { - //#if MC>=11600 - return keyBinding.getBoundKeyLocalizedText().getString(); - //#else - //#if MC>=11400 - //$$ return keyBinding.getLocalizedName(); - //#else - //$$ return Keyboard.getKeyName(keyBinding.getKeyCode()); - //#endif - //#endif - } catch (ArrayIndexOutOfBoundsException e) { - // Apparently windows likes to press strange keys, see https://www.replaymod.com/forum/thread/55 - return "Unknown"; - } + public static void pushMatrix() { + //#if MC>=11700 + //$$ RenderSystem.getModelViewStack().push(); + //#else + GlStateManager.pushMatrix(); + //#endif } - public static void playSound(Identifier sound) { - getMinecraft().getSoundManager().play( - //#if MC>=11400 - PositionedSoundInstance.master(new SoundEvent(sound), 1.0F) - //#elseif MC>=10904 - //$$ PositionedSoundRecord.getMasterRecord(new SoundEvent(sound), 1.0F) - //#elseif MC>=10800 - //$$ PositionedSoundRecord.create(sound, 1.0F) - //#else - //$$ PositionedSoundRecord.createPositionedSoundRecord(sound, 1.0F) - //#endif - ); + public static void popMatrix() { + //#if MC>=11700 + //$$ RenderSystem.getModelViewStack().pop(); + //$$ RenderSystem.applyModelViewMatrix(); + //#else + GlStateManager.popMatrix(); + //#endif } + public static void emitLine(BufferBuilder buffer, Vector2f p1, Vector2f p2, int color) { + emitLine(buffer, new Vector3f(p1.x, p1.y, 0), new Vector3f(p2.x, p2.y, 0), color); + } + + public static void emitLine(BufferBuilder buffer, Vector3f p1, Vector3f p2, int color) { + int r = color >> 24 & 0xff; + int g = color >> 16 & 0xff; + int b = color >> 8 & 0xff; + int a = color & 0xff; + //#if MC>=11700 + //$$ Vector3f n = Vector3f.sub(p2, p1, null); + //#endif + buffer.vertex(p1.x, p1.y, p1.z) + .color(r, g, b, a) + //#if MC>=11700 + //$$ .normal(n.x, n.y, n.z) + //#endif + .next(); + buffer.vertex(p2.x, p2.y, p2.z) + .color(r, g, b, a) + //#if MC>=11700 + //$$ .normal(n.x, n.y, n.z) + //#endif + .next(); + } + + public static void bindTexture(Identifier id) { + new MinecraftGuiRenderer(null).bindTexture(id); + } + + //#if MC<10900 + //$$ public static class SoundEvent {} + //#endif + //#if MC>=11400 private static Boolean hasOptifine; public static boolean hasOptifine() { diff --git a/src/main/java/com/replaymod/core/versions/Patterns.java b/src/main/java/com/replaymod/core/versions/Patterns.java new file mode 100644 index 00000000..2de3d7b5 --- /dev/null +++ b/src/main/java/com/replaymod/core/versions/Patterns.java @@ -0,0 +1,502 @@ +package com.replaymod.core.versions; + +import com.replaymod.gradle.remap.Pattern; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.options.KeyBinding; +import net.minecraft.client.render.VertexFormat; +import net.minecraft.client.texture.TextureManager; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.render.entity.EntityRenderDispatcher; +import net.minecraft.client.sound.PositionedSoundInstance; +import net.minecraft.util.crash.CrashReportSection; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.network.PacketByteBuf; +import net.minecraft.util.Identifier; +import net.minecraft.world.World; +import net.minecraft.world.chunk.WorldChunk; + +//#if MC>=11700 +//#else +import org.lwjgl.opengl.GL11; +//#endif + +//#if MC>=11400 +import net.minecraft.client.gui.widget.AbstractButtonWidget; +import net.minecraft.client.util.Window; +//#else +//$$ import net.minecraft.client.gui.GuiButton; +//#endif + +//#if MC>=10904 +import net.minecraft.sound.SoundEvent; +import net.minecraft.util.crash.CrashCallable; +//#else +//$$ import java.util.concurrent.Callable; +//#endif + +//#if MC>=10809 +import net.minecraft.client.render.VertexFormats; +//#else +//#endif + +//#if MC>=10800 +import net.minecraft.client.render.BufferBuilder; +//#else +//$$ import net.minecraft.entity.EntityLivingBase; +//#endif + +import java.util.Collection; +import java.util.List; + +class Patterns { + //#if MC>=10904 + @Pattern + private static void addCrashCallable(CrashReportSection category, String name, CrashCallable callable) { + //#if MC>=11200 + category.add(name, callable); + //#else + //$$ category.setDetail(name, callable); + //#endif + } + //#else + //$$ @Pattern + //$$ private static void addCrashCallable(CrashReportCategory category, String name, Callable callable) { + //$$ category.addCrashSectionCallable(name, callable); + //$$ } + //#endif + + @Pattern + private static double Entity_getX(Entity entity) { + //#if MC>=11500 + return entity.getX(); + //#else + //$$ return entity.x; + //#endif + } + + @Pattern + private static double Entity_getY(Entity entity) { + //#if MC>=11500 + return entity.getY(); + //#else + //$$ return entity.y; + //#endif + } + + @Pattern + private static double Entity_getZ(Entity entity) { + //#if MC>=11500 + return entity.getZ(); + //#else + //$$ return entity.z; + //#endif + } + + @Pattern + private static void Entity_setYaw(Entity entity, float value) { + //#if MC>=11700 + //$$ entity.setYaw(value); + //#else + entity.yaw = value; + //#endif + } + + @Pattern + private static float Entity_getYaw(Entity entity) { + //#if MC>=11700 + //$$ return entity.getYaw(); + //#else + return entity.yaw; + //#endif + } + + @Pattern + private static void Entity_setPitch(Entity entity, float value) { + //#if MC>=11700 + //$$ entity.setPitch(value); + //#else + entity.pitch = value; + //#endif + } + + @Pattern + private static float Entity_getPitch(Entity entity) { + //#if MC>=11700 + //$$ return entity.getPitch(); + //#else + return entity.pitch; + //#endif + } + + @Pattern + private static void Entity_setPos(Entity entity, double x, double y, double z) { + //#if MC>=11500 + entity.setPos(x, y, z); + //#else + //$$ { net.minecraft.entity.Entity self = entity; self.x = x; self.y = y; self.z = z; } + //#endif + } + + //#if MC>=11400 + @Pattern + private static void setWidth(AbstractButtonWidget button, int value) { + button.setWidth(value); + } + + @Pattern + private static int getWidth(AbstractButtonWidget button) { + return button.getWidth(); + } + + @Pattern + private static int getHeight(AbstractButtonWidget button) { + //#if MC>=11600 + return button.getHeight(); + //#else + //$$ return ((com.replaymod.core.mixin.AbstractButtonWidgetAccessor) button).getHeight(); + //#endif + } + //#else + //$$ @Pattern + //$$ private static void setWidth(GuiButton button, int value) { + //$$ button.width = value; + //$$ } + //$$ + //$$ @Pattern + //$$ private static int getWidth(GuiButton button) { + //$$ return button.width; + //$$ } + //$$ + //$$ @Pattern + //$$ private static int getHeight(GuiButton button) { + //$$ return button.height; + //$$ } + //#endif + + @Pattern + private static String readString(PacketByteBuf buffer, int max) { + //#if MC>=10800 + return buffer.readString(max); + //#else + //$$ return com.replaymod.core.versions.MCVer.tryReadString(buffer, max); + //#endif + } + + @Pattern + //#if MC>=10800 + private static Entity getRenderViewEntity(MinecraftClient mc) { + return mc.getCameraEntity(); + } + //#else + //$$ private static EntityLivingBase getRenderViewEntity(Minecraft mc) { + //$$ return mc.renderViewEntity; + //$$ } + //#endif + + @Pattern + //#if MC>=10800 + private static void setRenderViewEntity(MinecraftClient mc, Entity entity) { + mc.setCameraEntity(entity); + } + //#else + //$$ private static void setRenderViewEntity(Minecraft mc, EntityLivingBase entity) { + //$$ mc.renderViewEntity = entity; + //$$ } + //#endif + + @Pattern + private static Entity getVehicle(Entity passenger) { + //#if MC>=10904 + return passenger.getVehicle(); + //#else + //$$ return passenger.ridingEntity; + //#endif + } + + @Pattern + private static Iterable loadedEntityList(ClientWorld world) { + //#if MC>=11400 + return world.getEntities(); + //#else + //#if MC>=10809 + //$$ return world.loadedEntityList; + //#else + //$$ return ((java.util.List) world.loadedEntityList); + //#endif + //#endif + } + + @Pattern + //#if MC>=11700 + //$$ private static void getEntitySectionArray() {} + //#else + private static Collection[] getEntitySectionArray(WorldChunk chunk) { + //#if MC>=11700 + //$$ return obsolete(chunk); + //#elseif MC>=10800 + return chunk.getEntitySectionArray(); + //#else + //$$ return chunk.entityLists; + //#endif + } + //#endif + + @Pattern + private static List playerEntities(World world) { + //#if MC>=11400 + return world.getPlayers(); + //#elseif MC>=10809 + //$$ return world.playerEntities; + //#else + //$$ return ((List) world.playerEntities); + //#endif + } + + @Pattern + private static boolean isOnMainThread(MinecraftClient mc) { + //#if MC>=11400 + return mc.isOnThread(); + //#else + //$$ return mc.isCallingFromMinecraftThread(); + //#endif + } + + @Pattern + private static void scheduleOnMainThread(MinecraftClient mc, Runnable runnable) { + //#if MC>=11400 + mc.send(runnable); + //#else + //$$ mc.addScheduledTask(runnable); + //#endif + } + + @Pattern + private static Window getWindow(MinecraftClient mc) { + //#if MC>=11500 + return mc.getWindow(); + //#elseif MC>=11400 + //$$ return mc.window; + //#else + //$$ return new com.replaymod.core.versions.Window(mc); + //#endif + } + + @Pattern + private static BufferBuilder Tessellator_getBuffer(Tessellator tessellator) { + //#if MC>=10800 + return tessellator.getBuffer(); + //#else + //$$ return new BufferBuilder(tessellator); + //#endif + } + + //#if MC<11700 + @Pattern + private static void BufferBuilder_beginPosCol(BufferBuilder buffer, int mode) { + //#if MC>=10809 + buffer.begin(mode, VertexFormats.POSITION_COLOR); + //#else + //$$ buffer.startDrawing(mode /* POSITION_COLOR */); + //#endif + } + + @Pattern + private static void BufferBuilder_addPosCol(BufferBuilder buffer, double x, double y, double z, int r, int g, int b, int a) { + //#if MC>=10809 + buffer.vertex(x, y, z).color(r, g, b, a).next(); + //#else + //$$ { WorldRenderer $buffer = buffer; double $x = x; double $y = y; double $z = z; $buffer.setColorRGBA(r, g, b, a); $buffer.addVertex($x, $y, $z); } + //#endif + } + + @Pattern + private static void BufferBuilder_beginPosTex(BufferBuilder buffer, int mode) { + //#if MC>=10809 + buffer.begin(mode, VertexFormats.POSITION_TEXTURE); + //#else + //$$ buffer.startDrawing(mode /* POSITION_TEXTURE */); + //#endif + } + + @Pattern + private static void BufferBuilder_addPosTex(BufferBuilder buffer, double x, double y, double z, float u, float v) { + //#if MC>=10809 + buffer.vertex(x, y, z).texture(u, v).next(); + //#else + //$$ buffer.addVertexWithUV(x, y, z, u, v); + //#endif + } + + @Pattern + private static void BufferBuilder_beginPosTexCol(BufferBuilder buffer, int mode) { + //#if MC>=10809 + buffer.begin(mode, VertexFormats.POSITION_TEXTURE_COLOR); + //#else + //$$ buffer.startDrawing(mode /* POSITION_TEXTURE_COLOR */); + //#endif + } + + @Pattern + private static void BufferBuilder_addPosTexCol(BufferBuilder buffer, double x, double y, double z, float u, float v, int r, int g, int b, int a) { + //#if MC>=10809 + buffer.vertex(x, y, z).texture(u, v).color(r, g, b, a).next(); + //#else + //$$ { WorldRenderer $buffer = buffer; double $x = x; double $y = y; double $z = z; float $u = u; float $v = v; $buffer.setColorRGBA(r, g, b, a); $buffer.addVertexWithUV($x, $y, $z, $u, $v); } + //#endif + } + //#else + //$$ @Pattern private static void BufferBuilder_beginPosCol() {} + //$$ @Pattern private static void BufferBuilder_addPosCol() {} + //$$ @Pattern private static void BufferBuilder_beginPosTex() {} + //$$ @Pattern private static void BufferBuilder_addPosTex() {} + //$$ @Pattern private static void BufferBuilder_beginPosTexCol() {} + //$$ @Pattern private static void BufferBuilder_addPosTexCol() {} + //#endif + + @Pattern + private static Tessellator Tessellator_getInstance() { + //#if MC>=10800 + return Tessellator.getInstance(); + //#else + //$$ return Tessellator.instance; + //#endif + } + + @Pattern + private static EntityRenderDispatcher getEntityRenderDispatcher(MinecraftClient mc) { + //#if MC>=10800 + return mc.getEntityRenderDispatcher(); + //#else + //$$ return com.replaymod.core.versions.MCVer.getRenderManager(mc); + //#endif + } + + @Pattern + private static float getCameraYaw(EntityRenderDispatcher dispatcher) { + //#if MC>=11500 + return dispatcher.camera.getYaw(); + //#else + //$$ return dispatcher.cameraYaw; + //#endif + } + + @Pattern + private static float getCameraPitch(EntityRenderDispatcher dispatcher) { + //#if MC>=11500 + return dispatcher.camera.getPitch(); + //#else + //$$ return dispatcher.cameraPitch; + //#endif + } + + @Pattern + private static float getRenderPartialTicks(MinecraftClient mc) { + //#if MC>=10900 + return mc.getTickDelta(); + //#else + //$$ return ((com.replaymod.core.mixin.MinecraftAccessor) mc).getTimer().renderPartialTicks; + //#endif + } + + @Pattern + private static TextureManager getTextureManager(MinecraftClient mc) { + //#if MC>=11400 + return mc.getTextureManager(); + //#else + //$$ return mc.renderEngine; + //#endif + } + + @Pattern + private static String getBoundKeyName(KeyBinding keyBinding) { + //#if MC>=11600 + return keyBinding.getBoundKeyLocalizedText().getString(); + //#elseif MC>=11400 + //$$ return keyBinding.getLocalizedName(); + //#else + //$$ return org.lwjgl.input.Keyboard.getKeyName(keyBinding.getKeyCode()); + //#endif + } + + @Pattern + private static PositionedSoundInstance master(Identifier sound, float pitch) { + //#if MC>=10900 + return PositionedSoundInstance.master(new SoundEvent(sound), pitch); + //#elseif MC>=10800 + //$$ return PositionedSoundRecord.create(sound, pitch); + //#else + //$$ return PositionedSoundRecord.createPositionedSoundRecord(sound, pitch); + //#endif + } + + @Pattern + private static boolean isKeyBindingConflicting(KeyBinding a, KeyBinding b) { + //#if MC>=10900 + return a.equals(b); + //#else + //$$ return (a.getKeyCode() == b.getKeyCode()); + //#endif + } + + //#if MC>=11600 + @Pattern + private static void BufferBuilder_beginLineStrip(BufferBuilder buffer, VertexFormat vertexFormat) { + //#if MC>=11700 + //$$ buffer.begin(net.minecraft.client.render.VertexFormat.DrawMode.LINE_STRIP, VertexFormats.LINES); + //#else + buffer.begin(GL11.GL_LINE_STRIP, VertexFormats.POSITION_COLOR); + //#endif + } + + @Pattern + private static void BufferBuilder_beginLines(BufferBuilder buffer) { + //#if MC>=11700 + //$$ buffer.begin(net.minecraft.client.render.VertexFormat.DrawMode.LINES, VertexFormats.LINES); + //#else + buffer.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR); + //#endif + } + + @Pattern + private static void BufferBuilder_beginQuads(BufferBuilder buffer, VertexFormat vertexFormat) { + //#if MC>=11700 + //$$ buffer.begin(net.minecraft.client.render.VertexFormat.DrawMode.QUADS, vertexFormat); + //#else + buffer.begin(GL11.GL_QUADS, vertexFormat); + //#endif + } + //#else + //$$ @Pattern private static void BufferBuilder_beginLineStrip() {} + //$$ @Pattern private static void BufferBuilder_beginLines() {} + //$$ @Pattern private static void BufferBuilder_beginQuads() {} + //#endif + + @Pattern + private static void GL11_glLineWidth(float width) { + //#if MC>=11700 + //$$ com.mojang.blaze3d.systems.RenderSystem.lineWidth(width); + //#else + GL11.glLineWidth(width); + //#endif + } + + @Pattern + private static void GL11_glTranslatef(float x, float y, float z) { + //#if MC>=11700 + //$$ com.mojang.blaze3d.systems.RenderSystem.getModelViewStack().translate(x, y, z); + //#else + GL11.glTranslatef(x, y, z); + //#endif + } + + @Pattern + private static void GL11_glRotatef(float angle, float x, float y, float z) { + //#if MC>=11700 + //$$ { float $angle = angle; com.mojang.blaze3d.systems.RenderSystem.getModelViewStack().multiply(new net.minecraft.util.math.Quaternion(new net.minecraft.util.math.Vec3f(x, y, z), $angle, true)); } + //#else + GL11.glRotatef(angle, x, y, z); + //#endif + } +} diff --git a/src/main/java/com/replaymod/core/versions/Window.java b/src/main/java/com/replaymod/core/versions/Window.java new file mode 100644 index 00000000..fca4cd6c --- /dev/null +++ b/src/main/java/com/replaymod/core/versions/Window.java @@ -0,0 +1 @@ +// 1.12.2 and below diff --git a/src/main/java/com/replaymod/core/versions/forge/EventsAdapter.java b/src/main/java/com/replaymod/core/versions/forge/EventsAdapter.java new file mode 100644 index 00000000..c690defc --- /dev/null +++ b/src/main/java/com/replaymod/core/versions/forge/EventsAdapter.java @@ -0,0 +1 @@ +// Forge only diff --git a/src/main/java/com/replaymod/core/versions/forge/Patterns.java b/src/main/java/com/replaymod/core/versions/forge/Patterns.java new file mode 100644 index 00000000..6b1e174a --- /dev/null +++ b/src/main/java/com/replaymod/core/versions/forge/Patterns.java @@ -0,0 +1 @@ +// 1.12.2 and before diff --git a/src/main/java/com/replaymod/core/versions/scheduler/Scheduler.java b/src/main/java/com/replaymod/core/versions/scheduler/Scheduler.java new file mode 100644 index 00000000..df66ddc3 --- /dev/null +++ b/src/main/java/com/replaymod/core/versions/scheduler/Scheduler.java @@ -0,0 +1,30 @@ +package com.replaymod.core.versions.scheduler; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeoutException; + +public interface Scheduler { + /** + * Execute the given runnable on the main client thread, returning only after it has been run (or after 30 seconds). + */ + void runSync(Runnable runnable) throws InterruptedException, ExecutionException, TimeoutException; + + /** + * Execute the given runnable after game has started (once the overlay has been closed). + * Most importantly, it will run after resources (including language keys!) have been loaded. + * Below 1.14, this is equivalent to {@link #runLater(Runnable)}. + */ + void runPostStartup(Runnable runnable); + + /** + * Pre-1.14 MC would hold the lock on the scheduledTasks queue while executing its tasks + * such that no new tasks could be submitted while one of them was running. + * This would cause issues with long-running tasks (e.g. video rendering) as it would + * block all async tasks (e.g. skin loading). + */ + void runLaterWithoutLock(Runnable runnable); + + void runLater(Runnable runnable); + + void runTasks(); +} diff --git a/src/main/java/com/replaymod/core/versions/scheduler/SchedulerImpl.java b/src/main/java/com/replaymod/core/versions/scheduler/SchedulerImpl.java new file mode 100644 index 00000000..dc3664fe --- /dev/null +++ b/src/main/java/com/replaymod/core/versions/scheduler/SchedulerImpl.java @@ -0,0 +1,124 @@ +package com.replaymod.core.versions.scheduler; + +import com.replaymod.core.mixin.MinecraftAccessor; +import net.minecraft.client.MinecraftClient; +import net.minecraft.util.crash.CrashException; +import net.minecraft.util.thread.ReentrantThreadExecutor; + +import java.util.concurrent.ExecutionException; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +public class SchedulerImpl implements Scheduler { + private static final MinecraftClient mc = MinecraftClient.getInstance(); + + @Override + public void runSync(Runnable runnable) throws InterruptedException, ExecutionException, TimeoutException { + if (mc.isOnThread()) { + runnable.run(); + } else { + executor.submit(() -> { + runnable.run(); + return null; + }).get(30, TimeUnit.SECONDS); + } + } + + @Override + public void runPostStartup(Runnable runnable) { + runLater(new Runnable() { + @Override + public void run() { + if (mc.getOverlay() != null) { + // delay until after resources have been loaded + runLater(this); + return; + } + runnable.run(); + } + }); + } + + /** + * Set when the currently running code has been scheduled by runLater. + * If this is the case, subsequent calls to runLater have to be delayed until all scheduled tasks have been + * processed, otherwise a livelock may occur. + */ + private boolean inRunLater = false; + private boolean inRenderTaskQueue = false; + // Starting 1.14 MC clears the queue of scheduled tasks on disconnect. + // This works fine for MC since it uses the queue only for packet handling but breaks our assumption that + // stuff submitted via runLater is actually always run (e.g. recording might not be fully stopped because parts + // of that are run via runLater and stopping the recording happens right around the time MC clears the queue). + // Luckily, that's also the version where MC pulled out the executor implementation, so we can just spin up our own. + public static class ReplayModExecutor extends ReentrantThreadExecutor { + private final Thread mcThread = Thread.currentThread(); + + private ReplayModExecutor(String string_1) { + super(string_1); + } + + @Override + protected Runnable createTask(Runnable runnable) { + return runnable; + } + + @Override + protected boolean canExecute(Runnable runnable) { + return true; + } + + @Override + protected Thread getThread() { + return mcThread; + } + + @Override + public void runTasks() { + super.runTasks(); + } + } + public final ReplayModExecutor executor = new ReplayModExecutor("Client/ReplayMod"); + + @Override + public void runTasks() { + executor.runTasks(); + } + + @Override + public void runLaterWithoutLock(Runnable runnable) { + // MC 1.14+ no longer synchronizes on the queue while running its tasks + runLater(runnable); + } + + @Override + public void runLater(Runnable runnable) { + runLater(runnable, () -> runLater(runnable)); + } + + private void runLater(Runnable runnable, Runnable defer) { + if (mc.isOnThread() && inRunLater && !inRenderTaskQueue) { + ((MinecraftAccessor) mc).getRenderTaskQueue().offer(() -> { + inRenderTaskQueue = true; + try { + defer.run(); + } finally { + inRenderTaskQueue = false; + } + }); + } else { + executor.send(() -> { + inRunLater = true; + try { + runnable.run(); + } catch (CrashException e) { + e.printStackTrace(); + System.err.println(e.getReport().asString()); + mc.setCrashReport(e.getReport()); + } finally { + inRunLater = false; + } + }); + } + } +} diff --git a/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java b/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java index 80e6494d..e3c40d5a 100644 --- a/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java +++ b/src/main/java/com/replaymod/editor/gui/MarkerProcessor.java @@ -12,9 +12,9 @@ import com.replaymod.replaystudio.protocol.PacketTypeRegistry; import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayMetaData; import com.replaymod.replaystudio.stream.IteratorStream; -import com.replaymod.replaystudio.us.myles.ViaVersion.api.Pair; import com.replaymod.replaystudio.util.Utils; import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.tuple.Pair; import java.io.IOException; import java.io.InputStream; @@ -115,7 +115,7 @@ public class MarkerProcessor { try (ReplayFile inputReplayFile = mod.openReplay(path)) { metaData = inputReplayFile.getMetaData(); } - return Collections.singletonList(new Pair<>(path, metaData)); + return Collections.singletonList(Pair.of(path, metaData)); } String replayName = FilenameUtils.getBaseName(path.getFileName().toString()); @@ -249,7 +249,7 @@ public class MarkerProcessor { outputReplayFile.save(); - outputPaths.add(new Pair(outputPath, metaData)); + outputPaths.add(Pair.of(outputPath, metaData)); } } diff --git a/src/main/java/com/replaymod/editor/handler/GuiHandler.java b/src/main/java/com/replaymod/editor/handler/GuiHandler.java index 7f5f4727..38628e83 100644 --- a/src/main/java/com/replaymod/editor/handler/GuiHandler.java +++ b/src/main/java/com/replaymod/editor/handler/GuiHandler.java @@ -1,6 +1,5 @@ package com.replaymod.editor.handler; -import de.johni0702.minecraft.gui.utils.EventRegistrations; import com.replaymod.core.utils.Utils; import com.replaymod.editor.ReplayModEditor; import com.replaymod.editor.gui.GuiEditReplay; @@ -8,31 +7,15 @@ import com.replaymod.replay.gui.screen.GuiReplayViewer; import de.johni0702.minecraft.gui.container.AbstractGuiScreen; import de.johni0702.minecraft.gui.container.GuiScreen; import de.johni0702.minecraft.gui.element.GuiButton; -import net.minecraft.util.crash.CrashReport; - -//#if FABRIC>=1 +import de.johni0702.minecraft.gui.utils.EventRegistrations; import de.johni0702.minecraft.gui.versions.callbacks.InitScreenCallback; -import net.minecraft.client.gui.screen.Screen; -import net.minecraft.client.gui.widget.AbstractButtonWidget; -import java.util.List; -//#else -//$$ import net.minecraftforge.client.event.GuiScreenEvent; -//$$ import net.minecraftforge.eventbus.api.SubscribeEvent; -//#endif +import net.minecraft.util.crash.CrashReport; import java.io.IOException; -import static com.replaymod.core.versions.MCVer.*; - public class GuiHandler extends EventRegistrations { - //#if FABRIC>=1 - { on(InitScreenCallback.EVENT, this::injectIntoReplayViewer); } - public void injectIntoReplayViewer(Screen vanillaGuiScreen, List buttonList) { - //#else - //$$ @SubscribeEvent - //$$ public void injectIntoReplayViewer(GuiScreenEvent.InitGuiEvent.Post event) { - //$$ net.minecraft.client.gui.screen.Screen vanillaGuiScreen = getGui(event); - //#endif + { on(InitScreenCallback.EVENT, (vanillaGuiScreen, buttonList) -> injectIntoReplayViewer(vanillaGuiScreen)); } + public void injectIntoReplayViewer(net.minecraft.client.gui.screen.Screen vanillaGuiScreen) { AbstractGuiScreen guiScreen = GuiScreen.from(vanillaGuiScreen); if (!(guiScreen instanceof GuiReplayViewer)) { return; diff --git a/src/main/java/com/replaymod/extras/FullBrightness.java b/src/main/java/com/replaymod/extras/FullBrightness.java index 63fe5c16..b6bef691 100644 --- a/src/main/java/com/replaymod/extras/FullBrightness.java +++ b/src/main/java/com/replaymod/extras/FullBrightness.java @@ -1,6 +1,8 @@ package com.replaymod.extras; import com.replaymod.core.ReplayMod; +import com.replaymod.core.events.PostRenderCallback; +import com.replaymod.core.events.PreRenderCallback; import com.replaymod.core.versions.MCVer.Keyboard; import com.replaymod.replay.ReplayHandler; import com.replaymod.replay.ReplayModReplay; @@ -14,14 +16,6 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.entity.effect.StatusEffects; -//#if FABRIC>=1 -import com.replaymod.core.events.PreRenderCallback; -import com.replaymod.core.events.PostRenderCallback; -//#else -//$$ import net.minecraftforge.eventbus.api.SubscribeEvent; -//$$ import net.minecraftforge.event.TickEvent; -//#endif - public class FullBrightness extends EventRegistrations implements Extra { private ReplayMod core; private ReplayModReplay module; @@ -72,14 +66,8 @@ public class FullBrightness extends EventRegistrations implements Extra { return Type.Gamma; } - //#if FABRIC>=1 { on(PreRenderCallback.EVENT, this::preRender); } private void preRender() { - //#else - //$$ @SubscribeEvent - //$$ public void preRender(TickEvent.RenderTickEvent event) { - //$$ if (event.phase != TickEvent.Phase.START) return; - //#endif if (active && module.getReplayHandler() != null) { Type type = getType(); if (type == Type.Gamma || type == Type.Both) { @@ -98,14 +86,8 @@ public class FullBrightness extends EventRegistrations implements Extra { } } - //#if FABRIC>=1 { on(PostRenderCallback.EVENT, this::postRender); } private void postRender() { - //#else - //$$ @SubscribeEvent - //$$ public void postRender(TickEvent.RenderTickEvent event) { - //$$ if (event.phase != TickEvent.Phase.END) return; - //#endif if (active && module.getReplayHandler() != null) { Type type = getType(); if (type == Type.Gamma || type == Type.Both) { diff --git a/src/main/java/com/replaymod/extras/OpenEyeExtra.java b/src/main/java/com/replaymod/extras/OpenEyeExtra.java deleted file mode 100644 index 49675f01..00000000 --- a/src/main/java/com/replaymod/extras/OpenEyeExtra.java +++ /dev/null @@ -1,164 +0,0 @@ -package com.replaymod.extras; - -import com.replaymod.core.ReplayMod; -import com.replaymod.core.versions.MCVer; -import de.johni0702.minecraft.gui.container.AbstractGuiScreen; -import de.johni0702.minecraft.gui.container.GuiContainer; -import de.johni0702.minecraft.gui.container.GuiPanel; -import de.johni0702.minecraft.gui.container.GuiScreen; -import de.johni0702.minecraft.gui.element.GuiButton; -import de.johni0702.minecraft.gui.element.GuiLabel; -import de.johni0702.minecraft.gui.function.Tickable; -import de.johni0702.minecraft.gui.layout.CustomLayout; -import de.johni0702.minecraft.gui.layout.HorizontalLayout; -import de.johni0702.minecraft.gui.layout.VerticalLayout; -import de.johni0702.minecraft.gui.popup.AbstractGuiPopup; -import de.johni0702.minecraft.gui.utils.Colors; -import org.apache.commons.io.FileUtils; - -import javax.net.ssl.HttpsURLConnection; -import java.io.File; -import java.io.FileOutputStream; -import java.net.URL; -import java.nio.channels.Channels; -import java.nio.channels.FileChannel; -import java.nio.channels.ReadableByteChannel; - -import static com.replaymod.core.utils.Utils.SSL_SOCKET_FACTORY; -import static com.replaymod.extras.ReplayModExtras.LOGGER; - -public class OpenEyeExtra implements Extra { - private static final String DOWNLOAD_URL = "https://www.replaymod.com/dl/openeye/" + ReplayMod.getMinecraftVersion(); - - private ReplayMod mod; - - @Override - public void register(ReplayMod mod) throws Exception { - this.mod = mod; - - boolean isOpenEyeLoaded = MCVer.isModLoaded("OpenEye"); - if (!isOpenEyeLoaded && mod.getSettingsRegistry().get(Setting.ASK_FOR_OPEN_EYE)) { - new Thread(() -> { - try { - LOGGER.trace("Checking for OpenEye availability"); - HttpsURLConnection connection = (HttpsURLConnection) new URL(DOWNLOAD_URL).openConnection(); - connection.setSSLSocketFactory(SSL_SOCKET_FACTORY); - connection.setRequestMethod("HEAD"); - connection.connect(); - LOGGER.trace("Got response code: {}", connection.getResponseCode()); - if (connection.getResponseCode() == 200) { - mod.runLater(() -> new OfferGui(GuiScreen.wrap(mod.getMinecraft().currentScreen)).display()); - } else { - LOGGER.info("Cannot offer OpenEye, server returned: {} {}", - connection.getResponseCode(), connection.getResponseMessage()); - } - connection.disconnect(); - } catch (Throwable e) { - LOGGER.error("Failed to check for OpenEye availability:", e); - } - }).start(); - } - } - - public class OfferGui extends AbstractGuiScreen { - public final GuiScreen parent; - public final GuiPanel textPanel = new GuiPanel().setLayout(new VerticalLayout().setSpacing(3)) - .addElements(new VerticalLayout.Data(0.5), - new GuiLabel().setI18nText("replaymod.gui.offeropeneye1"), - new GuiLabel().setI18nText("replaymod.gui.offeropeneye2"), - new GuiLabel().setI18nText("replaymod.gui.offeropeneye3"), - new GuiLabel().setI18nText("replaymod.gui.offeropeneye4"), - new GuiLabel().setI18nText("replaymod.gui.offeropeneye5"), - new GuiLabel().setI18nText("replaymod.gui.offeropeneye6"), - new GuiLabel().setI18nText("replaymod.gui.offeropeneye7"), - new GuiLabel().setI18nText("replaymod.gui.offeropeneye8")); - public final GuiPanel buttonPanel = new GuiPanel() - .setLayout(new HorizontalLayout(HorizontalLayout.Alignment.CENTER).setSpacing(5)); - public final GuiPanel contentPanel = new GuiPanel(this).setLayout(new VerticalLayout().setSpacing(20)) - .addElements(new VerticalLayout.Data(0.5), textPanel, buttonPanel); - public final GuiButton yesButton = new GuiButton(buttonPanel).setSize(150, 20).setI18nLabel("gui.yes"); - public final GuiButton noButton = new GuiButton(buttonPanel).setSize(150, 20).setI18nLabel("gui.no"); - - public OfferGui(GuiScreen parent) { - this.parent = parent; - - yesButton.onClick(() -> { - GuiPopup popup = new GuiPopup(OfferGui.this); - new Thread(() -> { - try { - File targetFile = new File(mod.getMinecraft().runDirectory, "mods/" + ReplayMod.getMinecraftVersion() + "/OpenEye.jar"); - FileUtils.forceMkdir(targetFile.getParentFile()); - - HttpsURLConnection connection = (HttpsURLConnection) new URL(DOWNLOAD_URL).openConnection(); - connection.setSSLSocketFactory(SSL_SOCKET_FACTORY); - ReadableByteChannel in = Channels.newChannel(connection.getInputStream()); - FileChannel out = new FileOutputStream(targetFile).getChannel(); - out.transferFrom(in, 0, Long.MAX_VALUE); - } catch (Throwable e) { - e.printStackTrace(); - } finally { - mod.runLater(() -> { - popup.close(); - parent.display(); - }); - } - }).start(); - }); - noButton.onClick(() -> { - mod.getSettingsRegistry().set(Setting.ASK_FOR_OPEN_EYE, false); - mod.getSettingsRegistry().save(); - parent.display(); - }); - - setLayout(new CustomLayout() { - @Override - protected void layout(OfferGui container, int width, int height) { - pos(contentPanel, width / 2 - width(contentPanel) / 2, height / 2 - height(contentPanel) / 2); - } - }); - } - - @Override - protected OfferGui getThis() { - return this; - } - } - - public static final class GuiPopup extends AbstractGuiPopup { - GuiPopup(GuiContainer container) { - super(container); - popup.addElements(null, new GuiIndicator().setColor(Colors.BLACK)); - setBackgroundColor(Colors.DARK_TRANSPARENT); - open(); - } - - @Override - public void close() { - super.close(); - } - - @Override - protected GuiPopup getThis() { - return this; - } - - private static final class GuiIndicator extends GuiLabel implements Tickable { - private int tick; - - @Override - public void tick() { - tick++; - setText(new String[]{ - "Ooooo", - "oOooo", - "ooOoo", - "oooOo", - "ooooO", - "oooOo", - "ooOoo", - "oOooo", - }[tick / 5 % 8]); - } - } - } -} diff --git a/src/main/java/com/replaymod/extras/ReplayModExtras.java b/src/main/java/com/replaymod/extras/ReplayModExtras.java index d22ed045..add19778 100644 --- a/src/main/java/com/replaymod/extras/ReplayModExtras.java +++ b/src/main/java/com/replaymod/extras/ReplayModExtras.java @@ -24,8 +24,7 @@ public class ReplayModExtras implements Module { YoutubeUpload.class, FullBrightness.class, QuickMode.class, - HotkeyButtons.class, - OpenEyeExtra.class + HotkeyButtons.class ); private final Map, Extra> instances = new HashMap<>(); diff --git a/src/main/java/com/replaymod/extras/advancedscreenshots/ScreenshotRenderer.java b/src/main/java/com/replaymod/extras/advancedscreenshots/ScreenshotRenderer.java index e5b292e3..0f38c79d 100644 --- a/src/main/java/com/replaymod/extras/advancedscreenshots/ScreenshotRenderer.java +++ b/src/main/java/com/replaymod/extras/advancedscreenshots/ScreenshotRenderer.java @@ -4,22 +4,15 @@ import com.replaymod.core.versions.MCVer; import com.replaymod.render.RenderSettings; import com.replaymod.render.blend.BlendState; import com.replaymod.render.capturer.RenderInfo; +import com.replaymod.render.hooks.ForceChunkLoadingHook; import com.replaymod.render.rendering.Pipelines; import de.johni0702.minecraft.gui.utils.lwjgl.Dimension; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; import net.minecraft.client.MinecraftClient; +import net.minecraft.client.util.Window; import net.minecraft.util.crash.CrashReport; -//#if MC>=11400 -import com.replaymod.render.mixin.MainWindowAccessor; -import static com.replaymod.core.versions.MCVer.getWindow; -//#endif - -//#if MC>=10800 -import com.replaymod.render.hooks.ChunkLoadingRenderGlobal; -//#endif - -import static com.replaymod.core.versions.MCVer.getRenderPartialTicks; +import static com.replaymod.core.versions.MCVer.resizeMainWindow; public class ScreenshotRenderer implements RenderInfo { @@ -35,20 +28,13 @@ public class ScreenshotRenderer implements RenderInfo { public boolean renderScreenshot() throws Throwable { try { - //#if MC>=11400 - int displayWidthBefore = getWindow(mc).getFramebufferWidth(); - int displayHeightBefore = getWindow(mc).getFramebufferHeight(); - //#else - //$$ int displayWidthBefore = mc.displayWidth; - //$$ int displayHeightBefore = mc.displayHeight; - //#endif - + Window window = mc.getWindow(); + int widthBefore = window.getFramebufferWidth(); + int heightBefore = window.getFramebufferHeight(); boolean hideGUIBefore = mc.options.hudHidden; mc.options.hudHidden = true; - //#if MC>=10800 - ChunkLoadingRenderGlobal clrg = new ChunkLoadingRenderGlobal(mc.worldRenderer); - //#endif + ForceChunkLoadingHook clrg = new ForceChunkLoadingHook(mc.worldRenderer); if (settings.getRenderMethod() == RenderSettings.RenderMethod.BLEND) { BlendState.setState(new BlendState(settings.getOutputFile())); @@ -58,27 +44,10 @@ public class ScreenshotRenderer implements RenderInfo { new ScreenshotWriter(settings.getOutputFile())).run(); } - //#if MC>=10800 clrg.uninstall(); - //#endif mc.options.hudHidden = hideGUIBefore; - //#if MC>=11400 - //noinspection ConstantConditions - MainWindowAccessor acc = (MainWindowAccessor) (Object) getWindow(mc); - acc.setFramebufferWidth(displayWidthBefore); - acc.setFramebufferHeight(displayHeightBefore); - mc.getFramebuffer().resize(displayWidthBefore, displayHeightBefore - //#if MC>=11400 - , false - //#endif - ); - //#if MC>=11500 - mc.gameRenderer.onResized(displayWidthBefore, displayHeightBefore); - //#endif - //#else - //$$ mc.resize(displayWidthBefore, displayHeightBefore); - //#endif + resizeMainWindow(mc, widthBefore, heightBefore); return true; } catch (OutOfMemoryError e) { e.printStackTrace(); @@ -107,7 +76,7 @@ public class ScreenshotRenderer implements RenderInfo { @Override public float updateForNextFrame() { framesDone++; - return getRenderPartialTicks(); + return mc.getTickDelta(); } @Override diff --git a/src/main/java/com/replaymod/extras/modcore/ModCoreInstaller.java b/src/main/java/com/replaymod/extras/modcore/ModCoreInstaller.java deleted file mode 100644 index 40615608..00000000 --- a/src/main/java/com/replaymod/extras/modcore/ModCoreInstaller.java +++ /dev/null @@ -1,485 +0,0 @@ -/* Based on https://github.com/Sk1erLLC/ModCore-Example-Mod/blob/4a362fa1c67cee28bdfc82bd5a99a30896b081fb/src/main/java/club/sk1er/mods/modcoreexample/ModCoreInstaller.java - * - * MIT License - * - * Copyright (c) 2020 Sk1er LLC - * Copyright (c) 2020 ModCore Inc. - * - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ -package com.replaymod.extras.modcore; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import org.apache.commons.io.FileUtils; -import org.apache.commons.io.IOUtils; - -import javax.swing.JFrame; -import javax.swing.JProgressBar; -import java.awt.Dimension; -import java.awt.Font; -import java.awt.GridLayout; -import java.awt.TextArea; -import java.awt.Toolkit; -import java.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.net.HttpURLConnection; -import java.net.URL; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; - -//#if FABRIC>=1 -import net.fabricmc.loader.api.FabricLoader; -//#else -//#if MC>=11400 -//#else -//$$ import net.minecraft.launchwrapper.Launch; -//$$ import java.net.URLClassLoader; -//$$ import java.util.LinkedHashSet; -//#endif -//#endif - -public class ModCoreInstaller { - - private static final String VERSION_URL = "https://api.sk1er.club/modcore_versions"; - private static final String className = "club.sk1er.mods.core.tweaker.ModCoreTweaker"; - private static boolean errored = false; - private static String error; - private static File dataDir = null; - - - private static boolean isInitalized() { - try { - //#if MC<11400 - //$$ LinkedHashSet objects = new LinkedHashSet<>(); - //$$ objects.add(className); - //$$ Launch.classLoader.clearNegativeEntries(objects); - //#endif - return Class.forName(className) != null; - } catch (ClassNotFoundException ignored) { - } - return false; - } - - public static boolean isErrored() { - return errored; - } - - public static String getError() { - return error; - } - - private static void bail(String error) { - errored = true; - ModCoreInstaller.error = error; - } - - private static JsonHolder readFile(File in) { - try { - return new JsonHolder(FileUtils.readFileToString(in)); - } catch (IOException ignored) { - - } - return new JsonHolder(); - } - - private static boolean initializeModCore(File gameDir) { - try { - Class modCore = Class.forName("club.sk1er.mods.core.tweaker.ModCoreTweaker"); - Method initialize = modCore.getMethod("initialize", File.class); - initialize.invoke(null, gameDir); - System.out.println("Loaded ModCore Successfully"); - return true; - } catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { - e.printStackTrace(); - } - System.out.println("Did NOT initialize ModCore Successfully"); - return false; - } - - public static int initialize(File gameDir, String minecraftVersion) { - if (isInitalized()) return -1; - JsonHolder jsonHolder = fetchJSON(VERSION_URL); - if (!jsonHolder.has(minecraftVersion)) { - System.out.println("No ModCore target for " + minecraftVersion + ". This in fine, unless you're specifically looking for ModCore."); - return -2; - } - dataDir = new File(gameDir, "modcore"); - if (!dataDir.exists()) { - if (!dataDir.mkdirs()) { - bail("Unable to create necessary files"); - return 1; - } - } - String latestRemote = jsonHolder.optString(minecraftVersion); - boolean failed = jsonHolder.getKeys().size() == 0 || (jsonHolder.has("success") && !jsonHolder.optBoolean("success")); - - File metadataFile = new File(dataDir, "metadata.json"); - JsonHolder localMetadata = readFile(metadataFile); - if (failed) latestRemote = localMetadata.optString(minecraftVersion); - File modcoreFile = new File(dataDir, "Sk1er Modcore-" + latestRemote + " (" + minecraftVersion + ").jar"); - - if (!modcoreFile.exists() || !localMetadata.optString(minecraftVersion).equalsIgnoreCase(latestRemote) && !failed) { - //File does not exist, or is out of date, download it - File old = new File(dataDir, "Sk1er Modcore-" + localMetadata.optString(minecraftVersion) + " (" + minecraftVersion + ").jar"); - if (old.exists()) old.delete(); - - if (!download("https://static.sk1er.club/repo/mods/modcore/" + latestRemote + "/" + minecraftVersion + "/ModCore-" + latestRemote + " (" + minecraftVersion + ").jar", latestRemote, modcoreFile, minecraftVersion, localMetadata)) { - bail("Unable to download"); - return 2; - } - - } - - addToClasspath(modcoreFile); - - if (!isInitalized()) { - bail("Something went wrong and it did not add the jar to the class path. Local file exists? " + modcoreFile.exists()); - return 3; - } - return initializeModCore(gameDir) ? 0 : 4; - - } - - - public static void addToClasspath(File file) { - try { - URL url = file.toURI().toURL(); - ClassLoader classLoader = ModCoreInstaller.class.getClassLoader(); - //#if MC>=11400 - Method method = classLoader.getClass().getDeclaredMethod("addURL", URL.class); - //#else - //$$ Launch.classLoader.addURL(url); - //$$ Method method = URLClassLoader.class.getDeclaredMethod("addURL", URL.class); - //#endif - method.setAccessible(true); - method.invoke(classLoader, url); - } catch (Exception e) { - throw new RuntimeException("Unexpected exception", e); - } - } - - private static boolean download(String url, String version, File file, String mcver, JsonHolder versionData) { - url = url.replace(" ", "%20"); - System.out.println("Downloading ModCore " + " version " + version + " from: " + url); - JFrame frame = new JFrame("ModCore Initializer"); - JProgressBar bar = new JProgressBar(); - TextArea comp = new TextArea("", 1, 1, TextArea.SCROLLBARS_NONE); - frame.getContentPane().add(comp); - frame.getContentPane().add(bar); - GridLayout manager = new GridLayout(); - frame.setLayout(manager); - manager.setColumns(1); - manager.setRows(2); - comp.setText("Downloading Sk1er ModCore Library Version " + version + " for Minecraft " + mcver); - comp.setSize(399, 80); - comp.setEditable(false); - Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); - - - Dimension preferredSize = new Dimension(400, 225); - bar.setSize(preferredSize); - frame.setSize(preferredSize); - frame.setResizable(false); - bar.setBorderPainted(true); - bar.setMinimum(0); - bar.setStringPainted(true); - frame.setVisible(true); - frame.setLocation(dim.width / 2 - frame.getSize().width / 2, dim.height / 2 - frame.getSize().height / 2); - Font font = bar.getFont(); - bar.setFont(new Font(font.getName(), font.getStyle(), font.getSize() * 4)); - comp.setFont(new Font(font.getName(), font.getStyle(), font.getSize() * 2)); - - try { - - URL u = new URL(url); - HttpURLConnection connection = (HttpURLConnection) u.openConnection(); - connection.setRequestMethod("GET"); - connection.setUseCaches(true); - connection.addRequestProperty("User-Agent", "Mozilla/4.76 (Sk1er Modcore Initializer)"); - connection.setReadTimeout(15000); - connection.setConnectTimeout(15000); - connection.setDoOutput(true); - InputStream is = connection.getInputStream(); - int contentLength = connection.getContentLength(); - FileOutputStream outputStream = new FileOutputStream(file); - byte[] buffer = new byte[1024]; - System.out.println("MAX: " + contentLength); - bar.setMaximum(contentLength); - int read; - bar.setValue(0); - while ((read = is.read(buffer)) > 0) { - outputStream.write(buffer, 0, read); - bar.setValue(bar.getValue() + 1024); - } - outputStream.close(); - FileUtils.write(new File(dataDir, "metadata.json"), versionData.put(mcver, version).toString()); - } catch (Exception e) { - e.printStackTrace(); - frame.dispose(); - return false; - } - frame.dispose(); - return true; - } - - public static final JsonHolder fetchJSON(String url) { - return new JsonHolder(fetchString(url)); - } - - public static final String fetchString(String url) { - url = url.replace(" ", "%20"); - System.out.println("Fetching " + url); - try { - URL u = new URL(url); - HttpURLConnection connection = (HttpURLConnection) u.openConnection(); - connection.setRequestMethod("GET"); - connection.setUseCaches(true); - connection.addRequestProperty("User-Agent", "Mozilla/4.76 (Sk1er ModCore)"); - connection.setReadTimeout(15000); - connection.setConnectTimeout(15000); - connection.setDoOutput(true); - InputStream is = connection.getInputStream(); - return IOUtils.toString(is, Charset.defaultCharset()); - } catch (Exception e) { - e.printStackTrace(); - } - return "Failed to fetch"; - } - - - //Added because we need to use before ModCore is loaded - static class JsonHolder { - private JsonObject object; - - public JsonHolder(JsonObject object) { - this.object = object; - } - - public JsonHolder(String raw) { - if (raw == null) - object = new JsonObject(); - else - try { - this.object = new JsonParser().parse(raw).getAsJsonObject(); - } catch (Exception e) { - this.object = new JsonObject(); - e.printStackTrace(); - } - } - - public JsonHolder() { - this(new JsonObject()); - } - - @Override - public String toString() { - if (object != null) - return object.toString(); - return "{}"; - } - - public JsonHolder put(String key, boolean value) { - object.addProperty(key, value); - return this; - } - - public void mergeNotOverride(JsonHolder merge) { - merge(merge, false); - } - - public void mergeOverride(JsonHolder merge) { - merge(merge, true); - } - - public void merge(JsonHolder merge, boolean override) { - JsonObject object = merge.getObject(); - for (String s : merge.getKeys()) { - if (override || !this.has(s)) - put(s, object.get(s)); - } - } - - private void put(String s, JsonElement element) { - this.object.add(s, element); - } - - public JsonHolder put(String key, String value) { - object.addProperty(key, value); - return this; - } - - public JsonHolder put(String key, int value) { - object.addProperty(key, value); - return this; - } - - public JsonHolder put(String key, double value) { - object.addProperty(key, value); - return this; - } - - public JsonHolder put(String key, long value) { - object.addProperty(key, value); - return this; - } - - private JsonHolder defaultOptJSONObject(String key, JsonObject fallBack) { - try { - return new JsonHolder(object.get(key).getAsJsonObject()); - } catch (Exception e) { - return new JsonHolder(fallBack); - } - } - - public JsonArray defaultOptJSONArray(String key, JsonArray fallback) { - try { - return object.get(key).getAsJsonArray(); - } catch (Exception e) { - return fallback; - } - } - - public JsonArray optJSONArray(String key) { - return defaultOptJSONArray(key, new JsonArray()); - } - - - public boolean has(String key) { - return object.has(key); - } - - public long optLong(String key, long fallback) { - try { - return object.get(key).getAsLong(); - } catch (Exception e) { - return fallback; - } - } - - public long optLong(String key) { - return optLong(key, 0); - } - - public boolean optBoolean(String key, boolean fallback) { - try { - return object.get(key).getAsBoolean(); - } catch (Exception e) { - return fallback; - } - } - - public boolean optBoolean(String key) { - return optBoolean(key, false); - } - - public JsonObject optActualJSONObject(String key) { - try { - return object.get(key).getAsJsonObject(); - } catch (Exception e) { - return new JsonObject(); - } - } - - public JsonHolder optJSONObject(String key) { - return defaultOptJSONObject(key, new JsonObject()); - } - - - public int optInt(String key, int fallBack) { - try { - return object.get(key).getAsInt(); - } catch (Exception e) { - return fallBack; - } - } - - public int optInt(String key) { - return optInt(key, 0); - } - - - public String defaultOptString(String key, String fallBack) { - try { - return object.get(key).getAsString(); - } catch (Exception e) { - return fallBack; - } - } - - public String optString(String key) { - return defaultOptString(key, ""); - } - - - public double optDouble(String key, double fallBack) { - try { - return object.get(key).getAsDouble(); - } catch (Exception e) { - return fallBack; - } - } - - public List getKeys() { - List tmp = new ArrayList<>(); - object.entrySet().forEach(e -> tmp.add(e.getKey())); - return tmp; - } - - public double optDouble(String key) { - return optDouble(key, 0.0); - } - - - public JsonObject getObject() { - return object; - } - - public boolean isNull(String key) { - return object.has(key) && object.get(key).isJsonNull(); - } - - public JsonHolder put(String values, JsonHolder values1) { - return put(values, values1.getObject()); - } - - public JsonHolder put(String values, JsonObject object) { - this.object.add(values, object); - return this; - } - - public void put(String blacklisted, JsonArray jsonElements) { - this.object.add(blacklisted, jsonElements); - } - - public void remove(String header) { - object.remove(header); - } - } - - -} - diff --git a/src/main/java/com/replaymod/extras/playeroverview/PlayerOverview.java b/src/main/java/com/replaymod/extras/playeroverview/PlayerOverview.java index 0cbbbf8c..d5ecb0be 100644 --- a/src/main/java/com/replaymod/extras/playeroverview/PlayerOverview.java +++ b/src/main/java/com/replaymod/extras/playeroverview/PlayerOverview.java @@ -2,6 +2,7 @@ package com.replaymod.extras.playeroverview; import com.google.common.base.Optional; import com.replaymod.core.ReplayMod; +import com.replaymod.core.events.PreRenderHandCallback; import com.replaymod.core.utils.Utils; import com.replaymod.extras.Extra; import com.replaymod.replay.ReplayHandler; @@ -14,23 +15,11 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; //#if MC>=11400 -import com.replaymod.core.events.PreRenderHandCallback; import java.util.stream.Collectors; //#else -//$$ import net.minecraftforge.client.event.RenderHandEvent; -//$$ -//#if MC>=11400 -//#else //$$ import org.lwjgl.input.Keyboard; -//#endif -//$$ //#if MC>=10800 //$$ import com.google.common.base.Predicate; -//#if MC>=11400 -//$$ import net.minecraftforge.eventbus.api.SubscribeEvent; -//#else -//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -//#endif //#else //$$ import cpw.mods.fml.common.eventhandler.EventPriority; //$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; @@ -126,18 +115,9 @@ public class PlayerOverview extends EventRegistrations implements Extra { hiddenPlayers.clear(); } - //#if MC>=11400 { on(PreRenderHandCallback.EVENT, this::shouldHideHand); } - //#else - //$$ @SubscribeEvent - //$$ public void oRenderHand(RenderHandEvent event) { - //$$ if (shouldHideHand()) { - //$$ event.setCanceled(true); - //$$ } - //$$ } - //#endif private boolean shouldHideHand() { - Entity view = getRenderViewEntity(module.getCore().getMinecraft()); + Entity view = module.getCore().getMinecraft().getCameraEntity(); return view != null && isHidden(view.getUuid()); } diff --git a/src/main/java/com/replaymod/extras/playeroverview/PlayerOverviewGui.java b/src/main/java/com/replaymod/extras/playeroverview/PlayerOverviewGui.java index 01824f4f..97e0d20f 100644 --- a/src/main/java/com/replaymod/extras/playeroverview/PlayerOverviewGui.java +++ b/src/main/java/com/replaymod/extras/playeroverview/PlayerOverviewGui.java @@ -53,13 +53,13 @@ public class PlayerOverviewGui extends GuiScreen implements Closeable { public final GuiCheckbox checkAll = new GuiCheckbox(contentPanel){ @Override public void onClick() { - playersScrollable.forEach(IGuiCheckbox.class).setChecked(true); + playersScrollable.invokeAll(IGuiCheckbox.class, e -> e.setChecked(true)); } }.setLabel("").setChecked(true).setTooltip(new GuiTooltip().setI18nText("replaymod.gui.playeroverview.showall")); public final GuiCheckbox uncheckAll = new GuiCheckbox(contentPanel){ @Override public void onClick() { - playersScrollable.forEach(IGuiCheckbox.class).setChecked(false); + playersScrollable.invokeAll(IGuiCheckbox.class, e -> e.setChecked(false)); } }.setLabel("").setChecked(false).setTooltip(new GuiTooltip().setI18nText("replaymod.gui.playeroverview.hideall")); diff --git a/src/main/java/com/replaymod/extras/youtube/GuiYoutubeUpload.java b/src/main/java/com/replaymod/extras/youtube/GuiYoutubeUpload.java index e3097841..6c1358ef 100644 --- a/src/main/java/com/replaymod/extras/youtube/GuiYoutubeUpload.java +++ b/src/main/java/com/replaymod/extras/youtube/GuiYoutubeUpload.java @@ -31,7 +31,6 @@ import net.minecraft.util.Util; //$$ import org.lwjgl.Sys; //#endif -import javax.annotation.Nullable; import javax.imageio.ImageIO; import javax.imageio.ImageReader; import javax.imageio.stream.ImageInputStream; @@ -116,36 +115,27 @@ public class GuiYoutubeUpload extends GuiScreen { public final GuiButton thumbnailButton = new GuiButton().onClick(new Runnable() { @Override public void run() { - Futures.addCallback( - GuiFileChooserPopup.openLoadGui(GuiYoutubeUpload.this, "replaymod.gui.load", - ImageIO.getReaderFileSuffixes()).getFuture(), - new FutureCallback() { - @Override - public void onSuccess(@Nullable File result) { - if (result != null) { - thumbnailButton.setLabel(result.getName()); - Image image; - try { - thumbnailImage = IOUtils.toByteArray(new FileInputStream(result)); - ImageInputStream in = ImageIO.createImageInputStream(new ByteArrayInputStream(thumbnailImage)); - ImageReader reader = ImageIO.getImageReaders(in).next(); - thumbnailFormat = reader.getFormatName().toLowerCase(); - image = Image.read(new ByteArrayInputStream(thumbnailImage)); - } catch (Throwable t) { - t.printStackTrace(); - thumbnailImage = null; - image = null; - } - thumbnail.setTexture(image); - inputValidation.run(); - } - } - - @Override - public void onFailure(Throwable t) { - throw new RuntimeException(t); - } - }); + GuiFileChooserPopup.openLoadGui( + GuiYoutubeUpload.this, + "replaymod.gui.load", + ImageIO.getReaderFileSuffixes() + ).onAccept(file -> { + thumbnailButton.setLabel(file.getName()); + Image image; + try { + thumbnailImage = IOUtils.toByteArray(new FileInputStream(file)); + ImageInputStream in = ImageIO.createImageInputStream(new ByteArrayInputStream(thumbnailImage)); + ImageReader reader = ImageIO.getImageReaders(in).next(); + thumbnailFormat = reader.getFormatName().toLowerCase(); + image = Image.read(new ByteArrayInputStream(thumbnailImage)); + } catch (Throwable t) { + t.printStackTrace(); + thumbnailImage = null; + image = null; + } + thumbnail.setTexture(image); + inputValidation.run(); + }); } }).setSize(200, 20).setI18nLabel("replaymod.gui.videothumbnail"); @@ -191,7 +181,7 @@ public class GuiYoutubeUpload extends GuiScreen { } private void setState(boolean uploading) { - forEach(GuiElement.class).setEnabled(!uploading); + invokeAll(GuiElement.class, e -> e.setEnabled(!uploading)); uploadButton.setEnabled(); if (uploading) { uploadButton.onClick(() -> { diff --git a/src/main/java/com/replaymod/extras/youtube/YoutubeUpload.java b/src/main/java/com/replaymod/extras/youtube/YoutubeUpload.java index 5fde32a5..fa284bc0 100644 --- a/src/main/java/com/replaymod/extras/youtube/YoutubeUpload.java +++ b/src/main/java/com/replaymod/extras/youtube/YoutubeUpload.java @@ -3,19 +3,11 @@ package com.replaymod.extras.youtube; import com.replaymod.core.ReplayMod; import com.replaymod.extras.Extra; import com.replaymod.render.gui.GuiRenderingDone; -import de.johni0702.minecraft.gui.container.GuiScreen; +import de.johni0702.minecraft.gui.container.AbstractGuiScreen; import de.johni0702.minecraft.gui.element.GuiButton; import de.johni0702.minecraft.gui.utils.EventRegistrations; - -//#if FABRIC>=1 -import de.johni0702.minecraft.gui.versions.callbacks.OpenGuiScreenCallback; +import de.johni0702.minecraft.gui.versions.callbacks.InitScreenCallback; import net.minecraft.client.gui.screen.Screen; -//#else -//$$ import net.minecraftforge.client.event.GuiScreenEvent; -//$$ import net.minecraftforge.eventbus.api.SubscribeEvent; -//#endif - -import static com.replaymod.core.versions.MCVer.*; public class YoutubeUpload extends EventRegistrations implements Extra { @Override @@ -23,16 +15,11 @@ public class YoutubeUpload extends EventRegistrations implements Extra { register(); } - //#if FABRIC>=1 - { on(OpenGuiScreenCallback.EVENT, this::onGuiOpen); } + { on(InitScreenCallback.EVENT, ((screen, buttons) -> onGuiOpen(screen))); } private void onGuiOpen(Screen vanillaGui) { - //#else - //$$ @SubscribeEvent - //$$ public void onGuiOpen(GuiScreenEvent.InitGuiEvent.Post event) { - //$$ net.minecraft.client.gui.screen.Screen vanillaGui = getGui(event); - //#endif - if (GuiScreen.from(vanillaGui) instanceof GuiRenderingDone) { - GuiRenderingDone gui = (GuiRenderingDone) GuiScreen.from(vanillaGui); + AbstractGuiScreen abstractScreen = de.johni0702.minecraft.gui.container.GuiScreen.from(vanillaGui); + if (abstractScreen instanceof GuiRenderingDone) { + GuiRenderingDone gui = (GuiRenderingDone) abstractScreen; // Check if there already is a youtube button if (gui.actionsPanel.getChildren().stream().anyMatch(it -> it instanceof YoutubeButton)) { return; // Button already added diff --git a/src/main/java/com/replaymod/gradle/remap/Pattern.java b/src/main/java/com/replaymod/gradle/remap/Pattern.java new file mode 100644 index 00000000..c57f51a3 --- /dev/null +++ b/src/main/java/com/replaymod/gradle/remap/Pattern.java @@ -0,0 +1,11 @@ +package com.replaymod.gradle.remap; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Retention(RetentionPolicy.SOURCE) +@Target(ElementType.METHOD) +public @interface Pattern { +} diff --git a/src/main/java/com/replaymod/pathing/gui/GuiKeyframeRepository.java b/src/main/java/com/replaymod/pathing/gui/GuiKeyframeRepository.java index 34760873..6ac40e61 100644 --- a/src/main/java/com/replaymod/pathing/gui/GuiKeyframeRepository.java +++ b/src/main/java/com/replaymod/pathing/gui/GuiKeyframeRepository.java @@ -65,18 +65,15 @@ public class GuiKeyframeRepository extends GuiScreen implements Closeable, Typea public final GuiButton overwriteButton = new GuiButton().onClick(new Runnable() { @Override public void run() { - GuiYesNoPopup popup = GuiYesNoPopup.open(GuiKeyframeRepository.this, + GuiYesNoPopup.open(GuiKeyframeRepository.this, new GuiLabel().setI18nText("replaymod.gui.keyframerepo.overwrite").setColor(Colors.BLACK) - ).setYesI18nLabel("gui.yes").setNoI18nLabel("gui.no"); - Utils.addCallback(popup.getFuture(), doIt -> { - if (doIt) { - for (Entry entry : selectedEntries) { - timelines.put(entry.name, currentTimeline); - } - overwriteButton.setDisabled(); - save(); + ).setYesI18nLabel("gui.yes").setNoI18nLabel("gui.no").onAccept(() -> { + for (Entry entry : selectedEntries) { + timelines.put(entry.name, currentTimeline); } - }, Throwable::printStackTrace); + overwriteButton.setDisabled(); + save(); + }); } }).setSize(75, 20).setI18nLabel("replaymod.gui.overwrite").setDisabled(); public final GuiButton saveAsButton = new GuiButton().onClick(new Runnable() { @@ -103,21 +100,11 @@ public class GuiKeyframeRepository extends GuiScreen implements Closeable, Typea && !timelines.containsKey(nameField.getText())); } }); - Futures.addCallback(popup.getFuture(), new FutureCallback() { - @Override - public void onSuccess(Boolean save) { - if (save) { - String name = nameField.getText(); - timelines.put(name, currentTimeline); - list.getListPanel().addElements(null, new Entry(name)); - save(); - } - } - - @Override - public void onFailure(Throwable t) { - t.printStackTrace(); - } + popup.onAccept(() -> { + String name = nameField.getText(); + timelines.put(name, currentTimeline); + list.getListPanel().addElements(null, new Entry(name)); + save(); }); } }).setSize(75, 20).setI18nLabel("replaymod.gui.saveas"); @@ -161,50 +148,29 @@ public class GuiKeyframeRepository extends GuiScreen implements Closeable, Typea && !timelines.containsKey(nameField.getText())); } }); - Futures.addCallback(popup.getFuture(), new FutureCallback() { - @Override - public void onSuccess(Boolean save) { - if (save) { - String name = nameField.getText(); - timelines.put(name, timelines.remove(selectedEntry.name)); - selectedEntry.name = name; - selectedEntry.label.setText(name); - save(); - } - } - - @Override - public void onFailure(Throwable t) { - t.printStackTrace(); - } + popup.onAccept(() -> { + String name = nameField.getText(); + timelines.put(name, timelines.remove(selectedEntry.name)); + selectedEntry.name = name; + selectedEntry.label.setText(name); + save(); }); } }).setSize(75, 20).setI18nLabel("replaymod.gui.rename").setDisabled(); public final GuiButton removeButton = new GuiButton().onClick(new Runnable() { @Override public void run() { - GuiYesNoPopup popup = GuiYesNoPopup.open(GuiKeyframeRepository.this, + GuiYesNoPopup.open(GuiKeyframeRepository.this, new GuiLabel().setI18nText("replaymod.gui.keyframerepo.delete").setColor(Colors.BLACK) - ).setYesI18nLabel("replaymod.gui.delete").setNoI18nLabel("replaymod.gui.cancel"); - Futures.addCallback(popup.getFuture(), new FutureCallback() { - @Override - public void onSuccess(Boolean delete) { - if (delete) { - for (Entry entry : selectedEntries) { - timelines.remove(entry.name); - list.getListPanel().removeElement(entry); - } - - selectedEntries.clear(); - updateButtons(); - save(); - } + ).setYesI18nLabel("replaymod.gui.delete").setNoI18nLabel("replaymod.gui.cancel").onAccept(() -> { + for (Entry entry : selectedEntries) { + timelines.remove(entry.name); + list.getListPanel().removeElement(entry); } - @Override - public void onFailure(Throwable t) { - t.printStackTrace(); - } + selectedEntries.clear(); + updateButtons(); + save(); }); } }).setSize(75, 20).setI18nLabel("replaymod.gui.remove").setDisabled(); diff --git a/src/main/java/com/replaymod/recording/gui/GuiRecordingControls.java b/src/main/java/com/replaymod/recording/gui/GuiRecordingControls.java index df97260f..e766f0a5 100644 --- a/src/main/java/com/replaymod/recording/gui/GuiRecordingControls.java +++ b/src/main/java/com/replaymod/recording/gui/GuiRecordingControls.java @@ -4,7 +4,6 @@ import com.replaymod.core.ReplayMod; import com.replaymod.core.utils.Utils; import com.replaymod.core.versions.MCVer; import com.replaymod.editor.gui.MarkerProcessor; -import com.replaymod.recording.Setting; import com.replaymod.recording.packet.PacketListener; import de.johni0702.minecraft.gui.container.GuiPanel; import de.johni0702.minecraft.gui.container.VanillaGuiScreen; @@ -12,23 +11,15 @@ import de.johni0702.minecraft.gui.element.GuiButton; import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.utils.EventRegistrations; +import de.johni0702.minecraft.gui.versions.callbacks.InitScreenCallback; import net.minecraft.client.gui.screen.GameMenuScreen; import net.minecraft.client.gui.screen.Screen; -//#if FABRIC>=1 -import de.johni0702.minecraft.gui.versions.callbacks.InitScreenCallback; -//#else -//$$ import net.minecraftforge.client.event.GuiScreenEvent; -//$$ import net.minecraftforge.common.MinecraftForge; -//$$ import net.minecraftforge.eventbus.api.SubscribeEvent; -//$$ -//$$ import static com.replaymod.core.versions.MCVer.getGui; -//#endif - //#if MC>=11400 import net.minecraft.client.gui.widget.AbstractButtonWidget; //#endif +import java.util.Collection; import java.util.List; import java.util.Optional; import java.util.function.Function; @@ -85,19 +76,14 @@ public class GuiRecordingControls extends EventRegistrations { buttonPauseResume.setEnabled(!stopped); } - //#if FABRIC>=1 { on(InitScreenCallback.EVENT, this::injectIntoIngameMenu); } - private void injectIntoIngameMenu(Screen guiScreen, List buttonList) { - //#else - //$$ @SubscribeEvent - //$$ public void injectIntoIngameMenu(GuiScreenEvent.InitGuiEvent.Post event) { - //$$ Screen guiScreen = getGui(event); - //#if MC>=11400 - //$$ List buttonList = MCVer.getButtonList(event); - //#else - //$$ List buttonList = MCVer.getButtonList(event); - //#endif - //#endif + private void injectIntoIngameMenu(Screen guiScreen, + //#if MC>=11400 + Collection buttonList + //#else + //$$ Collection buttonList + //#endif + ) { if (!(guiScreen instanceof GameMenuScreen)) { return; } diff --git a/src/main/java/com/replaymod/recording/gui/GuiRecordingOverlay.java b/src/main/java/com/replaymod/recording/gui/GuiRecordingOverlay.java index f2e1b73f..ab9ee2e6 100644 --- a/src/main/java/com/replaymod/recording/gui/GuiRecordingOverlay.java +++ b/src/main/java/com/replaymod/recording/gui/GuiRecordingOverlay.java @@ -1,27 +1,18 @@ package com.replaymod.recording.gui; import com.replaymod.core.SettingsRegistry; -import com.replaymod.core.versions.MCVer; import com.replaymod.recording.Setting; import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.MinecraftGuiRenderer; import de.johni0702.minecraft.gui.utils.EventRegistrations; +import de.johni0702.minecraft.gui.versions.callbacks.RenderHudCallback; import net.minecraft.client.MinecraftClient; import net.minecraft.client.font.TextRenderer; import net.minecraft.client.resource.language.I18n; import net.minecraft.client.util.math.MatrixStack; -//#if FABRIC>=1 -import de.johni0702.minecraft.gui.versions.callbacks.RenderHudCallback; -//#else -//$$ import net.minecraftforge.client.event.RenderGameOverlayEvent; -//$$ import net.minecraftforge.common.MinecraftForge; -//$$ import net.minecraftforge.eventbus.api.SubscribeEvent; -//#endif - import static com.replaymod.core.ReplayMod.TEXTURE; import static com.replaymod.core.ReplayMod.TEXTURE_SIZE; -import static com.replaymod.core.versions.MCVer.*; import static com.mojang.blaze3d.platform.GlStateManager.*; /** @@ -41,27 +32,18 @@ public class GuiRecordingOverlay extends EventRegistrations { /** * Render the recording icon and text in the top left corner of the screen. */ - //#if FABRIC>=1 { on(RenderHudCallback.EVENT, (stack, partialTicks) -> renderRecordingIndicator(stack)); } private void renderRecordingIndicator(MatrixStack stack) { - //#else - //$$ @SubscribeEvent - //$$ public void renderRecordingIndicator(RenderGameOverlayEvent.Post event) { - //$$ if (getType(event) != RenderGameOverlayEvent.ElementType.ALL) return; - //$$ MatrixStack stack = new MatrixStack(); - //#endif if (guiControls.isStopped()) return; if (settingsRegistry.get(Setting.INDICATOR)) { TextRenderer fontRenderer = mc.textRenderer; String text = guiControls.isPaused() ? I18n.translate("replaymod.gui.paused") : I18n.translate("replaymod.gui.recording"); - fontRenderer.draw( - //#if MC>=11600 - stack, - //#endif - text.toUpperCase(), 30, 18 - (fontRenderer.fontHeight / 2), 0xffffffff); - bindTexture(TEXTURE); + MinecraftGuiRenderer renderer = new MinecraftGuiRenderer(stack); + renderer.drawString(30, 18 - (fontRenderer.fontHeight / 2), 0xffffffff, text.toUpperCase()); + renderer.bindTexture(TEXTURE); + //#if MC<11700 enableAlphaTest(); - GuiRenderer renderer = new MinecraftGuiRenderer(stack, MCVer.newScaledResolution(mc)); + //#endif renderer.drawTexturedRect(10, 10, 58, 20, 16, 16, 16, 16, TEXTURE_SIZE, TEXTURE_SIZE); } } diff --git a/src/main/java/com/replaymod/recording/gui/GuiSavingReplay.java b/src/main/java/com/replaymod/recording/gui/GuiSavingReplay.java index 7f1a1238..5ca75e7e 100644 --- a/src/main/java/com/replaymod/recording/gui/GuiSavingReplay.java +++ b/src/main/java/com/replaymod/recording/gui/GuiSavingReplay.java @@ -5,7 +5,6 @@ import com.replaymod.core.utils.Utils; import com.replaymod.recording.Setting; import com.replaymod.replay.gui.screen.GuiReplayViewer; import com.replaymod.replaystudio.replay.ReplayMetaData; -import com.replaymod.replaystudio.us.myles.ViaVersion.api.Pair; import de.johni0702.minecraft.gui.container.AbstractGuiScreen; import de.johni0702.minecraft.gui.container.GuiContainer; import de.johni0702.minecraft.gui.container.GuiPanel; @@ -25,6 +24,7 @@ import de.johni0702.minecraft.gui.utils.lwjgl.Dimension; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; import net.minecraft.client.MinecraftClient; import net.minecraft.util.crash.CrashReport; +import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; diff --git a/src/main/java/com/replaymod/recording/handler/ConnectionEventHandler.java b/src/main/java/com/replaymod/recording/handler/ConnectionEventHandler.java index 9707020a..0e90c7eb 100644 --- a/src/main/java/com/replaymod/recording/handler/ConnectionEventHandler.java +++ b/src/main/java/com/replaymod/recording/handler/ConnectionEventHandler.java @@ -3,6 +3,7 @@ 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; @@ -137,7 +138,7 @@ public class ConnectionEventHandler { metaData.setCustomServerName(serverName); metaData.setGenerator("ReplayMod v" + ReplayMod.instance.getVersion()); metaData.setDate(System.currentTimeMillis()); - metaData.setMcVersion(ReplayMod.getMinecraftVersion()); + metaData.setMcVersion(ReplayMod.instance.getMinecraftVersion()); packetListener = new PacketListener(core, outputPath, replayFile, metaData); Channel channel = ((NetworkManagerAccessor) networkManager).getChannel(); channel.pipeline().addBefore(packetHandlerKey, "replay_recorder", packetListener); diff --git a/src/main/java/com/replaymod/recording/handler/FMLHandshakeFilter.java b/src/main/java/com/replaymod/recording/handler/FMLHandshakeFilter.java index f280ca13..fca4cd6c 100644 --- a/src/main/java/com/replaymod/recording/handler/FMLHandshakeFilter.java +++ b/src/main/java/com/replaymod/recording/handler/FMLHandshakeFilter.java @@ -1,31 +1 @@ -//#if MC<11400 -//$$ package com.replaymod.recording.handler; -//$$ -//$$ import io.netty.channel.ChannelHandlerContext; -//$$ import io.netty.channel.SimpleChannelInboundHandler; -//$$ import net.minecraftforge.fml.common.network.handshake.FMLHandshakeMessage; -//$$ -//$$ /** -//$$ * Filters out all handshake packets that were sent for recording but must -//$$ * not actually be handled. -//$$ * This handler is only present when connected to the integrated server as -//$$ * otherwise all packets must be handled. -//$$ * -//$$ * When in single player, the game state packets must never be handled -//$$ * otherwise wired bugs related to semi-singletons can occur. -//$$ * See https://bugs.replaymod.com/show_bug.cgi?id=44 -//$$ */ -//$$ public class FMLHandshakeFilter extends SimpleChannelInboundHandler { -//$$ @Override -//$$ protected void channelRead0(ChannelHandlerContext ctx, FMLHandshakeMessage msg) throws Exception { - //#if MC>=10800 - //$$ if (!(msg instanceof FMLHandshakeMessage.RegistryData)) { - //#else - //$$ if (!(msg instanceof FMLHandshakeMessage.ModIdData)) { - //#endif -//$$ // Pass on everything but RegistryData messages -//$$ ctx.fireChannelRead(msg); -//$$ } -//$$ } -//$$ } -//#endif +// 1.12.2 and below diff --git a/src/main/java/com/replaymod/recording/handler/GuiHandler.java b/src/main/java/com/replaymod/recording/handler/GuiHandler.java index ffeb1e87..10f7c920 100644 --- a/src/main/java/com/replaymod/recording/handler/GuiHandler.java +++ b/src/main/java/com/replaymod/recording/handler/GuiHandler.java @@ -14,23 +14,13 @@ import de.johni0702.minecraft.gui.element.GuiToggleButton; import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.popup.GuiInfoPopup; import de.johni0702.minecraft.gui.utils.EventRegistrations; +import de.johni0702.minecraft.gui.versions.callbacks.InitScreenCallback; import net.minecraft.client.gui.screen.AddServerScreen; import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen; import net.minecraft.client.gui.screen.world.SelectWorldScreen; import net.minecraft.client.network.ServerInfo; import net.minecraft.client.resource.language.I18n; -//#if FABRIC>=1 -import de.johni0702.minecraft.gui.versions.callbacks.InitScreenCallback; -import net.minecraft.client.gui.screen.Screen; -//#else -//$$ import net.minecraftforge.client.event.GuiScreenEvent; -//$$ import net.minecraftforge.common.MinecraftForge; -//$$ import net.minecraftforge.eventbus.api.SubscribeEvent; -//$$ -//$$ import static com.replaymod.core.versions.MCVer.getGui; -//#endif - public class GuiHandler extends EventRegistrations { private final ReplayMod mod; @@ -39,14 +29,8 @@ public class GuiHandler extends EventRegistrations { this.mod = mod; } - //#if FABRIC>=1 { on(InitScreenCallback.EVENT, (screen, buttons) -> onGuiInit(screen)); } - private void onGuiInit(Screen gui) { - //#else - //$$ @SubscribeEvent - //$$ public void onGuiInit(GuiScreenEvent.InitGuiEvent.Post event) { - //$$ net.minecraft.client.gui.screen.Screen gui = getGui(event); - //#endif + private void onGuiInit(net.minecraft.client.gui.screen.Screen gui) { if (gui instanceof SelectWorldScreen || gui instanceof MultiplayerScreen) { boolean sp = gui instanceof SelectWorldScreen; SettingsRegistry settingsRegistry = mod.getSettingsRegistry(); diff --git a/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java b/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java index 7894fef8..a04cbee6 100644 --- a/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java +++ b/src/main/java/com/replaymod/recording/handler/RecordingEventHandler.java @@ -1,10 +1,13 @@ package com.replaymod.recording.handler; +import com.replaymod.core.events.PreRenderCallback; import com.replaymod.recording.mixin.IntegratedServerAccessor; import com.replaymod.recording.packet.PacketListener; import de.johni0702.minecraft.gui.utils.EventRegistrations; +import de.johni0702.minecraft.gui.versions.callbacks.PreTickCallback; import net.minecraft.client.MinecraftClient; import net.minecraft.client.network.ClientPlayerEntity; +import net.minecraft.entity.Entity; import net.minecraft.network.packet.s2c.play.BlockBreakingProgressS2CPacket; import net.minecraft.network.packet.s2c.play.EntityAnimationS2CPacket; import net.minecraft.network.packet.s2c.play.EntityAttachS2CPacket; @@ -20,15 +23,11 @@ import net.minecraft.network.Packet; import net.minecraft.server.integrated.IntegratedServer; // FIXME not (yet?) 1.13 import net.minecraftforge.event.entity.minecart.MinecartInteractEvent; -//#if FABRIC>=1 -import com.replaymod.core.events.PreRenderCallback; -import de.johni0702.minecraft.gui.versions.callbacks.PreTickCallback; -//#else +//#if FABRIC<1 //$$ import net.minecraft.network.play.server.SCollectItemPacket; //$$ import net.minecraftforge.event.entity.player.PlayerSleepInBedEvent; //$$ import net.minecraftforge.eventbus.api.SubscribeEvent; //$$ import net.minecraftforge.event.entity.player.PlayerEvent.ItemPickupEvent; -//$$ import net.minecraftforge.event.TickEvent; //#endif //#if MC>=11600 @@ -139,24 +138,18 @@ public class RecordingEventHandler extends EventRegistrations { } //#endif - //#if FABRIC>=1 { on(PreTickCallback.EVENT, this::onPlayerTick); } private void onPlayerTick() { if (mc.player == null) return; - //#else - //$$ @SubscribeEvent - //$$ public void onPlayerTick(TickEvent.ClientTickEvent e) { - //$$ if(e.phase != TickEvent.Phase.START || mc.player == null) return; - //#endif ClientPlayerEntity player = mc.player; try { boolean force = false; if(lastX == null || lastY == null || lastZ == null) { force = true; - lastX = Entity_getX(player); - lastY = Entity_getY(player); - lastZ = Entity_getZ(player); + lastX = player.getX(); + lastY = player.getY(); + lastZ = player.getZ(); } ticksSinceLastCorrection++; @@ -165,13 +158,13 @@ public class RecordingEventHandler extends EventRegistrations { force = true; } - double dx = Entity_getX(player) - lastX; - double dy = Entity_getY(player) - lastY; - double dz = Entity_getZ(player) - lastZ; + double dx = player.getX() - lastX; + double dy = player.getY() - lastY; + double dz = player.getZ() - lastZ; - lastX = Entity_getX(player); - lastY = Entity_getY(player); - lastZ = Entity_getZ(player); + lastX = player.getX(); + lastY = player.getY(); + lastZ = player.getZ(); Packet packet; if (force || Math.abs(dx) > 8.0 || Math.abs(dy) > 8.0 || Math.abs(dz) > 8.0) { @@ -317,18 +310,16 @@ public class RecordingEventHandler extends EventRegistrations { //Leaving Ride - if((!player.isRiding() && lastRiding != -1) || - (player.isRiding() && lastRiding != getRiddenEntity(player).getEntityId())) { - if(!player.isRiding()) { - lastRiding = -1; - } else { - lastRiding = getRiddenEntity(player).getEntityId(); - } + Entity vehicle = player.getVehicle(); + int vehicleId = vehicle == null ? -1 : vehicle.getEntityId(); + if (lastRiding != vehicleId) { + lastRiding = vehicleId; packetListener.save(new EntityAttachS2CPacket( //#if MC<10904 //$$ 0, //#endif - player, getRiddenEntity(player) + player, + vehicle )); } @@ -465,14 +456,8 @@ public class RecordingEventHandler extends EventRegistrations { } } - //#if FABRIC>=1 { on(PreRenderCallback.EVENT, this::checkForGamePaused); } private void checkForGamePaused() { - //#else - //$$ @SubscribeEvent - //$$ public void checkForGamePaused(TickEvent.RenderTickEvent event) { - //$$ if (event.phase != TickEvent.Phase.START) return; - //#endif if (mc.isIntegratedServerRunning()) { IntegratedServer server = mc.getServer(); if (server != null && ((IntegratedServerAccessor) server).isGamePaused()) { diff --git a/src/main/java/com/replaymod/recording/mixin/MixinGuiScreen.java b/src/main/java/com/replaymod/recording/mixin/MixinGuiScreen.java index c205f9ac..743815c4 100644 --- a/src/main/java/com/replaymod/recording/mixin/MixinGuiScreen.java +++ b/src/main/java/com/replaymod/recording/mixin/MixinGuiScreen.java @@ -1,45 +1 @@ -//#if MC<=10710 -//$$ package com.replaymod.recording.mixin; -//$$ -//$$ import de.johni0702.minecraft.gui.container.VanillaGuiScreen; -//$$ import net.minecraft.client.gui.GuiScreen; -//$$ import org.lwjgl.input.Keyboard; -//$$ import org.lwjgl.input.Mouse; -//$$ 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 static com.replaymod.core.versions.MCVer.*; -//$$ -//$$ @Mixin(GuiScreen.class) -//$$ public abstract class MixinGuiScreen { -//$$ -//$$ @Inject(method = "handleInput", at = @At("HEAD"), cancellable = true) -//$$ private void handleJGuiInput(CallbackInfo ci) { -//$$ ci.cancel(); -//$$ if (Mouse.isCreated()) { -//$$ while (Mouse.next()) { -//$$ if (FORGE_BUS.post(new VanillaGuiScreen.MouseInputEvent())) { -//$$ continue; -//$$ } -//$$ handleMouseInput(); -//$$ } -//$$ } -//$$ -//$$ if (Keyboard.isCreated()) { -//$$ while (Keyboard.next()) { -//$$ if (FORGE_BUS.post(new VanillaGuiScreen.KeyboardInputEvent())) { -//$$ continue; -//$$ } -//$$ handleKeyboardInput(); -//$$ } -//$$ } -//$$ } -//$$ -//$$ @Shadow public abstract void handleMouseInput(); -//$$ -//$$ @Shadow public abstract void handleKeyboardInput(); -//$$ } -//#endif +// 1.7.10 and below diff --git a/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerLoginClient.java b/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerLoginClient.java index 54cf8e67..b67d40bc 100644 --- a/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerLoginClient.java +++ b/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerLoginClient.java @@ -1,35 +1,26 @@ package com.replaymod.recording.mixin; +import com.replaymod.core.versions.MCVer; import com.replaymod.recording.ReplayModRecording; +import com.replaymod.recording.handler.RecordingEventHandler.RecordingEventSender; import net.minecraft.client.network.ClientLoginNetworkHandler; import net.minecraft.network.ClientConnection; +import net.minecraft.network.Packet; +import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket; +import net.minecraft.network.packet.s2c.login.LoginSuccessS2CPacket; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -//#if MC>=11400 -import com.replaymod.core.versions.MCVer; -import com.replaymod.recording.handler.RecordingEventHandler.RecordingEventSender; -import net.minecraft.network.Packet; -import net.minecraft.network.packet.s2c.login.LoginQueryRequestS2CPacket; -import net.minecraft.network.packet.s2c.login.LoginSuccessS2CPacket; -//#else -//$$ import net.minecraftforge.fml.common.network.FMLNetworkEvent; -//#endif - @Mixin(ClientLoginNetworkHandler.class) public abstract class MixinNetHandlerLoginClient { - @Shadow - //#if MC>=10800 + @Final @Shadow private ClientConnection connection; - //#else - //$$ private NetworkManager field_147393_d; - //#endif - //#if MC>=11400 @Inject(method = "onQueryRequest", at=@At("HEAD")) private void earlyInitiateRecording(LoginQueryRequestS2CPacket packet, CallbackInfo ci) { initiateRecording(packet); @@ -50,27 +41,4 @@ public abstract class MixinNetHandlerLoginClient { eventSender.getRecordingEventHandler().onPacket(packet); } } - //#else - //$$ /** - //$$ * Starts the recording right before switching into PLAY state. - //$$ * We cannot use the {@link FMLNetworkEvent.ClientConnectedToServerEvent} - //$$ * as it only fires after the forge handshake. - //$$ */ - //$$ @Inject(method = "handleLoginSuccess", at=@At("HEAD")) - //$$ public void replayModRecording_initiateRecording(CallbackInfo cb) { - //#if MC>=10800 - //$$ ReplayModRecording.instance.initiateRecording(this.networkManager); - //#else - //$$ ReplayModRecording.instance.initiateRecording(this.field_147393_d); - //#endif - //$$ } - //#endif - - // Race condition in Forge's networking (not sure if still present in 1.13) - //#if MC>=11200 && MC<11400 - //$$ @Inject(method = "handleLoginSuccess", at=@At("RETURN")) - //$$ public void replayModRecording_raceConditionWorkAround(CallbackInfo cb) { - //$$ ((NetworkManagerAccessor) this.networkManager).getChannel().config().setAutoRead(true); - //$$ } - //#endif } diff --git a/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java b/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java index 2212424c..8c1841fc 100644 --- a/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java +++ b/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerPlayClient.java @@ -57,7 +57,7 @@ public abstract class MixinNetHandlerPlayClient { //$$ @Inject(method = "handlePlayerListItem", at=@At("HEAD")) //#endif public void recordOwnJoin(PlayerListS2CPacket packet, CallbackInfo ci) { - if (!MCVer.isOnMainThread()) return; + if (!mcStatic.isOnThread()) return; if (mcStatic.player == null) return; RecordingEventHandler handler = getRecordingEventHandler(); diff --git a/src/main/java/com/replaymod/recording/mixin/MixinNetworkDispatcher.java b/src/main/java/com/replaymod/recording/mixin/MixinNetworkDispatcher.java index 0ea736a1..fca4cd6c 100644 --- a/src/main/java/com/replaymod/recording/mixin/MixinNetworkDispatcher.java +++ b/src/main/java/com/replaymod/recording/mixin/MixinNetworkDispatcher.java @@ -1,70 +1 @@ -//#if MC<11400 -//$$ package com.replaymod.recording.mixin; -//$$ -//$$ import com.replaymod.recording.handler.FMLHandshakeFilter; -//$$ import io.netty.channel.ChannelPipeline; -//$$ import io.netty.channel.embedded.EmbeddedChannel; -//$$ import net.minecraftforge.fml.common.network.handshake.FMLHandshakeCodec; -//$$ import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher; -//$$ import net.minecraftforge.fml.relauncher.Side; -//$$ 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; -//$$ -//#if MC>=11200 -//$$ import io.netty.channel.ChannelConfig; -//$$ import net.minecraft.network.EnumConnectionState; -//$$ import net.minecraft.network.NetworkManager; -//$$ import org.spongepowered.asm.mixin.injection.Redirect; -//#endif -//$$ -//$$ @Mixin(value = NetworkDispatcher.class, remap = false) -//$$ public abstract class MixinNetworkDispatcher { -//$$ -//$$ @Shadow -//$$ private Side side; -//$$ -//$$ @Shadow -//$$ private EmbeddedChannel handshakeChannel; -//$$ -//$$ /** -//$$ * Always sets fml:isLocal to false on the server side. -//$$ * This effectively removes the difference in the FML handshake between SP and MP -//$$ * and forces the block/item ids, etc. to always be send. -//$$ * Injects a {@link FMLHandshakeFilter} on the client side to filter out -//$$ * those extra, unexpected packets. -//$$ */ -//$$ @Inject(method = "insertIntoChannel", at=@At("HEAD")) -//$$ public void replayModRecording_setupForLocalRecording(CallbackInfo cb) { -//$$ // If we're in multiplayer, everything is fine as is -//$$ if (!handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).get()) return; -//$$ -//$$ if (side == Side.SERVER) { -//$$ // On the server side, force all packets to be sent -//$$ handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).set(false); -//$$ } else { -//$$ // On the client side, discard additional packets -//$$ ChannelPipeline pipeline = handshakeChannel.pipeline(); -//$$ pipeline.addAfter(pipeline.context(FMLHandshakeCodec.class).name(), -//$$ "replaymod_filter", new FMLHandshakeFilter()); -//$$ } -//$$ } -//$$ - //#if MC>=11200 - //$$ @Redirect(method = "clientListenForServerHandshake", at = @At(value = "INVOKE", remap = true, target = - //$$ "Lnet/minecraft/network/NetworkManager;setConnectionState(Lnet/minecraft/network/EnumConnectionState;)V")) - //$$ public void replayModRecording_raceConditionWorkAround1(NetworkManager self, EnumConnectionState ignored) { } - //$$ - //$$ @Redirect(method = "insertIntoChannel", at = @At(value = "INVOKE", target = - //$$ "Lio/netty/channel/ChannelConfig;setAutoRead(Z)Lio/netty/channel/ChannelConfig;")) - //$$ public ChannelConfig replayModRecording_raceConditionWorkAround2(ChannelConfig self, boolean autoRead) { - //$$ if (side == Side.CLIENT) { - //$$ autoRead = false; - //$$ } - //$$ return self.setAutoRead(autoRead); - //$$ } - //#endif -//$$ } -//#endif +// 1.12.2 and below diff --git a/src/main/java/com/replaymod/recording/mixin/MixinPlayerControllerMP.java b/src/main/java/com/replaymod/recording/mixin/MixinPlayerControllerMP.java index 2084aa75..1adc3fa2 100644 --- a/src/main/java/com/replaymod/recording/mixin/MixinPlayerControllerMP.java +++ b/src/main/java/com/replaymod/recording/mixin/MixinPlayerControllerMP.java @@ -1,32 +1 @@ -package com.replaymod.recording.mixin; - -//#if MC>=10904 && MC<11400 -//$$ import com.replaymod.recording.handler.RecordingEventHandler; -//$$ import net.minecraft.client.Minecraft; -//$$ import net.minecraft.client.multiplayer.PlayerControllerMP; -//$$ import net.minecraft.util.math.BlockPos; -//$$ import net.minecraft.world.World; -//$$ 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.Redirect; -//$$ -//$$ import static com.replaymod.core.versions.MCVer.*; -//$$ -//$$ @Mixin(PlayerControllerMP.class) -//$$ public abstract class MixinPlayerControllerMP implements RecordingEventHandler.RecordingEventSender { -//$$ -//$$ @Shadow -//$$ private Minecraft mc; -//$$ -//$$ // Redirects the call to playEvent without the initial player argument to the method with that argument -//$$ // The new method will then play it and (if applicable) record it. (See MixinWorldClient) -//$$ // This is necessary for the block break event (particles and sound) to be recorded. Otherwise it looks like the -//$$ // event was emitted because of a packet (player will be null) and not as it actually was (by the player). -//$$ @Redirect(method = "onPlayerDestroyBlock", at = @At(value = "INVOKE", -//$$ target = "Lnet/minecraft/world/World;playEvent(ILnet/minecraft/util/math/BlockPos;I)V")) -//$$ public void replayModRecording_playEvent_fixed(World world, int type, BlockPos pos, int data) { -//$$ world.playEvent(mc.player, type, pos, data); -//$$ } -//$$ } -//#endif +// 1.9.4 - 1.12.2 diff --git a/src/main/java/com/replaymod/recording/mixin/MixinS26PacketMapChunkBulk.java b/src/main/java/com/replaymod/recording/mixin/MixinS26PacketMapChunkBulk.java index be5ef086..743815c4 100644 --- a/src/main/java/com/replaymod/recording/mixin/MixinS26PacketMapChunkBulk.java +++ b/src/main/java/com/replaymod/recording/mixin/MixinS26PacketMapChunkBulk.java @@ -1,38 +1 @@ -//#if MC<=10710 -//$$ package com.replaymod.recording.mixin; -//$$ -//$$ import net.minecraft.network.PacketBuffer; -//$$ import net.minecraft.network.play.server.S21PacketChunkData; -//$$ import net.minecraft.network.play.server.S26PacketMapChunkBulk; -//$$ import org.spongepowered.asm.mixin.Mixin; -//$$ import org.spongepowered.asm.mixin.Shadow; -//$$ import org.spongepowered.asm.mixin.injection.At; -//$$ import org.spongepowered.asm.mixin.injection.Inject; -//$$ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -//$$ -//$$ import java.util.concurrent.Semaphore; -//$$ -//$$ @Mixin({ S21PacketChunkData.class, S26PacketMapChunkBulk.class }) -//$$ public abstract class MixinS26PacketMapChunkBulk { -//$$ -//$$ private byte[] rawData; -//$$ -//$$ @Shadow(remap = false) -//$$ private Semaphore deflateGate; -//$$ -//$$ @Inject(method = "readPacketData", at = @At("HEAD")) -//$$ private void replayModRecording_readRawPacketData(PacketBuffer data, CallbackInfo ci) { -//$$ int readerIndex = data.readerIndex(); -//$$ data.readBytes(rawData = new byte[data.readableBytes()]); -//$$ data.readerIndex(readerIndex); -//$$ } -//$$ -//$$ @Inject(method = "writePacketData", at = @At("HEAD"), cancellable = true) -//$$ private void replayModRecording_writePacketData(PacketBuffer data, CallbackInfo ci) { -//$$ if (rawData != null && deflateGate == null) { -//$$ data.writeBytes(rawData); -//$$ ci.cancel(); -//$$ } -//$$ } -//$$ } -//#endif +// 1.7.10 and below diff --git a/src/main/java/com/replaymod/recording/packet/PacketListener.java b/src/main/java/com/replaymod/recording/packet/PacketListener.java index 83f573cd..e7236e5b 100644 --- a/src/main/java/com/replaymod/recording/packet/PacketListener.java +++ b/src/main/java/com/replaymod/recording/packet/PacketListener.java @@ -20,13 +20,13 @@ import com.replaymod.replaystudio.data.Marker; import com.replaymod.replaystudio.io.ReplayOutputStream; import com.replaymod.replaystudio.replay.ReplayFile; import com.replaymod.replaystudio.replay.ReplayMetaData; -import com.replaymod.replaystudio.us.myles.ViaVersion.api.Pair; import de.johni0702.minecraft.gui.container.VanillaGuiScreen; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandlerAdapter; 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; @@ -40,6 +40,7 @@ import net.minecraft.network.PacketByteBuf; import net.minecraft.text.LiteralText; import net.minecraft.util.crash.CrashReport; import org.apache.commons.io.FilenameUtils; +import org.apache.commons.lang3.tuple.Pair; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -163,8 +164,8 @@ public class PacketListener extends ChannelInboundHandlerAdapter { // to happen on the main thread so we can guarantee correct ordering of inbound and inject packets. // Otherwise, injected packets may end up further down the packet stream than they were supposed to and other // inbound packets which may rely on the injected packet would behave incorrectly when played back. - if (!MCVer.isOnMainThread()) { - MCVer.scheduleOnMainThread(() -> save(packet)); + if (!mc.isOnThread()) { + mc.send(() -> save(packet)); return; } try { @@ -295,7 +296,7 @@ public class PacketListener extends ChannelInboundHandlerAdapter { if (core.getSettingsRegistry().get(Setting.AUTO_POST_PROCESS) && !ReplayMod.isMinimalMode()) { outputPaths = MarkerProcessor.apply(outputPath, guiSavingReplay.getProgressBar()::setProgress); } else { - outputPaths = Collections.singletonList(new Pair<>(outputPath, metaData)); + outputPaths = Collections.singletonList(Pair.of(outputPath, metaData)); } } catch (Exception e) { logger.error("Saving replay file:", e); @@ -339,7 +340,8 @@ public class PacketListener extends ChannelInboundHandlerAdapter { //#if MC>=10800 if (packet instanceof ResourcePackSendS2CPacket) { - save(resourcePackRecorder.handleResourcePack((ResourcePackSendS2CPacket) packet)); + ClientConnection connection = ctx.pipeline().get(ClientConnection.class); + save(resourcePackRecorder.handleResourcePack(connection, (ResourcePackSendS2CPacket) packet)); return; } //#else @@ -488,15 +490,15 @@ public class PacketListener extends ChannelInboundHandlerAdapter { } public void addMarker(String name, int timestamp) { - Entity view = getRenderViewEntity(mc); + Entity view = mc.getCameraEntity(); Marker marker = new Marker(); marker.setName(name); marker.setTime(timestamp); if (view != null) { - marker.setX(Entity_getX(view)); - marker.setY(Entity_getY(view)); - marker.setZ(Entity_getZ(view)); + marker.setX(view.getX()); + marker.setY(view.getY()); + marker.setZ(view.getZ()); marker.setYaw(view.yaw); marker.setPitch(view.pitch); } diff --git a/src/main/java/com/replaymod/recording/packet/ResourcePackRecorder.java b/src/main/java/com/replaymod/recording/packet/ResourcePackRecorder.java index 6c28c388..689f2cfd 100644 --- a/src/main/java/com/replaymod/recording/packet/ResourcePackRecorder.java +++ b/src/main/java/com/replaymod/recording/packet/ResourcePackRecorder.java @@ -37,7 +37,6 @@ import java.util.concurrent.CompletableFuture; //#else //$$ import com.google.common.util.concurrent.ListenableFuture; //#endif -import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.network.ClientConnection; //#else //$$ import com.replaymod.core.mixin.ResourcePackRepositoryAccessor; @@ -111,10 +110,8 @@ public class ResourcePackRecorder { } - public synchronized ResourcePackSendS2CPacket handleResourcePack(ResourcePackSendS2CPacket packet) { + public synchronized ResourcePackSendS2CPacket handleResourcePack(ClientConnection netManager, ResourcePackSendS2CPacket packet) { final int requestId = nextRequestId++; - final ClientPlayNetworkHandler netHandler = mc.getNetworkHandler(); - final ClientConnection netManager = netHandler.getConnection(); final String url = packet.getURL(); final String hash = packet.getSHA1(); @@ -138,7 +135,7 @@ public class ResourcePackRecorder { final ServerInfo serverData = mc.getCurrentServerEntry(); if (serverData != null && serverData.getResourcePack() == ServerInfo.ResourcePackState.ENABLED) { netManager.send(makeStatusPacket(hash, Status.ACCEPTED)); - downloadResourcePackFuture(requestId, url, hash); + downloadResourcePackFuture(netManager, requestId, url, hash); } else if (serverData != null && serverData.getResourcePack() != ServerInfo.ResourcePackState.PROMPT) { netManager.send(makeStatusPacket(hash, Status.DECLINED)); } else { @@ -156,7 +153,7 @@ public class ResourcePackRecorder { } if (result) { netManager.send(makeStatusPacket(hash, Status.ACCEPTED)); - downloadResourcePackFuture(requestId, url, hash); + downloadResourcePackFuture(netManager, requestId, url, hash); } else { netManager.send(makeStatusPacket(hash, Status.DECLINED)); } @@ -172,13 +169,18 @@ public class ResourcePackRecorder { } } - return new ResourcePackSendS2CPacket("replay://" + requestId, ""); + return new ResourcePackSendS2CPacket( + "replay://" + requestId, "" + //#if MC>=11700 + //$$ , packet.isRequired(), packet.getPrompt() + //#endif + ); } - private void downloadResourcePackFuture(int requestId, String url, final String hash) { + private void downloadResourcePackFuture(ClientConnection connection, int requestId, String url, final String hash) { addCallback(downloadResourcePack(requestId, url, hash), - result -> mc.getNetworkHandler().sendPacket(makeStatusPacket(hash, Status.SUCCESSFULLY_LOADED)), - throwable -> mc.getNetworkHandler().sendPacket(makeStatusPacket(hash, Status.FAILED_DOWNLOAD))); + result -> connection.send(makeStatusPacket(hash, Status.SUCCESSFULLY_LOADED)), + throwable -> connection.send(makeStatusPacket(hash, Status.FAILED_DOWNLOAD))); } private @@ -190,7 +192,11 @@ 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 + //$$ return packFinder.download(url, hash, true); + //#else return packFinder.download(url, hash); + //#endif } public interface IDownloadingPackFinder { diff --git a/src/main/java/com/replaymod/render/FFmpegWriter.java b/src/main/java/com/replaymod/render/FFmpegWriter.java index c0067e3a..f4e1381c 100644 --- a/src/main/java/com/replaymod/render/FFmpegWriter.java +++ b/src/main/java/com/replaymod/render/FFmpegWriter.java @@ -126,8 +126,8 @@ public class FFmpegWriter implements FrameConsumer { } CrashReport report = CrashReport.create(t, "Exporting frame"); CrashReportSection exportDetails = report.addElement("Export details"); - MCVer.addDetail(exportDetails, "Export command", settings::getExportCommand); - MCVer.addDetail(exportDetails, "Export args", commandArgs::toString); + exportDetails.add("Export command", settings::getExportCommand); + exportDetails.add("Export args", commandArgs::toString); MCVer.getMinecraft().setCrashReport(report); } finally { channels.values().forEach(it -> ByteBufferPool.release(it.getByteBuffer())); diff --git a/src/main/java/com/replaymod/render/PNGWriter.java b/src/main/java/com/replaymod/render/PNGWriter.java new file mode 100644 index 00000000..846e63be --- /dev/null +++ b/src/main/java/com/replaymod/render/PNGWriter.java @@ -0,0 +1,71 @@ +package com.replaymod.render; + +import com.replaymod.core.versions.MCVer; +import com.replaymod.render.blend.Util.IOConsumer; +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 de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; +import de.johni0702.minecraft.gui.versions.Image; +import net.minecraft.util.crash.CrashReport; + +import java.io.IOException; +import java.nio.ByteBuffer; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Map; + +public class PNGWriter implements FrameConsumer { + + private final Path outputFolder; + + public PNGWriter(Path outputFolder) throws IOException { + this.outputFolder = outputFolder; + + Files.createDirectories(outputFolder); + } + + @Override + public void consume(Map channels) { + BitmapFrame bgraFrame = channels.get(Channel.BRGA); + BitmapFrame depthFrame = channels.get(Channel.DEPTH); + try { + if (bgraFrame != null) { + withImage(bgraFrame, image -> + image.writePNG(outputFolder.resolve(bgraFrame.getFrameId() + ".png").toFile())); + } + if (depthFrame != null) { + withImage(depthFrame, image -> + image.writePNG(outputFolder.resolve(depthFrame.getFrameId() + ".depth.png").toFile())); + } + } catch (Throwable t) { + MCVer.getMinecraft().setCrashReport(CrashReport.create(t, "Exporting EXR frame")); + } finally { + channels.values().forEach(it -> ByteBufferPool.release(it.getByteBuffer())); + } + } + + private void withImage(BitmapFrame frame, IOConsumer consumer) throws IOException { + ByteBuffer buffer = frame.getByteBuffer(); + ReadableDimension size = frame.getSize(); + int width = size.getWidth(); + int height = size.getHeight(); + try (Image image = new Image(width, height)) { + for (int y = 0; y < height; y++) { + for (int x = 0; x < width; x++) { + byte b = buffer.get(); + byte g = buffer.get(); + byte r = buffer.get(); + byte a = buffer.get(); + image.setRGBA(x, y, r, g, b, a); + } + } + consumer.accept(image); + } + } + + @Override + public void close() { + } +} diff --git a/src/main/java/com/replaymod/render/RenderSettings.java b/src/main/java/com/replaymod/render/RenderSettings.java index 511ef027..28558f3c 100644 --- a/src/main/java/com/replaymod/render/RenderSettings.java +++ b/src/main/java/com/replaymod/render/RenderSettings.java @@ -1,10 +1,13 @@ package com.replaymod.render; +import com.google.gson.annotations.JsonAdapter; import com.google.gson.annotations.SerializedName; +import com.replaymod.core.utils.FileTypeAdapter; import com.replaymod.core.versions.MCVer; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableColor; import net.minecraft.client.resource.language.I18n; import net.minecraft.util.Util; +import org.apache.maven.artifact.versioning.ComparableVersion; import java.io.File; import java.io.IOException; @@ -19,16 +22,6 @@ import java.util.Optional; import static com.replaymod.render.ReplayModRender.LOGGER; -//#if MC>=11400 -import org.apache.maven.artifact.versioning.ComparableVersion; -//#else -//#if MC>=10800 -//$$ import net.minecraftforge.fml.common.versioning.ComparableVersion; -//#else -//$$ import cpw.mods.fml.common.versioning.ComparableVersion; -//#endif -//#endif - public class RenderSettings { public enum RenderMethod { DEFAULT, STEREOSCOPIC, CUBIC, EQUIRECTANGULAR, ODS, BLEND; @@ -69,8 +62,6 @@ public class RenderSettings { public enum EncodingPreset { MP4_CUSTOM("-an -c:v libx264 -b:v %BITRATE% -pix_fmt yuv420p \"%FILENAME%\"", "mp4"), - MP4_DEFAULT("-an -c:v libx264 -preset ultrafast -pix_fmt yuv420p \"%FILENAME%\"", "mp4"), - MP4_POTATO("-an -c:v libx264 -preset ultrafast -crf 51 -pix_fmt yuv420p \"%FILENAME%\"", "mp4"), WEBM_CUSTOM("-an -c:v libvpx -b:v %BITRATE% -pix_fmt yuv420p \"%FILENAME%\"", "webm"), @@ -81,7 +72,7 @@ public class RenderSettings { EXR(null, "exr"), - PNG("\"%FILENAME%-%06d.png\"", "png"); + PNG(null, "png"); private final String preset; private final String fileExtension; @@ -155,6 +146,7 @@ public class RenderSettings { private final int videoHeight; private final int framesPerSecond; private final int bitRate; + @JsonAdapter(FileTypeAdapter.class) private final File outputFile; private final boolean renderNameTags; @@ -342,9 +334,13 @@ public class RenderSettings { } } // Homebrew doesn't seem to reliably symlink its installed binaries either - File homebrewFolder = new File("/usr/local/Cellar/ffmpeg"); - String[] homebrewVersions = homebrewFolder.list(); - if (homebrewVersions != null) { + // and there's multiple locations for where Homebrew is. + for (String path : new String[]{"/usr/local", "/opt/homebrew"}) { + File homebrewFolder = new File(path + "/Cellar/ffmpeg"); + String[] homebrewVersions = homebrewFolder.list(); + if (homebrewVersions == null) { + continue; + } Optional latestOpt = Arrays.stream(homebrewVersions) .map(ComparableVersion::new) // Convert file name to comparable version .sorted(Comparator.reverseOrder()) // Sort for latest version diff --git a/src/main/java/com/replaymod/render/Setting.java b/src/main/java/com/replaymod/render/Setting.java index d8426126..c8a58037 100644 --- a/src/main/java/com/replaymod/render/Setting.java +++ b/src/main/java/com/replaymod/render/Setting.java @@ -7,4 +7,6 @@ public final class Setting { new SettingsRegistry.SettingKeys<>("advanced", "renderPath", null, "./replay_videos/"); public static final SettingsRegistry.SettingKey SKIP_POST_RENDER_GUI = new SettingsRegistry.SettingKeys<>("advanced", "skipPostRenderGui", null, false); + public static final SettingsRegistry.SettingKey FRAME_TIME_FROM_WORLD_TIME = + new SettingsRegistry.SettingKeys<>("render", "frameTimeFromWorldTime", null, false); } diff --git a/src/main/java/com/replaymod/render/blend/BlendFrameCapturer.java b/src/main/java/com/replaymod/render/blend/BlendFrameCapturer.java index 1047a78b..d9db73ee 100644 --- a/src/main/java/com/replaymod/render/blend/BlendFrameCapturer.java +++ b/src/main/java/com/replaymod/render/blend/BlendFrameCapturer.java @@ -1,6 +1,5 @@ package com.replaymod.render.blend; -import com.replaymod.core.versions.MCVer; import com.replaymod.render.capturer.RenderInfo; import com.replaymod.render.capturer.WorldRenderer; import com.replaymod.render.frame.BitmapFrame; @@ -8,6 +7,7 @@ import com.replaymod.render.rendering.Channel; import com.replaymod.render.rendering.FrameCapturer; import com.replaymod.render.utils.ByteBufferPool; import de.johni0702.minecraft.gui.utils.lwjgl.Dimension; +import net.minecraft.client.MinecraftClient; import java.io.IOException; import java.util.Collections; @@ -37,7 +37,7 @@ public class BlendFrameCapturer implements FrameCapturer { renderInfo.updateForNextFrame(); BlendState.getState().preFrame(framesDone); - worldRenderer.renderWorld(MCVer.getRenderPartialTicks(), null); + worldRenderer.renderWorld(MinecraftClient.getInstance().getTickDelta(), null); BlendState.getState().postFrame(framesDone); BitmapFrame frame = new BitmapFrame(framesDone++, new Dimension(0, 0), 0, ByteBufferPool.allocate(0)); diff --git a/src/main/java/com/replaymod/render/blend/BlendMeshBuilder.java b/src/main/java/com/replaymod/render/blend/BlendMeshBuilder.java index ebf38b75..8b9e2f50 100644 --- a/src/main/java/com/replaymod/render/blend/BlendMeshBuilder.java +++ b/src/main/java/com/replaymod/render/blend/BlendMeshBuilder.java @@ -9,6 +9,7 @@ import de.johni0702.minecraft.gui.utils.lwjgl.vector.Vector3f; import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.VertexFormat; import net.minecraft.client.render.VertexFormatElement; +import net.minecraft.client.render.VertexFormats; import org.lwjgl.opengl.GL11; //#if MC>=11500 @@ -62,7 +63,9 @@ public class BlendMeshBuilder } @Override - //#if MC>=10809 + //#if MC>=11700 + //$$ public void begin(VertexFormat.DrawMode mode, VertexFormat vertexFormat) { + //#elseif MC>=10809 public void begin(int mode, VertexFormat vertexFormat) { //#else //$$ public void startDrawing(int mode) { @@ -82,7 +85,7 @@ public class BlendMeshBuilder if (!wellBehaved) { // In case the calling code finishes with Tessellator.getInstance().draw() - BufferBuilder_beginPosTexCol(mode); + Tessellator.getInstance().getBuffer().begin(mode, VertexFormats.POSITION_TEXTURE_COLOR); } //#if MC>=10809 @@ -147,7 +150,17 @@ public class BlendMeshBuilder //#endif } - public static DMesh addBufferToMesh(ByteBuffer buffer, int mode, VertexFormat vertexFormat, DMesh mesh, ReadableVector3f vertOffset) { + public static DMesh addBufferToMesh( + ByteBuffer buffer, + //#if MC>=11700 + //$$ VertexFormat.DrawMode mode, + //#else + int mode, + //#endif + VertexFormat vertexFormat, + DMesh mesh, + ReadableVector3f vertOffset + ) { //#if MC>=11400 int vertexCount = buffer.remaining() / vertexFormat.getVertexSize(); //#else @@ -156,7 +169,18 @@ public class BlendMeshBuilder return addBufferToMesh(buffer, vertexCount, mode, vertexFormat, mesh, vertOffset); } - public static DMesh addBufferToMesh(ByteBuffer buffer, int vertexCount, int mode, VertexFormat vertexFormat, DMesh mesh, ReadableVector3f vertOffset) { + public static DMesh addBufferToMesh( + ByteBuffer buffer, + int vertexCount, + //#if MC>=11700 + //$$ VertexFormat.DrawMode mode, + //#else + int mode, + //#endif + VertexFormat vertexFormat, + DMesh mesh, + ReadableVector3f vertOffset + ) { if (mesh == null) { mesh = new DMesh(); } @@ -260,7 +284,11 @@ public class BlendMeshBuilder // Bundle vertices into shapes and add them to the mesh switch (mode) { + //#if MC>=11700 + //$$ case TRIANGLES: + //#else case GL11.GL_TRIANGLES: + //#endif for (int i = 0; i < vertices.size(); i+=3) { mesh.addTriangle( vertices.get(i ), @@ -276,7 +304,11 @@ public class BlendMeshBuilder ); } break; + //#if MC>=11700 + //$$ case QUADS: + //#else case GL11.GL_QUADS: + //#endif for (int i = 0; i < vertices.size(); i+=4) { mesh.addQuad( vertices.get(i ), diff --git a/src/main/java/com/replaymod/render/blend/BlendState.java b/src/main/java/com/replaymod/render/blend/BlendState.java index 0b3333b7..0ef4a29b 100644 --- a/src/main/java/com/replaymod/render/blend/BlendState.java +++ b/src/main/java/com/replaymod/render/blend/BlendState.java @@ -88,7 +88,7 @@ public class BlendState implements Exporter { } catch (IOException e) { CrashReport report = CrashReport.create(e, "Setup of blend exporter"); CrashReportSection category = report.addElement("Exporter"); - addDetail(category, "Exporter", exporter::toString); + category.add("Exporter", exporter::toString); throw new CrashException(report); } } @@ -102,7 +102,7 @@ public class BlendState implements Exporter { } catch (IOException e) { CrashReport report = CrashReport.create(e, "Tear down of blend exporter"); CrashReportSection category = report.addElement("Exporter"); - addDetail(category, "Exporter", exporter::toString); + category.add("Exporter", exporter::toString); throw new CrashException(report); } } @@ -139,8 +139,8 @@ public class BlendState implements Exporter { } catch (IOException e) { CrashReport report = CrashReport.create(e, "Pre frame of blend exporter"); CrashReportSection category = report.addElement("Exporter"); - addDetail(category, "Exporter", exporter::toString); - addDetail(category, "Frame", () -> String.valueOf(frame)); + category.add("Exporter", exporter::toString); + category.add("Frame", () -> String.valueOf(frame)); throw new CrashException(report); } } @@ -154,8 +154,8 @@ public class BlendState implements Exporter { } catch (IOException e) { CrashReport report = CrashReport.create(e, "Post frame of blend exporter"); CrashReportSection category = report.addElement("Exporter"); - addDetail(category, "Exporter", exporter::toString); - addDetail(category, "Frame", () -> String.valueOf(frame)); + category.add("Exporter", exporter::toString); + category.add("Frame", () -> String.valueOf(frame)); throw new CrashException(report); } } diff --git a/src/main/java/com/replaymod/render/blend/Util.java b/src/main/java/com/replaymod/render/blend/Util.java index d47092e7..c43e695a 100644 --- a/src/main/java/com/replaymod/render/blend/Util.java +++ b/src/main/java/com/replaymod/render/blend/Util.java @@ -39,7 +39,7 @@ public class Util { } } - private static FloatBuffer floatBuffer = GlAllocationUtils.allocateFloatBuffer(16); + private static FloatBuffer floatBuffer = GlAllocationUtils.allocateByteBuffer(16 * 4).asFloatBuffer(); public static Matrix4f getGlMatrix(int matrix) { floatBuffer.clear(); //#if MC>=11400 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 909e7c6e..2320f236 100644 --- a/src/main/java/com/replaymod/render/blend/exporters/ModelRendererExporter.java +++ b/src/main/java/com/replaymod/render/blend/exporters/ModelRendererExporter.java @@ -21,7 +21,6 @@ import net.minecraft.client.model.ModelPart; import java.io.IOException; -import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.render.blend.Util.isGlTextureMatrixIdentity; public class ModelRendererExporter implements Exporter { @@ -86,11 +85,13 @@ public class ModelRendererExporter implements Exporter { DMesh mesh = new DMesh(); BlendMeshBuilder builder = new BlendMeshBuilder(mesh); //#if MC>=11500 - for (Cuboid box : cubeList(model)) { - // FIXME 1.15 - } + // FIXME 1.15 + //#elseif MC>=10809 + //$$ for (Box box : model.boxes) { + //$$ box.render(builder, scale); + //$$ } //#else - //$$ for (Box box : cubeList(model)) { + //$$ for (ModelBox box : (java.util.List) model.cubeList) { //$$ box.render(builder, scale); //$$ } //#endif 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 3cf2fb1a..2aecf1a4 100644 --- a/src/main/java/com/replaymod/render/blend/exporters/ParticlesExporter.java +++ b/src/main/java/com/replaymod/render/blend/exporters/ParticlesExporter.java @@ -11,6 +11,7 @@ import com.replaymod.render.blend.mixin.ParticleAccessor; import de.johni0702.minecraft.gui.utils.lwjgl.vector.Matrix4f; import de.johni0702.minecraft.gui.utils.lwjgl.vector.Vector3f; import net.minecraft.client.MinecraftClient; +import org.lwjgl.opengl.GL11; //#if MC>=11400 import net.minecraft.util.math.Vec3d; @@ -169,7 +170,7 @@ public class ParticlesExporter implements Exporter { builder.setReverseOffset(offset); builder.setWellBehaved(true); //#if MC>=10809 - builder.begin(7, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT); + builder.begin(GL11.GL_QUADS, VertexFormats.POSITION_TEXTURE_COLOR_LIGHT); //#else //$$ builder.startDrawingQuads(); //#endif diff --git a/src/main/java/com/replaymod/render/capturer/IrisODSFrameCapturer.java b/src/main/java/com/replaymod/render/capturer/IrisODSFrameCapturer.java new file mode 100644 index 00000000..a1d31055 --- /dev/null +++ b/src/main/java/com/replaymod/render/capturer/IrisODSFrameCapturer.java @@ -0,0 +1,156 @@ +//#if MC>=11600 +package com.replaymod.render.capturer; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.replaymod.render.RenderSettings; +import com.replaymod.render.frame.CubicOpenGlFrame; +import com.replaymod.render.frame.ODSOpenGlFrame; +import com.replaymod.render.frame.OpenGlFrame; +import com.replaymod.render.rendering.Channel; +import com.replaymod.render.rendering.FrameCapturer; +import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; +import net.coderbot.iris.Iris; +import net.coderbot.iris.config.IrisConfig; + +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +import static com.replaymod.core.versions.MCVer.popMatrix; +import static com.replaymod.core.versions.MCVer.pushMatrix; +import static com.replaymod.core.versions.MCVer.resizeMainWindow; +import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; +import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; + +public class IrisODSFrameCapturer implements FrameCapturer { + + public static final String SHADER_PACK_NAME = "assets/replaymod/iris/ods"; + public static IrisODSFrameCapturer INSTANCE; + private final CubicPboOpenGlFrameCapturer left, right; + private final String prevShaderPack; + private int direction; + private boolean isLeftEye; + + public IrisODSFrameCapturer(WorldRenderer worldRenderer, final RenderInfo renderInfo, int frameSize) { + RenderInfo fakeInfo = new RenderInfo() { + private int call; + private float partialTicks; + @Override + public ReadableDimension getFrameSize() { + return renderInfo.getFrameSize(); + } + + @Override + public int getFramesDone() { + return renderInfo.getFramesDone(); + } + + @Override + public int getTotalFrames() { + return renderInfo.getTotalFrames(); + } + + @Override + public float updateForNextFrame() { + if (call++ % 2 == 0) { + partialTicks = renderInfo.updateForNextFrame(); + } + return partialTicks; + } + + @Override + public RenderSettings getRenderSettings() { + return renderInfo.getRenderSettings(); + } + }; + left = new CubicStereoFrameCapturer(worldRenderer, fakeInfo, frameSize); + right = new CubicStereoFrameCapturer(worldRenderer, fakeInfo, frameSize); + + INSTANCE = this; + prevShaderPack = Iris.getIrisConfig().getShaderPackName().orElse(null); + setShaderPack(SHADER_PACK_NAME); + } + + private static void setShaderPack(String name) { + IrisConfig irisConfig = Iris.getIrisConfig(); + irisConfig.setShaderPackName(name); + try { + irisConfig.save(); + Iris.reload(); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public int getDirection() { + return direction; + } + + public boolean isLeftEye() { + return isLeftEye; + } + + @Override + public boolean isDone() { + return left.isDone() && right.isDone(); + } + + @Override + public Map process() { + isLeftEye = true; + Map leftChannels = left.process(); + isLeftEye = false; + Map rightChannels = right.process(); + + if (leftChannels != null && rightChannels != null) { + Map result = new HashMap<>(); + for (Channel channel : Channel.values()) { + CubicOpenGlFrame leftFrame = leftChannels.get(channel); + CubicOpenGlFrame rightFrame = rightChannels.get(channel); + if (leftFrame != null && rightFrame != null) { + result.put(channel, new ODSOpenGlFrame(leftFrame, rightFrame)); + } + } + return result; + } + return null; + } + + @Override + public void close() throws IOException { + left.close(); + right.close(); + INSTANCE = null; + setShaderPack(prevShaderPack); + } + + private class CubicStereoFrameCapturer extends CubicPboOpenGlFrameCapturer { + public CubicStereoFrameCapturer(WorldRenderer worldRenderer, RenderInfo renderInfo, int frameSize) { + super(worldRenderer, renderInfo, frameSize); + } + + @Override + protected OpenGlFrame renderFrame(int frameId, float partialTicks, CubicOpenGlFrameCapturer.Data captureData) { + resizeMainWindow(mc, getFrameWidth(), getFrameHeight()); + + pushMatrix(); + frameBuffer().beginWrite(true); + + GlStateManager.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT + //#if MC>=11400 + , false + //#endif + ); + GlStateManager.enableTexture(); + + direction = captureData.ordinal(); + worldRenderer.renderWorld(partialTicks, null); + + frameBuffer().endWrite(); + popMatrix(); + + return captureFrame(frameId, captureData); + } + } +} +//#endif diff --git a/src/main/java/com/replaymod/render/capturer/ODSFrameCapturer.java b/src/main/java/com/replaymod/render/capturer/ODSFrameCapturer.java index 5f8d1713..4df2fa18 100644 --- a/src/main/java/com/replaymod/render/capturer/ODSFrameCapturer.java +++ b/src/main/java/com/replaymod/render/capturer/ODSFrameCapturer.java @@ -1,5 +1,6 @@ package com.replaymod.render.capturer; +import com.mojang.blaze3d.platform.GlStateManager; import com.replaymod.render.rendering.Channel; import de.johni0702.minecraft.gui.utils.EventRegistrations; import com.replaymod.render.RenderSettings; @@ -15,12 +16,13 @@ import net.minecraft.util.crash.CrashReport; import net.minecraft.util.crash.CrashException; import net.minecraft.util.Identifier; -import static com.mojang.blaze3d.platform.GlStateManager.*; - import java.io.IOException; import java.util.HashMap; import java.util.Map; +import static com.replaymod.core.versions.MCVer.popMatrix; +import static com.replaymod.core.versions.MCVer.pushMatrix; +import static com.replaymod.core.versions.MCVer.resizeMainWindow; import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; @@ -164,17 +166,17 @@ public class ODSFrameCapturer implements FrameCapturer { @Override protected OpenGlFrame renderFrame(int frameId, float partialTicks, CubicOpenGlFrameCapturer.Data captureData) { - resize(getFrameWidth(), getFrameHeight()); + resizeMainWindow(mc, getFrameWidth(), getFrameHeight()); pushMatrix(); frameBuffer().beginWrite(true); - clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT + GlStateManager.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT //#if MC>=11400 , false //#endif ); - enableTexture(); + GlStateManager.enableTexture(); directionVariable.set(captureData.ordinal()); worldRenderer.renderWorld(partialTicks, null); diff --git a/src/main/java/com/replaymod/render/capturer/OpenGlFrameCapturer.java b/src/main/java/com/replaymod/render/capturer/OpenGlFrameCapturer.java index 571d5164..eb26e0bc 100644 --- a/src/main/java/com/replaymod/render/capturer/OpenGlFrameCapturer.java +++ b/src/main/java/com/replaymod/render/capturer/OpenGlFrameCapturer.java @@ -1,5 +1,6 @@ package com.replaymod.render.capturer; +import com.mojang.blaze3d.platform.GlStateManager; import com.replaymod.core.versions.MCVer; import com.replaymod.render.frame.OpenGlFrame; import com.replaymod.render.rendering.Frame; @@ -16,17 +17,9 @@ import org.lwjgl.opengl.GL12; import java.io.IOException; import java.nio.ByteBuffer; -//#if MC>=11400 -import com.replaymod.render.mixin.MainWindowAccessor; -import static com.replaymod.core.versions.MCVer.getWindow; -//#endif - -//#if MC>=10800 -import static com.mojang.blaze3d.platform.GlStateManager.*; -//#else -//$$ import static com.replaymod.core.versions.MCVer.GlStateManager.*; -//#endif - +import static com.replaymod.core.versions.MCVer.popMatrix; +import static com.replaymod.core.versions.MCVer.pushMatrix; +import static com.replaymod.core.versions.MCVer.resizeMainWindow; import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; @@ -36,7 +29,7 @@ public abstract class OpenGlFrameCapturer=11400 , false //#endif ); - enableTexture(); + GlStateManager.enableTexture(); worldRenderer.renderWorld(partialTicks, captureData); @@ -115,30 +108,6 @@ public abstract class OpenGlFrameCapturer=11400 - Framebuffer fb = mc.getFramebuffer(); - if (fb.viewportWidth != width || fb.viewportHeight != height) { - fb.resize(width, height - //#if MC>=11400 - , false - //#endif - ); - } - //noinspection ConstantConditions - MainWindowAccessor mainWindow = (MainWindowAccessor) (Object) getWindow(mc); - mainWindow.setFramebufferWidth(width); - mainWindow.setFramebufferHeight(height); - //#if MC>=11500 - mc.gameRenderer.onResized(width, height); - //#endif - //#else - //$$ if (width != mc.displayWidth || height != mc.displayHeight) { - //$$ mc.resize(width, height); - //$$ } - //#endif - } - @Override public void close() throws IOException { } diff --git a/src/main/java/com/replaymod/render/gui/GuiExportFailed.java b/src/main/java/com/replaymod/render/gui/GuiExportFailed.java index b3a8a702..de5e9c0e 100644 --- a/src/main/java/com/replaymod/render/gui/GuiExportFailed.java +++ b/src/main/java/com/replaymod/render/gui/GuiExportFailed.java @@ -19,7 +19,6 @@ import net.minecraft.util.crash.CrashException; import java.util.Arrays; import java.util.function.Consumer; -import static com.replaymod.core.versions.MCVer.addDetail; import static com.replaymod.render.ReplayModRender.LOGGER; public class GuiExportFailed extends GuiScreen { @@ -33,8 +32,8 @@ public class GuiExportFailed extends GuiScreen { // If they haven't, then this is probably a faulty ffmpeg installation and there's nothing we can do CrashReport crashReport = CrashReport.create(e, "Exporting video"); CrashReportSection details = crashReport.addElement("Export details"); - addDetail(details, "Settings", settings::toString); - addDetail(details, "FFmpeg log", e::getLog); + details.add("Settings", settings::toString); + details.add("FFmpeg log", e::getLog); throw new CrashException(crashReport); } else { // If they have, ask them whether it was intentional diff --git a/src/main/java/com/replaymod/render/gui/GuiRenderQueue.java b/src/main/java/com/replaymod/render/gui/GuiRenderQueue.java index 7cd0a6cd..a019b565 100644 --- a/src/main/java/com/replaymod/render/gui/GuiRenderQueue.java +++ b/src/main/java/com/replaymod/render/gui/GuiRenderQueue.java @@ -2,6 +2,7 @@ package com.replaymod.render.gui; import com.google.common.collect.Iterables; import com.replaymod.core.ReplayMod; +import com.replaymod.core.utils.Result; import com.replaymod.core.utils.Utils; import com.replaymod.core.versions.MCVer; import com.replaymod.render.RenderSettings; @@ -14,7 +15,6 @@ import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.ReplaySender; import com.replaymod.replaystudio.pathing.path.Timeline; import com.replaymod.replaystudio.replay.ReplayFile; -import com.replaymod.replaystudio.us.myles.ViaVersion.api.Pair; import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.RenderInfo; import de.johni0702.minecraft.gui.container.AbstractGuiClickableContainer; @@ -31,6 +31,7 @@ import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.layout.GridLayout; import de.johni0702.minecraft.gui.layout.HorizontalLayout; import de.johni0702.minecraft.gui.popup.AbstractGuiPopup; +import de.johni0702.minecraft.gui.popup.GuiInfoPopup; import de.johni0702.minecraft.gui.utils.Colors; import de.johni0702.minecraft.gui.utils.lwjgl.Dimension; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; @@ -39,6 +40,7 @@ import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.NoticeScreen; import net.minecraft.util.crash.CrashReport; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.tuple.Pair; import java.io.File; import java.io.IOException; @@ -88,7 +90,7 @@ public class GuiRenderQueue extends AbstractGuiPopup implements private final AbstractGuiScreen container; private final ReplayHandler replayHandler; private final Set selectedEntries = new HashSet<>(); - private final Supplier timelineSupplier; + private final Supplier> timelineSupplier; private boolean opened; { @@ -113,7 +115,7 @@ public class GuiRenderQueue extends AbstractGuiPopup implements private final ReplayModRender mod = ReplayModRender.instance; private final List jobs = mod.getRenderQueue(); - public GuiRenderQueue(AbstractGuiScreen container, ReplayHandler replayHandler, Supplier timelineSupplier) { + public GuiRenderQueue(AbstractGuiScreen container, ReplayHandler replayHandler, Supplier> timelineSupplier) { super(container); this.container = container; this.replayHandler = replayHandler; @@ -127,7 +129,7 @@ public class GuiRenderQueue extends AbstractGuiPopup implements list.getListPanel().addElements(null, new Entry(renderJob)); } - addButton.onClick(this::addButtonClicked); + addButton.onClick(() -> addButtonClicked().ifErr(lines -> GuiInfoPopup.open(container, lines))); editButton.onClick(() -> { Entry job = selectedEntries.iterator().next(); @@ -267,15 +269,12 @@ public class GuiRenderQueue extends AbstractGuiPopup implements }); } - private GuiRenderSettings addButtonClicked() { - Timeline timeline = timelineSupplier.get(); - if (timeline != null) { + private Result addButtonClicked() { + return timelineSupplier.get().mapOk(timeline -> { GuiRenderSettings popup = addJob(timeline); popup.open(); return popup; - } else { - return null; - } + }); } public GuiRenderSettings addJob(Timeline timeline) { @@ -317,9 +316,8 @@ public class GuiRenderQueue extends AbstractGuiPopup implements @Override public void open() { if (jobs.isEmpty() && timelineSupplier != null) { - if (addButtonClicked() == null) { - close(); - } + addButtonClicked().ifErr(lines -> + GuiInfoPopup.open(container, lines).onClosed(this::close)); return; } @@ -348,7 +346,7 @@ public class GuiRenderQueue extends AbstractGuiPopup implements renderButton.setEnabled(jobs.size() > 0); renderButton.setI18nLabel("replaymod.gui.renderqueue.render" + (selected > 0 ? "selected" : "all")); - String[] compatError = VideoRenderer.checkCompat(); + String[] compatError = VideoRenderer.checkCompat(jobs.stream().map(RenderJob::getSettings)); if (compatError != null) { renderButton.setDisabled().setTooltip(new GuiTooltip().setText(compatError)); } diff --git a/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java b/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java index 24aac257..0a1fbdcd 100644 --- a/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java +++ b/src/main/java/com/replaymod/render/gui/GuiRenderSettings.java @@ -1,8 +1,6 @@ package com.replaymod.render.gui; import com.google.common.base.Preconditions; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; import com.google.gson.Gson; import com.google.gson.GsonBuilder; import com.google.gson.InstanceCreator; @@ -39,7 +37,6 @@ import net.minecraft.client.gui.screen.NoticeScreen; import net.minecraft.client.resource.language.I18n; import net.minecraft.util.crash.CrashReport; -import javax.annotation.Nullable; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; @@ -73,7 +70,7 @@ public class GuiRenderSettings extends AbstractGuiPopup { if (renderMethodDropdown.getSelectedValue() == RenderSettings.RenderMethod.BLEND) { encodingPresetDropdown.setSelected(RenderSettings.EncodingPreset.BLEND); } else { - encodingPresetDropdown.setSelected(RenderSettings.EncodingPreset.MP4_DEFAULT); + encodingPresetDropdown.setSelected(RenderSettings.EncodingPreset.MP4_CUSTOM); } } updateInputs(); @@ -131,25 +128,13 @@ public class GuiRenderSettings extends AbstractGuiPopup { encodingPresetDropdown.getSelectedValue().getFileExtension()); popup.setFolder(getParentFile(outputFile)); popup.setFileName(outputFile.getName()); - Futures.addCallback( - popup.getFuture(), - new FutureCallback() { - @Override - public void onSuccess(@Nullable File result) { - if (result != null) { - if (!result.getName().equals(outputFile.getName())) { - userDefinedOutputFileName = true; - } - outputFile = result; - outputFileButton.setLabel(result.getName()); - } - } - - @Override - public void onFailure(Throwable t) { - throw new RuntimeException(t); - } - }); + popup.onAccept(file -> { + if (!file.getName().equals(outputFile.getName())) { + userDefinedOutputFileName = true; + } + outputFile = file; + outputFileButton.setLabel(file.getName()); + }); } }); @@ -378,7 +363,7 @@ public class GuiRenderSettings extends AbstractGuiPopup { videoHeight.setTextColor(Colors.RED); } - String[] compatError = VideoRenderer.checkCompat(); + String[] compatError = VideoRenderer.checkCompat(save(false)); if (resolutionError != null) { renderButton.setDisabled().setTooltip(new GuiTooltip().setI18nText(resolutionError)); } else if (compatError != null) { @@ -401,10 +386,10 @@ public class GuiRenderSettings extends AbstractGuiPopup { case CUBIC: case EQUIRECTANGULAR: case ODS: - stabilizePanel.forEach(IGuiCheckbox.class).setEnabled(); + stabilizePanel.invokeAll(IGuiCheckbox.class, GuiElement::setEnabled); break; default: - stabilizePanel.forEach(IGuiCheckbox.class).setDisabled(); + stabilizePanel.invokeAll(IGuiCheckbox.class, GuiElement::setDisabled); } // Enable/Disable Spherical FOV slider @@ -421,8 +406,9 @@ public class GuiRenderSettings extends AbstractGuiPopup { // Enable/Disable various options for blend export boolean isEXR = encodingPresetDropdown.getSelectedValue() == RenderSettings.EncodingPreset.EXR; + boolean isPNG = encodingPresetDropdown.getSelectedValue() == RenderSettings.EncodingPreset.PNG; boolean isBlend = renderMethod == RenderSettings.RenderMethod.BLEND; - boolean isFFmpeg = !isBlend && !isEXR; + boolean isFFmpeg = !isBlend && !isEXR && !isPNG; if (isBlend) { videoWidth.setDisabled(); videoHeight.setDisabled(); @@ -432,11 +418,11 @@ public class GuiRenderSettings extends AbstractGuiPopup { exportArguments.setEnabled(isFFmpeg); antiAliasingDropdown.setEnabled(isFFmpeg); - if (isEXR) { + if (isEXR || isPNG) { depthMap.setEnabled().setTooltip(null); } else { depthMap.setDisabled().setTooltip(new GuiTooltip().setColor(Colors.RED) - .setI18nText("replaymod.gui.rendersettings.depthmap.onlyexr")); + .setI18nText("replaymod.gui.rendersettings.depthmap.only_exr_or_png")); } // Enable/Disable export args reset button @@ -520,7 +506,8 @@ public class GuiRenderSettings extends AbstractGuiPopup { } RenderSettings.EncodingPreset encodingPreset = settings.getEncodingPreset(); /* encodingPreset can be null from a previously supported and later removed preset */ - if (encodingPreset == null || !encodingPreset.isSupported()) { + boolean invalidEncodingPreset = encodingPreset == null || !encodingPreset.isSupported(); + if (invalidEncodingPreset) { encodingPreset = getDefaultRenderSettings().getEncodingPreset(); } encodingPresetDropdown.setSelected(encodingPreset); @@ -569,7 +556,7 @@ public class GuiRenderSettings extends AbstractGuiPopup { antiAliasingDropdown.setSelected(settings.getAntiAliasing()); exportCommand.setText(settings.getExportCommand()); String exportArguments = settings.getExportArguments(); - if (exportArguments == null || settings.getEncodingPreset() == null) { + if (exportArguments == null || settings.getEncodingPreset() == null || invalidEncodingPreset) { // backwards compat, see RenderSettings#exportArguments exportArguments = encodingPreset.getValue(); } @@ -642,8 +629,8 @@ public class GuiRenderSettings extends AbstractGuiPopup { } private RenderSettings getDefaultRenderSettings() { - return new RenderSettings(RenderSettings.RenderMethod.DEFAULT, RenderSettings.EncodingPreset.MP4_DEFAULT, 1920, 1080, 60, 10 << 20, null, - true, false, false, false, null, 360, 180, false, false, false, RenderSettings.AntiAliasing.NONE, "", RenderSettings.EncodingPreset.MP4_DEFAULT.getValue(), false); + return new RenderSettings(RenderSettings.RenderMethod.DEFAULT, RenderSettings.EncodingPreset.MP4_CUSTOM, 1920, 1080, 60, 20 << 20, null, + true, false, false, false, null, 360, 180, false, false, false, RenderSettings.AntiAliasing.NONE, "", RenderSettings.EncodingPreset.MP4_CUSTOM.getValue(), false); } @Override diff --git a/src/main/java/com/replaymod/render/hooks/ChunkLoadingRenderGlobal.java b/src/main/java/com/replaymod/render/hooks/ChunkLoadingRenderGlobal.java deleted file mode 100644 index f15097cf..00000000 --- a/src/main/java/com/replaymod/render/hooks/ChunkLoadingRenderGlobal.java +++ /dev/null @@ -1,188 +0,0 @@ -//#if MC>=10800 -package com.replaymod.render.hooks; - -import net.minecraft.client.render.WorldRenderer; - -import java.lang.reflect.Field; - -//#if MC>=11500 -//#else -//$$ import com.replaymod.render.mixin.ChunkRenderDispatcherAccessor; -//$$ import com.replaymod.render.mixin.WorldRendererAccessor; -//$$ import com.replaymod.render.utils.JailingQueue; -//$$ import net.minecraft.client.render.chunk.BlockBufferBuilderStorage; -//$$ import net.minecraft.client.render.chunk.ChunkBuilder; -//$$ import net.minecraft.client.render.chunk.ChunkRenderTask; -//$$ import net.minecraft.client.render.chunk.ChunkRenderWorker; -//$$ import net.minecraft.client.render.chunk.ChunkRenderer; -//$$ import java.util.Iterator; -//$$ import static com.replaymod.core.versions.MCVer.*; -//#endif - -//#if MC>=10904 -import java.util.concurrent.PriorityBlockingQueue; -//#else -//$$ import java.util.concurrent.BlockingQueue; -//#endif - -public class ChunkLoadingRenderGlobal { - - //#if MC>=11400 - private final WorldRenderer hooked; - //#else - //$$ private final RenderGlobal hooked; - //#endif - - //#if MC>=11500 - //#else - //$$ private ChunkBuilder renderDispatcher; - //#if MC>=11400 - //$$ private JailingQueue workerJailingQueue; - //#else - //$$ private JailingQueue workerJailingQueue; - //#endif - //$$ private ChunkRenderWorkerAccessor renderWorker; - //$$ private int frame; - //#endif - - public ChunkLoadingRenderGlobal( - //#if MC>=11400 - WorldRenderer renderGlobal - //#else - //$$ RenderGlobal renderGlobal - //#endif - ) { - this.hooked = renderGlobal; - - //#if MC>=11500 - install(); - //#else - //$$ setup(((WorldRendererAccessor) renderGlobal).getRenderDispatcher()); - //$$ install(); - //#endif - } - - private void install() { - try { - //#if MC>=11400 - Field hookField = WorldRenderer.class.getField("replayModRender_hook"); - //#else - //$$ Field hookField = RenderGlobal.class.getField("replayModRender_hook"); - //#endif - hookField.set(hooked, this); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new Error(e); - } - } - - //#if MC>=11500 - //#else - //$$ public void updateRenderDispatcher(ChunkBuilder renderDispatcher) { - //$$ if (this.renderDispatcher != null) { - //$$ workerJailingQueue.freeAll(); - //$$ this.renderDispatcher = null; - //$$ } - //$$ if (renderDispatcher != null) { - //$$ setup(renderDispatcher); - //$$ } - //$$ } - //$$ - //$$ private void setup(ChunkBuilder renderDispatcher) { - //$$ this.renderDispatcher = renderDispatcher; - //$$ this.renderWorker = (ChunkRenderWorkerAccessor) new ChunkRenderWorker(renderDispatcher, new BlockBufferBuilderStorage()); - //$$ ChunkRenderDispatcherAccessor renderDispatcherAcc = (ChunkRenderDispatcherAccessor) renderDispatcher; - //$$ - //$$ int workerThreads = renderDispatcherAcc.getListThreadedWorkers().size(); - //#if MC>=10904 - //$$ PriorityBlockingQueue queueChunkUpdates = renderDispatcherAcc.getQueueChunkUpdates(); - //#else - //$$ BlockingQueue queueChunkUpdates = renderDispatcherAcc.getQueueChunkUpdates(); - //#endif - //$$ workerJailingQueue = new JailingQueue<>(queueChunkUpdates); - //$$ renderDispatcherAcc.setQueueChunkUpdates(workerJailingQueue); - //#if MC>=10904 - //$$ ChunkRenderTask element = new ChunkRenderTask( - //$$ null, - //$$ null, - //$$ 0 - //#if MC>=11400 - //$$ , null - //#endif - //$$ ); - //#else - //$$ ChunkCompileTaskGenerator element = new ChunkCompileTaskGenerator(null, null); - //#endif - //$$ element.cancel(); - //$$ for (int i = 0; i < workerThreads; i++) { - //$$ queueChunkUpdates.add(element); - //$$ } - //$$ - //$$ // Temporary workaround for dead lock, will be replaced by a new (ShaderMod compatible) mechanism later - //$$ //noinspection StatementWithEmptyBody - //$$ while (renderDispatcher.runTasksSync(0)) {} - //$$ - //$$ workerJailingQueue.jail(workerThreads); - //$$ renderDispatcherAcc.setQueueChunkUpdates(queueChunkUpdates); - //$$ } - //$$ - //$$ public void updateChunks() { - //$$ while (renderDispatcher.runTasksSync(0)) { - //$$ ((WorldRendererAccessor) hooked).setDisplayListEntitiesDirty(true); - //$$ } - //$$ - //#if MC>=10904 - //$$ PriorityBlockingQueue queueChunkUpdates; - //#else - //$$ BlockingQueue queueChunkUpdates; - //#endif - //$$ queueChunkUpdates = ((ChunkRenderDispatcherAccessor) renderDispatcher).getQueueChunkUpdates(); - //$$ while (!queueChunkUpdates.isEmpty()) { - //$$ try { - //$$ renderWorker.doRunTask(queueChunkUpdates.poll()); - //$$ } catch (InterruptedException ignored) { } - //$$ } - //$$ - //$$ Iterator iterator = ((WorldRendererAccessor) hooked).getChunksToUpdate().iterator(); - //$$ while (iterator.hasNext()) { - //$$ ChunkRenderer renderchunk = iterator.next(); - //$$ - //$$ renderDispatcher.rebuildSync(renderchunk); - //$$ - //#if MC>=10904 - //$$ renderchunk.unscheduleRebuild(); - //#else - //$$ renderchunk.setNeedsUpdate(false); - //#endif - //$$ iterator.remove(); - //$$ } - //$$ } - //$$ - //$$ public int nextFrameId() { - //$$ return frame++; - //$$ } - //#endif - - public void uninstall() { - //#if MC<11500 - //$$ workerJailingQueue.freeAll(); - //#endif - - try { - //#if MC>=11400 - Field hookField = WorldRenderer.class.getField("replayModRender_hook"); - //#else - //$$ Field hookField = RenderGlobal.class.getField("replayModRender_hook"); - //#endif - hookField.set(hooked, null); - } catch (NoSuchFieldException | IllegalAccessException e) { - throw new Error(e); - } - } - - //#if MC>=11500 - public interface IBlockOnChunkRebuilds { - boolean uploadEverythingBlocking(); - } - //#endif -} -//#endif diff --git a/src/main/java/com/replaymod/render/hooks/EntityRendererHandler.java b/src/main/java/com/replaymod/render/hooks/EntityRendererHandler.java index dba61c99..8dbd50f7 100644 --- a/src/main/java/com/replaymod/render/hooks/EntityRendererHandler.java +++ b/src/main/java/com/replaymod/render/hooks/EntityRendererHandler.java @@ -1,21 +1,22 @@ package com.replaymod.render.hooks; +import com.replaymod.core.ReplayMod; +import com.replaymod.core.events.PreRenderHandCallback; import com.replaymod.core.versions.MCVer; import com.replaymod.render.RenderSettings; +import com.replaymod.render.Setting; import com.replaymod.render.capturer.CaptureData; import com.replaymod.render.capturer.RenderInfo; import com.replaymod.render.capturer.WorldRenderer; +import com.replaymod.replay.ReplayModReplay; import de.johni0702.minecraft.gui.utils.EventRegistrations; import net.minecraft.client.MinecraftClient; - -//#if MC>=11500 -import net.minecraft.client.util.math.MatrixStack; -//#endif +import net.minecraft.client.gui.screen.Screen; //#if MC>=11400 import com.replaymod.core.events.PostRenderCallback; import com.replaymod.core.events.PreRenderCallback; -import com.replaymod.core.events.PreRenderHandCallback; +import net.minecraft.util.Util; //#else //#if MC>=11400 //$$ import net.minecraftforge.fml.hooks.BasicEventHooks; @@ -37,14 +38,20 @@ public class EntityRendererHandler extends EventRegistrations implements WorldRe public boolean omnidirectional; + private final long startTime; + public EntityRendererHandler(RenderSettings settings, RenderInfo renderInfo) { this.settings = settings; this.renderInfo = renderInfo; //#if MC>=11400 - on(PreRenderHandCallback.EVENT, () -> omnidirectional); + this.startTime = Util.getMeasuringTimeNano(); + //#else + //$$ this.startTime = System.nanoTime(); //#endif + on(PreRenderHandCallback.EVENT, () -> omnidirectional); + ((IEntityRenderer) mc.gameRenderer).replayModRender_setHandler(this); register(); } @@ -52,7 +59,14 @@ public class EntityRendererHandler extends EventRegistrations implements WorldRe @Override public void renderWorld(final float partialTicks, CaptureData data) { this.data = data; - renderWorld(partialTicks, 0); + long offsetMillis; + if (ReplayMod.instance.getSettingsRegistry().get(Setting.FRAME_TIME_FROM_WORLD_TIME)) { + offsetMillis = ReplayModReplay.instance.getReplayHandler().getReplaySender().currentTimeStamp(); + } else { + offsetMillis = renderInfo.getFramesDone() * 1_000L / settings.getFramesPerSecond(); + } + long frameStartTimeNano = startTime + offsetMillis * 1_000_000L; + renderWorld(partialTicks, frameStartTimeNano); } public void renderWorld(float partialTicks, long finishTimeNano) { @@ -67,11 +81,26 @@ public class EntityRendererHandler extends EventRegistrations implements WorldRe //#endif if (mc.world != null && mc.player != null) { - //#if MC>=11500 - mc.gameRenderer.renderWorld(partialTicks, finishTimeNano, new MatrixStack()); - //#else - //$$ mc.gameRenderer.renderWorld(partialTicks, finishTimeNano); - //#endif + Screen orgScreen = mc.currentScreen; + boolean orgPauseOnLostFocus = mc.options.pauseOnLostFocus; + try { + mc.currentScreen = null; // do not want to render the current screen (that'd just be the progress gui) + mc.options.pauseOnLostFocus = false; // do not want the pause menu to open if the window is unfocused + + //#if MC>=11400 + mc.gameRenderer.render(partialTicks, finishTimeNano, true); + //#else + //$$ mc.setIngameNotInFocus(); // this should already be the case but it somehow still sometimes is not + //#if MC>=10809 + //$$ mc.entityRenderer.updateCameraAndRender(partialTicks, finishTimeNano); + //#else + //$$ mc.entityRenderer.updateCameraAndRender(partialTicks); + //#endif + //#endif + } finally { + mc.currentScreen = orgScreen; + mc.options.pauseOnLostFocus = orgPauseOnLostFocus; + } } //#if MC>=11400 diff --git a/src/main/java/com/replaymod/render/hooks/ForceChunkLoadingHook.java b/src/main/java/com/replaymod/render/hooks/ForceChunkLoadingHook.java new file mode 100644 index 00000000..7352945f --- /dev/null +++ b/src/main/java/com/replaymod/render/hooks/ForceChunkLoadingHook.java @@ -0,0 +1,25 @@ +package com.replaymod.render.hooks; + +import com.replaymod.render.utils.FlawlessFrames; +import net.minecraft.client.render.WorldRenderer; + +public class ForceChunkLoadingHook { + + private final WorldRenderer hooked; + + public ForceChunkLoadingHook(WorldRenderer renderGlobal) { + this.hooked = renderGlobal; + + FlawlessFrames.setEnabled(true); + IForceChunkLoading.from(renderGlobal).replayModRender_setHook(this); + } + + public void uninstall() { + IForceChunkLoading.from(hooked).replayModRender_setHook(null); + FlawlessFrames.setEnabled(false); + } + + public interface IBlockOnChunkRebuilds { + boolean uploadEverythingBlocking(); + } +} diff --git a/src/main/java/com/replaymod/render/hooks/GLStateTracker.java b/src/main/java/com/replaymod/render/hooks/GLStateTracker.java index bf6b3c9e..743815c4 100644 --- a/src/main/java/com/replaymod/render/hooks/GLStateTracker.java +++ b/src/main/java/com/replaymod/render/hooks/GLStateTracker.java @@ -1,46 +1 @@ -//#if MC<=10710 -//$$ package com.replaymod.render.hooks; -//$$ -//$$ import org.lwjgl.opengl.GL11; -//$$ import org.lwjgl.opengl.GL13; -//$$ -//$$ /** -//$$ * Tracks OpenGL state for use in the ODS shader. -//$$ * All methods should be called from the GL mixins only. -//$$ */ -//$$ public class GLStateTracker { -//$$ private static ThreadLocal stateTracker = ThreadLocal.withInitial(GLStateTracker::new); -//$$ public static GLStateTracker getInstance() { -//$$ return stateTracker.get(); -//$$ } -//$$ -//$$ public int activeTexture; -//$$ -//$$ public void updateActiveTexture(int magic) { -//$$ this.activeTexture = magic - GL13.GL_TEXTURE0; -//$$ } -//$$ -//$$ public void updateEnabledState(int magic, boolean enabled) { -//$$ switch (magic) { -//$$ case GL11.GL_FOG: FogStateCallback.EVENT.invoker().fogStateChanged(enabled); break; -//$$ case GL11.GL_TEXTURE_2D: Texture2DStateCallback.EVENT.invoker().texture2DStateChanged(activeTexture, enabled); break; -//$$ } -//$$ } -//$$ -//$$ // Called via ASM (see GLStateTrackerTransformer) -//$$ @Deprecated -//$$ @SuppressWarnings("unused") -//$$ public static void hook_glEnable(int magic) { -//$$ GL11.glEnable(magic); -//$$ getInstance().updateEnabledState(magic, true); -//$$ } -//$$ -//$$ // Called via ASM (see GLStateTrackerTransformer) -//$$ @Deprecated -//$$ @SuppressWarnings("unused") -//$$ public static void hook_glDisable(int magic) { -//$$ GL11.glDisable(magic); -//$$ getInstance().updateEnabledState(magic, false); -//$$ } -//$$ } -//#endif +// 1.7.10 and below diff --git a/src/main/java/com/replaymod/render/hooks/IForceChunkLoading.java b/src/main/java/com/replaymod/render/hooks/IForceChunkLoading.java new file mode 100644 index 00000000..7c21c77e --- /dev/null +++ b/src/main/java/com/replaymod/render/hooks/IForceChunkLoading.java @@ -0,0 +1,11 @@ +package com.replaymod.render.hooks; + +import net.minecraft.client.render.WorldRenderer; + +public interface IForceChunkLoading { + void replayModRender_setHook(ForceChunkLoadingHook hook); + + static IForceChunkLoading from(WorldRenderer worldRenderer) { + return (IForceChunkLoading) worldRenderer; + } +} diff --git a/src/main/java/com/replaymod/render/mixin/ChunkRenderDispatcherAccessor.java b/src/main/java/com/replaymod/render/mixin/ChunkRenderDispatcherAccessor.java index 2e1ed260..a6f9a3fe 100644 --- a/src/main/java/com/replaymod/render/mixin/ChunkRenderDispatcherAccessor.java +++ b/src/main/java/com/replaymod/render/mixin/ChunkRenderDispatcherAccessor.java @@ -1,34 +1 @@ -//#if MC>=10800 && MC<11500 -//$$ package com.replaymod.render.mixin; -//$$ -//$$ import net.minecraft.client.render.chunk.ChunkBuilder; -//$$ import net.minecraft.client.render.chunk.ChunkRenderTask; -//$$ import net.minecraft.client.render.chunk.ChunkRenderWorker; -//$$ import org.spongepowered.asm.mixin.Mixin; -//$$ import org.spongepowered.asm.mixin.gen.Accessor; -//$$ -//$$ import java.util.List; -//#if MC>=10904 -//$$ import java.util.concurrent.PriorityBlockingQueue; -//#else -//$$ import java.util.concurrent.BlockingQueue; -//#endif -//$$ -//$$ @Mixin(ChunkBuilder.class) -//$$ public interface ChunkRenderDispatcherAccessor { -//$$ @Accessor("workers") -//$$ List getListThreadedWorkers(); -//$$ - //#if MC>=10904 - //$$ @Accessor("pendingChunks") - //$$ PriorityBlockingQueue getQueueChunkUpdates(); - //$$ @Accessor("pendingChunks") - //$$ void setQueueChunkUpdates(PriorityBlockingQueue value); - //#else - //$$ @Accessor - //$$ BlockingQueue getQueueChunkUpdates(); - //$$ @Accessor - //$$ void setQueueChunkUpdates(BlockingQueue value); - //#endif -//$$ } -//#endif +// 1.14.4 to 1.8 diff --git a/src/main/java/com/replaymod/render/mixin/MainWindowAccessor.java b/src/main/java/com/replaymod/render/mixin/MainWindowAccessor.java index bb276fa2..335c9908 100644 --- a/src/main/java/com/replaymod/render/mixin/MainWindowAccessor.java +++ b/src/main/java/com/replaymod/render/mixin/MainWindowAccessor.java @@ -1,4 +1,3 @@ -//#if MC>=11400 package com.replaymod.render.mixin; import net.minecraft.client.util.Window; @@ -7,9 +6,12 @@ import org.spongepowered.asm.mixin.gen.Accessor; @Mixin(Window.class) public interface MainWindowAccessor { + @Accessor + int getFramebufferWidth(); @Accessor void setFramebufferWidth(int value); @Accessor + int getFramebufferHeight(); + @Accessor void setFramebufferHeight(int value); } -//#endif diff --git a/src/main/java/com/replaymod/render/mixin/MixinChunkRenderWorker.java b/src/main/java/com/replaymod/render/mixin/MixinChunkRenderWorker.java index aea88e0c..c0669fe9 100644 --- a/src/main/java/com/replaymod/render/mixin/MixinChunkRenderWorker.java +++ b/src/main/java/com/replaymod/render/mixin/MixinChunkRenderWorker.java @@ -1,19 +1 @@ -package com.replaymod.render.mixin; - -//#if MC>=10800 && MC<11500 -//$$ -//$$ import com.replaymod.core.versions.MCVer; -//$$ import net.minecraft.client.render.chunk.ChunkRenderTask; -//$$ import net.minecraft.client.render.chunk.ChunkRenderWorker; -//$$ import org.spongepowered.asm.mixin.Mixin; -//$$ import org.spongepowered.asm.mixin.Shadow; -//$$ -//$$ @Mixin(ChunkRenderWorker.class) -//$$ public abstract class MixinChunkRenderWorker implements MCVer.ChunkRenderWorkerAccessor { -//$$ @Shadow abstract void runTask(ChunkRenderTask task) throws InterruptedException; -//$$ -//$$ public void doRunTask(ChunkRenderTask task) throws InterruptedException { -//$$ runTask(task); -//$$ } -//$$ } -//#endif +// 1.14.4 - 1.8 diff --git a/src/main/java/com/replaymod/render/mixin/MixinEffectRenderer.java b/src/main/java/com/replaymod/render/mixin/MixinEffectRenderer.java index 9a9269f8..3387d168 100644 --- a/src/main/java/com/replaymod/render/mixin/MixinEffectRenderer.java +++ b/src/main/java/com/replaymod/render/mixin/MixinEffectRenderer.java @@ -1,103 +1 @@ -package com.replaymod.render.mixin; - -//#if MC<10904 -//$$ import com.replaymod.render.blend.BlendState; -//$$ import com.replaymod.render.hooks.EntityRendererHandler; -//$$ import net.minecraft.client.Minecraft; -//$$ import net.minecraft.client.particle.EffectRenderer; -//$$ import net.minecraft.client.particle.EntityFX; -//$$ import net.minecraft.entity.Entity; -//$$ import org.spongepowered.asm.mixin.Mixin; -//$$ import org.spongepowered.asm.mixin.injection.At; -//$$ import org.spongepowered.asm.mixin.injection.Redirect; -//$$ -//#if MC>=10800 -//$$ import com.replaymod.render.blend.exporters.ParticlesExporter; -//$$ import net.minecraft.client.renderer.WorldRenderer; -//#else -//$$ import net.minecraft.client.renderer.Tessellator; -//#endif -//$$ -//$$ @Mixin(EffectRenderer.class) -//$$ public abstract class MixinEffectRenderer { - //#if MC>=10800 - //#if MC>=10809 - //$$ @Redirect(method = "renderParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;renderParticle(Lnet/minecraft/client/renderer/WorldRenderer;Lnet/minecraft/entity/Entity;FFFFFF)V")) - //#else - //$$ @Redirect(method = "renderParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;func_180434_a(Lnet/minecraft/client/renderer/WorldRenderer;Lnet/minecraft/entity/Entity;FFFFFF)V")) - //#endif - //$$ private void renderNormalParticle(EntityFX fx, WorldRenderer worldRenderer, Entity view, float partialTicks, - //$$ float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) { - //$$ renderParticle(fx, worldRenderer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY); - //$$ } - //#else - //$$ @Redirect(method = "renderParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;renderParticle(Lnet/minecraft/client/renderer/Tessellator;FFFFFF)V")) - //$$ private void renderNormalParticle(EntityFX fx, Tessellator tesselator, float partialTicks, - //$$ float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) { - //$$ renderParticle(fx, tesselator, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY); - //$$ } - //#endif -//$$ - //#if MC>=10800 - //#if MC>=10809 - //$$ @Redirect(method = "renderLitParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;renderParticle(Lnet/minecraft/client/renderer/WorldRenderer;Lnet/minecraft/entity/Entity;FFFFFF)V")) - //#else - //$$ @Redirect(method = "renderLitParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;func_180434_a(Lnet/minecraft/client/renderer/WorldRenderer;Lnet/minecraft/entity/Entity;FFFFFF)V")) - //#endif - //$$ private void renderLitParticle(EntityFX fx, WorldRenderer worldRenderer, Entity view, float partialTicks, - //$$ float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) { - //$$ renderParticle(fx, worldRenderer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY); - //$$ } - //#else - //$$ @Redirect(method = "renderLitParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;renderParticle(Lnet/minecraft/client/renderer/Tessellator;FFFFFF)V")) - //$$ private void renderLitParticle(EntityFX fx, Tessellator tessellator, float partialTicks, - //$$ float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) { - //$$ renderParticle(fx, tessellator, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY); - //$$ } - //#endif -//$$ -//$$ private void renderParticle(EntityFX fx, - //#if MC>=10800 - //$$ WorldRenderer worldRenderer, Entity view, - //#else - //$$ Tessellator tessellator, - //#endif -//$$ float partialTicks, -//$$ float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) { -//$$ EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler(); -//$$ if (handler != null && handler.omnidirectional) { - //#if MC<=10710 - //$$ Entity view = Minecraft.getMinecraft().renderViewEntity; - //#endif -//$$ // Align all particles towards the camera -//$$ double dx = fx.prevPosX + (fx.posX - fx.prevPosX) * partialTicks - view.posX; -//$$ double dy = fx.prevPosY + (fx.posY - fx.prevPosY) * partialTicks - view.posY; -//$$ double dz = fx.prevPosZ + (fx.posZ - fx.prevPosZ) * partialTicks - view.posZ; -//$$ double pitch = -Math.atan2(dy, Math.sqrt(dx * dx + dz * dz)); -//$$ double yaw = -Math.atan2(dx, dz); -//$$ -//$$ rotX = (float) Math.cos(yaw); -//$$ rotZ = (float) Math.sin(yaw); -//$$ rotXZ = (float) Math.cos(pitch); -//$$ -//$$ rotYZ = (float) (-rotZ * Math.sin(pitch)); -//$$ rotXY = (float) (rotX * Math.sin(pitch)); -//$$ } - //#if MC>=10800 - //$$ BlendState blendState = BlendState.getState(); - //$$ if (blendState != null) { - //$$ blendState.get(ParticlesExporter.class).onRender(fx, partialTicks); - //$$ } - //#endif - //#if MC>=10809 - //$$ fx.renderParticle(worldRenderer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY); - //#else - //#if MC>=10800 - //$$ fx.func_180434_a(worldRenderer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY); - //#else - //$$ fx.renderParticle(tessellator, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY); - //#endif - //#endif -//$$ } -//$$ } -//#endif +// 1.8.9 and below diff --git a/src/main/java/com/replaymod/render/mixin/MixinEntityRenderer.java b/src/main/java/com/replaymod/render/mixin/MixinEntityRenderer.java index b82e5be6..559f2336 100644 --- a/src/main/java/com/replaymod/render/mixin/MixinEntityRenderer.java +++ b/src/main/java/com/replaymod/render/mixin/MixinEntityRenderer.java @@ -1,66 +1,11 @@ package com.replaymod.render.mixin; -import com.replaymod.core.versions.MCVer; -import com.replaymod.render.capturer.StereoscopicOpenGlFrameCapturer; import com.replaymod.render.hooks.EntityRendererHandler; -import net.minecraft.client.MinecraftClient; -import net.minecraft.entity.player.PlayerEntity; -import org.lwjgl.opengl.GL11; -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.Redirect; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; - -//#if MC>=11500 -import net.minecraft.client.util.math.MatrixStack; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -//#endif - -//#if MC>=11400 -import net.minecraft.client.render.Camera; -import net.minecraft.util.hit.HitResult; -//#endif - -//#if MC>=11400 -import net.minecraft.client.options.GameOptions; import net.minecraft.client.render.GameRenderer; -import net.minecraft.util.math.Matrix4f; -import net.minecraft.client.render.WorldRenderer; -//#else -//$$ import com.replaymod.replay.camera.CameraEntity; -//$$ import net.minecraft.client.renderer.EntityRenderer; -//$$ import net.minecraft.client.renderer.RenderGlobal; -//$$ import net.minecraft.client.settings.GameSettings; -//$$ import net.minecraft.entity.Entity; -//$$ import org.lwjgl.util.glu.Project; -//#endif +import org.spongepowered.asm.mixin.Mixin; -//#if MC>=10904 -import net.minecraft.world.RaycastContext; -//#else -//$$ import net.minecraft.util.MovingObjectPosition; -//#endif - -//#if MC>=10800 -import com.mojang.blaze3d.platform.GlStateManager; -//#else -//$$ import net.minecraft.entity.EntityLivingBase; -//$$ import com.replaymod.core.versions.MCVer.GlStateManager; -//#endif - -import static com.replaymod.core.versions.MCVer.*; - -//#if MC>=11400 -@Mixin(value = GameRenderer.class) -//#else -//$$ @Mixin(value = EntityRenderer.class) -//#endif +@Mixin(GameRenderer.class) public abstract class MixinEntityRenderer implements EntityRendererHandler.IEntityRenderer { - @Shadow - public MinecraftClient client; - private EntityRendererHandler replayModRender_handler; @Override @@ -72,207 +17,4 @@ public abstract class MixinEntityRenderer implements EntityRendererHandler.IEnti public EntityRendererHandler replayModRender_getHandler() { return replayModRender_handler; } - - //#if MC<=10710 - //$$ @Redirect(method = "renderWorld", at = @At(value = "INVOKE",target = - //$$ "Lnet/minecraft/client/renderer/RenderGlobal;updateRenderers(Lnet/minecraft/entity/EntityLivingBase;Z)Z")) - //$$ private boolean replayModRender_updateAllChunks(RenderGlobal self, EntityLivingBase view, boolean renderAllChunks) { - //$$ if (replayModRender_handler != null) { - //$$ renderAllChunks = true; - //$$ } - //$$ return self.updateRenderers(view, renderAllChunks); - //$$ } - //#endif - - // Moved to MixinFogRenderer in 1.13 - //#if MC<11400 - //$$ @Inject(method = "setupFog", at = @At("HEAD"), cancellable = true) - //$$ private void replayModRender_onSetupFog(int fogDistanceFlag, float partialTicks, CallbackInfo ci) { - //$$ if (replayModRender_handler == null) return; - //$$ if (replayModRender_handler.getSettings().getChromaKeyingColor() != null) { - //$$ ci.cancel(); - //$$ } - //$$ } - //#endif - - @Inject(method = "renderHand", at = @At("HEAD"), cancellable = true) - private void replayModRender_renderSpectatorHand( - //#if MC>=11500 - MatrixStack matrixStack, - //#endif - //#if MC>=11400 - Camera camera, - //#endif - float partialTicks, - //#if MC<11400 - //$$ int renderPass, - //#endif - CallbackInfo ci - ) { - if (replayModRender_handler != null) { - if (replayModRender_handler.omnidirectional) { - // No spectator hands during 360° view, we wouldn't even know where to put it - ci.cancel(); - //#if MC>=11400 - } - //#else - //$$ } else { - //$$ Entity currentEntity = getRenderViewEntity(MCVer.getMinecraft()); - //$$ if (currentEntity instanceof EntityPlayer && !(currentEntity instanceof CameraEntity)) { - //$$ if (renderPass == 2) { // Need to update render pass - //$$ renderPass = replayModRender_handler.data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE ? 1 : 0; - //$$ renderHand(partialTicks, renderPass); - //$$ ci.cancel(); - //$$ } // else, render normally - //$$ } - //$$ } - //#endif - } - } - - //#if MC<11400 - //$$ @Shadow - //$$ public abstract void renderHand(float partialTicks, int renderPass); - //#endif - - /* - * Stereoscopic Renderer - */ - - //#if MC>=11500 - @Inject(method = "method_22973", at = @At("RETURN"), cancellable = true) - private void replayModRender_setupStereoscopicProjection(CallbackInfoReturnable ci) { - if (replayModRender_getHandler() != null) { - Matrix4f offset; - if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE) { - offset = Matrix4f.translate(0.07f, 0, 0); - } else if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.RIGHT_EYE) { - offset = Matrix4f.translate(-0.07f, 0, 0); - } else { - return; - } - offset.multiply(ci.getReturnValue()); - ci.setReturnValue(offset); - } - } - //#else - //#if MC>=10800 - //$$ @Inject(method = "applyCameraTransformations", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/GlStateManager;loadIdentity()V", shift = At.Shift.AFTER, ordinal = 0)) - //#else - //$$ @Inject(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glLoadIdentity()V", shift = At.Shift.AFTER, ordinal = 0, remap = false)) - //#endif - //$$ private void replayModRender_setupStereoscopicProjection( - //$$ float partialTicks, - //#if MC<11400 - //$$ int renderPass, - //#endif - //$$ CallbackInfo ci - //$$ ) { - //$$ if (replayModRender_getHandler() != null) { - //$$ if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE) { - //$$ GL11.glTranslatef(0.07f, 0, 0); - //$$ } else if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.RIGHT_EYE) { - //$$ GL11.glTranslatef(-0.07f, 0, 0); - //$$ } - //$$ } - //$$ } - //#endif - - //#if MC>=11500 - @Inject(method = "renderWorld", at = @At("HEAD")) - private void replayModRender_setupStereoscopicProjection(float partialTicks, long frameStartNano, MatrixStack matrixStack, CallbackInfo ci) { - if (replayModRender_getHandler() != null) { - if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE) { - matrixStack.translate(0.1, 0, 0); - } else if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.RIGHT_EYE) { - matrixStack.translate(-0.1, 0, 0); - } - } - } - //#else - //#if MC>=10800 - //$$ @Inject(method = "applyCameraTransformations", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/GlStateManager;loadIdentity()V", shift = At.Shift.AFTER, ordinal = 1)) - //#else - //$$ @Inject(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glLoadIdentity()V", shift = At.Shift.AFTER, ordinal = 1, remap = false)) - //#endif - //$$ private void replayModRender_setupStereoscopicModelView( - //$$ float partialTicks, - //#if MC<11400 - //$$ int renderPass, - //#endif - //$$ CallbackInfo ci - //$$ ) { - //$$ if (replayModRender_getHandler() != null) { - //$$ if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE) { - //$$ GL11.glTranslatef(0.1f, 0, 0); - //$$ } else if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.RIGHT_EYE) { - //$$ GL11.glTranslatef(-0.1f, 0, 0); - //$$ } - //$$ } - //$$ } - //#endif - - /* - * Cubic Renderer - */ - - //#if MC>=11400 - @Redirect(method = "method_22973", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/Matrix4f;viewboxMatrix(DFFF)Lnet/minecraft/util/math/Matrix4f;")) - private Matrix4f replayModRender_perspective$0(double fovY, float aspect, float zNear, float zFar) { - return replayModRender_perspective((float) fovY, aspect, zNear, zFar); - } - - //#if MC<11500 - //#if MC>=11400 - //$$ @Redirect(method = "renderCenter", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/math/Matrix4f;method_4929(DFFF)Lnet/minecraft/client/util/math/Matrix4f;")) - //#else - //$$ @Redirect(method = "updateCameraAndRender(FJ)V", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/Matrix4f;perspective(DFFF)Lnet/minecraft/client/renderer/Matrix4f;")) - //#endif - //$$ private Matrix4f replayModRender_perspective$1(double fovY, float aspect, float zNear, float zFar) { - //$$ return replayModRender_perspective((float) fovY, aspect, zNear, zFar); - //$$ } - //$$ - //$$ @Redirect(method = "renderAboveClouds", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/math/Matrix4f;method_4929(DFFF)Lnet/minecraft/client/util/math/Matrix4f;")) - //$$ private Matrix4f replayModRender_perspective$2(double fovY, float aspect, float zNear, float zFar) { - //$$ return replayModRender_perspective((float) fovY, aspect, zNear, zFar); - //$$ } - //#endif - - private Matrix4f replayModRender_perspective(float fovY, float aspect, float zNear, float zFar) { - if (replayModRender_getHandler() != null && replayModRender_getHandler().omnidirectional) { - fovY = 90; - aspect = 1; - } - return Matrix4f.viewboxMatrix(fovY, aspect, zNear, zFar); - } - //#else - //$$ @Redirect(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false)) - //$$ private void replayModRender_gluPerspective$0(float fovY, float aspect, float zNear, float zFar) { - //$$ replayModRender_gluPerspective(fovY, aspect, zNear, zFar); - //$$ } - //$$ - //#if MC>=10800 - //$$ @Redirect(method = "renderWorldPass", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false)) - //#else - //$$ @Redirect(method = "renderHand", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false)) - //#endif - //$$ private void replayModRender_gluPerspective$1(float fovY, float aspect, float zNear, float zFar) { - //$$ replayModRender_gluPerspective(fovY, aspect, zNear, zFar); - //$$ } - //$$ - //#if MC>=10800 - //$$ @Redirect(method = "renderCloudsCheck", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false)) - //$$ private void replayModRender_gluPerspective$2(float fovY, float aspect, float zNear, float zFar) { - //$$ replayModRender_gluPerspective(fovY, aspect, zNear, zFar); - //$$ } - //#endif - //$$ - //$$ private void replayModRender_gluPerspective(float fovY, float aspect, float zNear, float zFar) { - //$$ if (replayModRender_getHandler() != null && replayModRender_getHandler().omnidirectional) { - //$$ fovY = 90; - //$$ aspect = 1; - //$$ } - //$$ Project.gluPerspective(fovY, aspect, zNear, zFar); - //$$ } - //#endif } diff --git a/src/main/java/com/replaymod/render/mixin/MixinGlStateManager.java b/src/main/java/com/replaymod/render/mixin/MixinGlStateManager.java index 84a06a3d..71aecaa2 100644 --- a/src/main/java/com/replaymod/render/mixin/MixinGlStateManager.java +++ b/src/main/java/com/replaymod/render/mixin/MixinGlStateManager.java @@ -1,4 +1,4 @@ -//#if MC>=10800 +//#if MC>=10800 && MC<11700 package com.replaymod.render.mixin; import com.replaymod.render.hooks.FogStateCallback; diff --git a/src/main/java/com/replaymod/render/mixin/MixinOpenGlHelper.java b/src/main/java/com/replaymod/render/mixin/MixinOpenGlHelper.java index 7c2380f2..743815c4 100644 --- a/src/main/java/com/replaymod/render/mixin/MixinOpenGlHelper.java +++ b/src/main/java/com/replaymod/render/mixin/MixinOpenGlHelper.java @@ -1,18 +1 @@ -//#if MC<=10710 -//$$ package com.replaymod.render.mixin; -//$$ -//$$ import com.replaymod.render.hooks.GLStateTracker; -//$$ import net.minecraft.client.renderer.OpenGlHelper; -//$$ import org.spongepowered.asm.mixin.Mixin; -//$$ import org.spongepowered.asm.mixin.injection.At; -//$$ import org.spongepowered.asm.mixin.injection.Inject; -//$$ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -//$$ -//$$ @Mixin(OpenGlHelper.class) -//$$ public abstract class MixinOpenGlHelper { -//$$ @Inject(method = "setActiveTexture", at = @At("RETURN")) -//$$ private static void replayModRender_trackActiveTexture(int magic, CallbackInfo ci) { -//$$ GLStateTracker.getInstance().updateActiveTexture(magic); -//$$ } -//$$ } -//#endif +// 1.7.10 and below diff --git a/src/main/java/com/replaymod/render/mixin/MixinRendererLivingEntity.java b/src/main/java/com/replaymod/render/mixin/MixinRendererLivingEntity.java deleted file mode 100644 index b936efda..00000000 --- a/src/main/java/com/replaymod/render/mixin/MixinRendererLivingEntity.java +++ /dev/null @@ -1,23 +0,0 @@ -package com.replaymod.render.mixin; - -//#if MC<10904 -//$$ import com.replaymod.render.hooks.EntityRendererHandler; -//$$ import net.minecraft.client.Minecraft; -//$$ import net.minecraft.client.renderer.entity.RendererLivingEntity; -//$$ import net.minecraft.entity.EntityLivingBase; -//$$ import org.spongepowered.asm.mixin.Mixin; -//$$ import org.spongepowered.asm.mixin.injection.At; -//$$ import org.spongepowered.asm.mixin.injection.Inject; -//$$ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -//$$ -//$$ @Mixin(RendererLivingEntity.class) -//$$ public abstract class MixinRendererLivingEntity { -//$$ @Inject(method = "canRenderName", at = @At("HEAD"), cancellable = true) -//$$ private void replayModRender_areAllNamesHidden(EntityLivingBase entity, CallbackInfoReturnable ci) { -//$$ EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler(); -//$$ if (handler != null && !handler.getSettings().isRenderNameTags()) { -//$$ ci.setReturnValue(false); //this calls the cancel method -//$$ } -//$$ } -//$$ } -//#endif diff --git a/src/main/java/com/replaymod/render/mixin/Mixin_AddIrisOdsShaderUniforms.java b/src/main/java/com/replaymod/render/mixin/Mixin_AddIrisOdsShaderUniforms.java new file mode 100644 index 00000000..e034a547 --- /dev/null +++ b/src/main/java/com/replaymod/render/mixin/Mixin_AddIrisOdsShaderUniforms.java @@ -0,0 +1,27 @@ +//#if MC>=11600 +package com.replaymod.render.mixin; + +import com.replaymod.render.capturer.IrisODSFrameCapturer; +import net.coderbot.iris.gl.uniform.UniformHolder; +import net.coderbot.iris.gl.uniform.UniformUpdateFrequency; +import net.coderbot.iris.uniforms.CommonUniforms; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +@Pseudo +@Mixin(value = CommonUniforms.class, remap = false) +public class Mixin_AddIrisOdsShaderUniforms { + // Using ModifyVariable, so we only depend on a single argument. Hoping that reduces the chance of breaking changes. + @ModifyVariable(method = "generalCommonUniforms", at = @At("HEAD"), argsOnly = true) + private static UniformHolder addReplayModOdsUniforms(UniformHolder uniforms) { + IrisODSFrameCapturer ods = IrisODSFrameCapturer.INSTANCE; + if (ods != null) { + uniforms.uniform1b(UniformUpdateFrequency.PER_FRAME, "leftEye", ods::isLeftEye); + uniforms.uniform1i(UniformUpdateFrequency.PER_FRAME, "direction", ods::getDirection); + } + return uniforms; + } +} +//#endif diff --git a/src/main/java/com/replaymod/render/mixin/Mixin_BlockOnChunkRebuilds.java b/src/main/java/com/replaymod/render/mixin/Mixin_BlockOnChunkRebuilds.java index dff22e27..a11818c4 100644 --- a/src/main/java/com/replaymod/render/mixin/Mixin_BlockOnChunkRebuilds.java +++ b/src/main/java/com/replaymod/render/mixin/Mixin_BlockOnChunkRebuilds.java @@ -2,7 +2,7 @@ package com.replaymod.render.mixin; //#if MC>=11500 import com.mojang.blaze3d.systems.RenderSystem; -import com.replaymod.render.hooks.ChunkLoadingRenderGlobal; +import com.replaymod.render.hooks.ForceChunkLoadingHook; import net.minecraft.client.render.chunk.BlockBufferBuilderStorage; import net.minecraft.client.render.chunk.ChunkBuilder; import net.minecraft.util.thread.TaskExecutor; @@ -21,7 +21,7 @@ import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; @Mixin(ChunkBuilder.class) -public abstract class Mixin_BlockOnChunkRebuilds implements ChunkLoadingRenderGlobal.IBlockOnChunkRebuilds { +public abstract class Mixin_BlockOnChunkRebuilds implements ForceChunkLoadingHook.IBlockOnChunkRebuilds { @Shadow @Final private Queue threadBuffers; @Shadow public abstract boolean upload(); diff --git a/src/main/java/com/replaymod/render/mixin/MixinFogRenderer.java b/src/main/java/com/replaymod/render/mixin/Mixin_ChromaKeyDisableFog.java similarity index 87% rename from src/main/java/com/replaymod/render/mixin/MixinFogRenderer.java rename to src/main/java/com/replaymod/render/mixin/Mixin_ChromaKeyDisableFog.java index 8249e6c8..48719c54 100644 --- a/src/main/java/com/replaymod/render/mixin/MixinFogRenderer.java +++ b/src/main/java/com/replaymod/render/mixin/Mixin_ChromaKeyDisableFog.java @@ -1,4 +1,3 @@ -//#if MC>=11400 package com.replaymod.render.mixin; import com.replaymod.core.versions.MCVer; @@ -9,12 +8,8 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -//#if MC>=11400 -import net.minecraft.client.render.Camera; -//#endif - @Mixin(BackgroundRenderer.class) -public abstract class MixinFogRenderer { +public abstract class Mixin_ChromaKeyDisableFog { @Inject(method = "applyFog", at = @At("HEAD"), cancellable = true) private //#if MC>=11500 @@ -29,4 +24,3 @@ public abstract class MixinFogRenderer { } } } -//#endif diff --git a/src/main/java/com/replaymod/render/mixin/Mixin_ForceChunkLoading.java b/src/main/java/com/replaymod/render/mixin/Mixin_ForceChunkLoading.java index f7b8b80b..3312a60e 100644 --- a/src/main/java/com/replaymod/render/mixin/Mixin_ForceChunkLoading.java +++ b/src/main/java/com/replaymod/render/mixin/Mixin_ForceChunkLoading.java @@ -1,10 +1,11 @@ package com.replaymod.render.mixin; -//#if MC>=10800 import com.replaymod.compat.shaders.ShaderReflection; -import com.replaymod.render.hooks.ChunkLoadingRenderGlobal; +import com.replaymod.render.hooks.ForceChunkLoadingHook; +import com.replaymod.render.hooks.IForceChunkLoading; import net.minecraft.client.render.Camera; import net.minecraft.client.render.Frustum; +import net.minecraft.client.render.WorldRenderer; import net.minecraft.client.render.chunk.ChunkBuilder; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; @@ -12,46 +13,23 @@ import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -//#if MC>=11500 import java.util.Set; -//#else -//$$ import net.minecraft.client.render.VisibleRegion; -//$$ import net.minecraft.entity.Entity; -//#endif -//#if MC>=11400 && MC<11500 -//$$ import net.minecraft.client.render.Camera; -//#endif - -//#if MC>=11400 -import net.minecraft.client.render.WorldRenderer; -//#else -//$$ import net.minecraft.client.renderer.RenderGlobal; -//#endif - -//#if MC<10904 -//$$ import net.minecraft.client.renderer.chunk.RenderChunk; -//$$ import net.minecraft.util.BlockPos; -//$$ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -//#endif - -//#if MC>=11400 @Mixin(WorldRenderer.class) -//#else -//$$ @Mixin(RenderGlobal.class) -//#endif -public abstract class Mixin_ForceChunkLoading { - public ChunkLoadingRenderGlobal replayModRender_hook; +public abstract class Mixin_ForceChunkLoading implements IForceChunkLoading { + private ForceChunkLoadingHook replayModRender_hook; + + @Override + public void replayModRender_setHook(ForceChunkLoadingHook hook) { + this.replayModRender_hook = hook; + } - //#if MC>=11500 @Shadow private Set chunksToRebuild; @Shadow private ChunkBuilder chunkBuilder; @Shadow private boolean needsTerrainUpdate; - @Shadow public abstract void scheduleTerrainUpdate(); - @Shadow protected abstract void setupTerrain(Camera camera_1, Frustum frustum_1, boolean boolean_1, int int_1, boolean boolean_2); @Shadow private int frame; @@ -88,7 +66,7 @@ public abstract class Mixin_ForceChunkLoading { this.chunksToRebuild.clear(); // Upload all chunks - this.needsTerrainUpdate |= ((ChunkLoadingRenderGlobal.IBlockOnChunkRebuilds) this.chunkBuilder).uploadEverythingBlocking(); + this.needsTerrainUpdate |= ((ForceChunkLoadingHook.IBlockOnChunkRebuilds) this.chunkBuilder).uploadEverythingBlocking(); // Repeat until no more updates are needed } while (this.needsTerrainUpdate); @@ -96,108 +74,4 @@ public abstract class Mixin_ForceChunkLoading { passThrough = false; } } - //#else - //$$ private boolean replayModRender_passThroughSetupTerrain; - //$$ - //$$ @Shadow - //$$ public boolean needsTerrainUpdate; - //$$ - //$$ @Shadow - //$$ public ChunkBuilder chunkBuilder; - //$$ - //$$ @Shadow - //$$ public abstract void setUpTerrain( - //#if MC>=11400 - //$$ Camera viewEntity, - //#else - //$$ Entity viewEntity, - //#if MC>=11400 - //$$ float partialTicks, - //#else - //$$ double partialTicks, - //#endif - //#endif - //$$ VisibleRegion camera, - //$$ int frameCount, - //$$ boolean playerSpectator - //$$ ); - //$$ - //$$ @Inject(method = "setUpTerrain", at = @At("HEAD"), cancellable = true) - //$$ private void replayModRender_setupTerrain( - //#if MC>=11400 - //$$ Camera viewEntity, - //#else - //$$ Entity viewEntity, - //#if MC>=11400 - //$$ float partialTicks, - //#else - //$$ double partialTicks, - //#endif - //#endif - //$$ VisibleRegion camera, - //$$ int frameCount, - //$$ boolean playerSpectator, - //$$ CallbackInfo ci - //$$ ) throws IllegalAccessException { - //$$ if (ShaderReflection.shaders_isShadowPass != null && (boolean) ShaderReflection.shaders_isShadowPass.get(null)) { - //$$ return; - //$$ } - //$$ if (replayModRender_hook != null && !replayModRender_passThroughSetupTerrain) { - //$$ replayModRender_passThroughSetupTerrain = true; - //$$ - //$$ do { - //$$ setUpTerrain( - //$$ viewEntity, - //#if MC<11400 - //$$ partialTicks, - //#endif - //$$ camera, - //$$ replayModRender_hook.nextFrameId(), - //$$ playerSpectator - //$$ ); - //$$ replayModRender_hook.updateChunks(); - //$$ } while (this.needsTerrainUpdate); - //$$ - //$$ this.needsTerrainUpdate = true; - //$$ - //$$ replayModRender_passThroughSetupTerrain = false; - //$$ ci.cancel(); - //$$ } - //$$ } - //$$ - //#if MC<10904 - //$$ @Inject(method = "isPositionInRenderChunk", at = @At("HEAD"), cancellable = true) - //$$ public void replayModRender_isPositionInRenderChunk(BlockPos pos, RenderChunk chunk, CallbackInfoReturnable ci) { - //$$ if (replayModRender_hook != null) { - //$$ ci.setReturnValue(true); - //$$ } - //$$ } - //#endif - //$$ - //$$ @Inject(method = "updateChunks", at = @At("HEAD"), cancellable = true) - //$$ public void replayModRender_updateChunks(long finishTimeNano, CallbackInfo ci) { - //$$ if (replayModRender_hook != null) { - //$$ replayModRender_hook.updateChunks(); - //$$ ci.cancel(); - //$$ } - //$$ } - //$$ - //$$ // Prior to 1.9.4, MC always uses the same ChunkRenderDispatcher instance - //#if MC>=10904 - //$$ @Inject(method = "setWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/chunk/ChunkBuilder;stop()V")) - //$$ private void stopWorkerThreadsAndChunkLoadingRenderGlobal(CallbackInfo ci) { - //$$ if (replayModRender_hook != null) { - //$$ replayModRender_hook.updateRenderDispatcher(null); - //$$ } - //$$ } - //#endif - //$$ - //$$ @Inject(method = "reload", at = @At(value = "RETURN")) - //$$ private void setupChunkLoadingRenderGlobal(CallbackInfo ci) { - //$$ if (replayModRender_hook != null) { - //$$ replayModRender_hook.updateRenderDispatcher(this.chunkBuilder); - //$$ } - //$$ } - //#endif } -//#endif diff --git a/src/main/java/com/replaymod/render/mixin/MixinRender.java b/src/main/java/com/replaymod/render/mixin/Mixin_HideNameTags.java similarity index 96% rename from src/main/java/com/replaymod/render/mixin/MixinRender.java rename to src/main/java/com/replaymod/render/mixin/Mixin_HideNameTags.java index 39473e39..6377afda 100644 --- a/src/main/java/com/replaymod/render/mixin/MixinRender.java +++ b/src/main/java/com/replaymod/render/mixin/Mixin_HideNameTags.java @@ -9,7 +9,7 @@ import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(EntityRenderer.class) -public abstract class MixinRender { +public abstract class Mixin_HideNameTags { //#if MC>=11500 @Inject(method = "renderLabelIfPresent", at = @At("HEAD"), cancellable = true) //#else diff --git a/src/main/java/com/replaymod/render/mixin/Mixin_HideNameTags_LivingEntity.java b/src/main/java/com/replaymod/render/mixin/Mixin_HideNameTags_LivingEntity.java new file mode 100644 index 00000000..cafbe9f8 --- /dev/null +++ b/src/main/java/com/replaymod/render/mixin/Mixin_HideNameTags_LivingEntity.java @@ -0,0 +1,9 @@ +package com.replaymod.render.mixin; + +import net.minecraft.client.render.entity.LivingEntityRenderer; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(LivingEntityRenderer.class) +public abstract class Mixin_HideNameTags_LivingEntity { + // 1.8.9 and below +} diff --git a/src/main/java/com/replaymod/render/mixin/Mixin_LoadIrisOdsShaderPack.java b/src/main/java/com/replaymod/render/mixin/Mixin_LoadIrisOdsShaderPack.java new file mode 100644 index 00000000..d8756e7b --- /dev/null +++ b/src/main/java/com/replaymod/render/mixin/Mixin_LoadIrisOdsShaderPack.java @@ -0,0 +1,29 @@ +//#if MC>=11600 +package com.replaymod.render.mixin; + +import com.replaymod.render.capturer.IrisODSFrameCapturer; +import net.coderbot.iris.Iris; +import net.fabricmc.loader.api.FabricLoader; +import org.objectweb.asm.Opcodes; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import java.nio.file.Path; + +@Pseudo +@Mixin(value = Iris.class, remap = false) +public class Mixin_LoadIrisOdsShaderPack { + @Redirect(method = "loadExternalShaderpack", at = @At(value = "FIELD", opcode = Opcodes.GETSTATIC, target = "Lnet/coderbot/iris/Iris;SHADERPACKS_DIRECTORY:Ljava/nio/file/Path;")) + private static Path loadReplayModOdsPack(String name) { + if (IrisODSFrameCapturer.INSTANCE != null && IrisODSFrameCapturer.SHADER_PACK_NAME.equals(name)) { + return FabricLoader.getInstance().getModContainer("replaymod") + .orElseThrow(() -> new RuntimeException("Failed to get mod container for ReplayMod")) + .getRootPath(); + } else { + return Iris.SHADERPACKS_DIRECTORY; + } + } +} +//#endif diff --git a/src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_Camera.java b/src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_Camera.java new file mode 100644 index 00000000..fb58ab87 --- /dev/null +++ b/src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_Camera.java @@ -0,0 +1,24 @@ +package com.replaymod.render.mixin; + +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.util.math.Matrix4f; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(GameRenderer.class) +public abstract class Mixin_Omnidirectional_Camera implements EntityRendererHandler.IEntityRenderer { + @Redirect(method = "method_22973", at = @At(value = "INVOKE", target = "Lnet/minecraft/util/math/Matrix4f;viewboxMatrix(DFFF)Lnet/minecraft/util/math/Matrix4f;")) + private Matrix4f replayModRender_perspective$0(double fovY, float aspect, float zNear, float zFar) { + return replayModRender_perspective((float) fovY, aspect, zNear, zFar); + } + + private Matrix4f replayModRender_perspective(float fovY, float aspect, float zNear, float zFar) { + if (replayModRender_getHandler() != null && replayModRender_getHandler().omnidirectional) { + fovY = 90; + aspect = 1; + } + return Matrix4f.viewboxMatrix(fovY, aspect, zNear, zFar); + } +} diff --git a/src/main/java/com/replaymod/render/mixin/MixinFrustum.java b/src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_DisableFrustumCulling.java similarity index 95% rename from src/main/java/com/replaymod/render/mixin/MixinFrustum.java rename to src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_DisableFrustumCulling.java index c23ac874..f174e0a3 100644 --- a/src/main/java/com/replaymod/render/mixin/MixinFrustum.java +++ b/src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_DisableFrustumCulling.java @@ -18,7 +18,7 @@ import net.minecraft.client.render.Frustum; //#else //$$ @Mixin(Frustrum.class) //#endif -public abstract class MixinFrustum { +public abstract class Mixin_Omnidirectional_DisableFrustumCulling { //#if MC>=11500 @Inject(method = "isAnyCornerVisible", at = @At("HEAD"), cancellable = true) //#else diff --git a/src/main/java/com/replaymod/render/mixin/Mixin_CubicRotation.java b/src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_Rotation.java similarity index 98% rename from src/main/java/com/replaymod/render/mixin/Mixin_CubicRotation.java rename to src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_Rotation.java index 089d41fc..6107028e 100644 --- a/src/main/java/com/replaymod/render/mixin/Mixin_CubicRotation.java +++ b/src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_Rotation.java @@ -25,7 +25,7 @@ import static com.replaymod.core.versions.MCVer.getMinecraft; //$$ @Mixin(value = net.minecraft.client.renderer.EntityRenderer.class) //#endif //#endif -public abstract class Mixin_CubicRotation { +public abstract class Mixin_Omnidirectional_Rotation { private EntityRendererHandler getHandler() { return ((EntityRendererHandler.IEntityRenderer) getMinecraft().gameRenderer).replayModRender_getHandler(); } diff --git a/src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_SkipHand.java b/src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_SkipHand.java new file mode 100644 index 00000000..939f6ec8 --- /dev/null +++ b/src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_SkipHand.java @@ -0,0 +1,34 @@ +package com.replaymod.render.mixin; + +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.render.Camera; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.util.math.MatrixStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(GameRenderer.class) +public abstract class Mixin_Omnidirectional_SkipHand implements EntityRendererHandler.IEntityRenderer { + @Inject(method = "renderHand", at = @At("HEAD"), cancellable = true) + private void replayModRender_renderSpectatorHand( + //#if MC>=11500 + MatrixStack matrixStack, + //#endif + //#if MC>=11400 + Camera camera, + //#endif + float partialTicks, + //#if MC<11400 + //$$ int renderPass, + //#endif + CallbackInfo ci + ) { + EntityRendererHandler handler = replayModRender_getHandler(); + if (handler != null && handler.omnidirectional) { + // No spectator hands during 360° view, we wouldn't even know where to put it + ci.cancel(); + } + } +} diff --git a/src/main/java/com/replaymod/render/mixin/Mixin_PreserveDepthDuringGuiRendering.java b/src/main/java/com/replaymod/render/mixin/Mixin_PreserveDepthDuringGuiRendering.java new file mode 100644 index 00000000..4e380f62 --- /dev/null +++ b/src/main/java/com/replaymod/render/mixin/Mixin_PreserveDepthDuringGuiRendering.java @@ -0,0 +1,28 @@ +package com.replaymod.render.mixin; + +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.render.GameRenderer; +import org.lwjgl.opengl.GL11; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@Mixin(GameRenderer.class) +public abstract class Mixin_PreserveDepthDuringGuiRendering { + @ModifyArg( + //#if MC>=11400 + method = "render", + at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/systems/RenderSystem;clear(IZ)V"), index = 0 + //#else + //$$ method = "setupOverlayRendering", + //$$ at = @At(value = "INVOKE", target = "Lnet/minecraft/client/renderer/GlStateManager;clear(I)V"), index = 0 + //#endif + ) + private int replayModRender_skipClearWhenRecordingDepth(int mask) { + EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) this).replayModRender_getHandler(); + if (handler != null && handler.getSettings().isDepthMap()) { + mask = mask & ~GL11.GL_DEPTH_BUFFER_BIT; + } + return mask; + } +} diff --git a/src/main/java/com/replaymod/render/mixin/Mixin_SkipHudDuringRender.java b/src/main/java/com/replaymod/render/mixin/Mixin_SkipHudDuringRender.java new file mode 100644 index 00000000..328b726d --- /dev/null +++ b/src/main/java/com/replaymod/render/mixin/Mixin_SkipHudDuringRender.java @@ -0,0 +1,23 @@ +package com.replaymod.render.mixin; + +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.gui.hud.InGameHud; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +//#if MC>=11400 +@Mixin(InGameHud.class) +//#else +//$$ @Mixin({ GuiIngame.class, net.minecraftforge.client.GuiIngameForge.class }) +//#endif +public abstract class Mixin_SkipHudDuringRender { + @Inject(method = "render", at = @At("HEAD"), cancellable = true) + private void replayModRender_skipHudDuringRender(CallbackInfo ci) { + if (((EntityRendererHandler.IEntityRenderer) MinecraftClient.getInstance().gameRenderer).replayModRender_getHandler() != null) { + ci.cancel(); + } + } +} diff --git a/src/main/java/com/replaymod/render/mixin/MixinCamera.java b/src/main/java/com/replaymod/render/mixin/Mixin_StabilizeCamera.java similarity index 91% rename from src/main/java/com/replaymod/render/mixin/MixinCamera.java rename to src/main/java/com/replaymod/render/mixin/Mixin_StabilizeCamera.java index 63044901..1ff658ac 100644 --- a/src/main/java/com/replaymod/render/mixin/MixinCamera.java +++ b/src/main/java/com/replaymod/render/mixin/Mixin_StabilizeCamera.java @@ -24,7 +24,7 @@ import net.minecraft.world.BlockView; //#else //$$ @Mixin(value = EntityRenderer.class) //#endif -public abstract class MixinCamera { +public abstract class Mixin_StabilizeCamera { private EntityRendererHandler getHandler() { return ((EntityRendererHandler.IEntityRenderer) getMinecraft().gameRenderer).replayModRender_getHandler(); } @@ -59,7 +59,7 @@ public abstract class MixinCamera { ) { if (getHandler() != null) { //#if MC<11400 - //$$ Entity entity = getRenderViewEntity(getMinecraft()); + //$$ Entity entity = getMinecraft().getRenderViewEntity(); //#endif orgYaw = entity.yaw; orgPitch = entity.pitch; @@ -79,17 +79,19 @@ public abstract class MixinCamera { //#endif if (getHandler() != null) { //#if MC<11400 - //$$ Entity entity = getRenderViewEntity(getMinecraft()); + //$$ Entity entity = getMinecraft().getRenderViewEntity(); //#endif RenderSettings settings = getHandler().getSettings(); if (settings.isStabilizeYaw()) { - entity.prevYaw = entity.yaw = 0; + entity.prevYaw = 0; + entity.yaw = 0; if (entity instanceof LivingEntity) { ((LivingEntity) entity).prevHeadYaw = ((LivingEntity) entity).headYaw = 0; } } if (settings.isStabilizePitch()) { - entity.prevPitch = entity.pitch = 0; + entity.prevPitch = 0; + entity.pitch = 0; } if (settings.isStabilizeRoll() && entity instanceof CameraEntity) { ((CameraEntity) entity).roll = 0; @@ -117,7 +119,7 @@ public abstract class MixinCamera { ) { if (getHandler() != null) { //#if MC<11400 - //$$ Entity entity = getRenderViewEntity(getMinecraft()); + //$$ Entity entity = getMinecraft().getRenderViewEntity(); //#endif entity.yaw = orgYaw; entity.pitch = orgPitch; diff --git a/src/main/java/com/replaymod/render/mixin/Mixin_Stereoscopic_Camera.java b/src/main/java/com/replaymod/render/mixin/Mixin_Stereoscopic_Camera.java new file mode 100644 index 00000000..945ac107 --- /dev/null +++ b/src/main/java/com/replaymod/render/mixin/Mixin_Stereoscopic_Camera.java @@ -0,0 +1,42 @@ +package com.replaymod.render.mixin; + +import com.replaymod.render.capturer.StereoscopicOpenGlFrameCapturer; +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.util.math.Matrix4f; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(GameRenderer.class) +public abstract class Mixin_Stereoscopic_Camera implements EntityRendererHandler.IEntityRenderer { + @Inject(method = "method_22973", at = @At("RETURN"), cancellable = true) + private void replayModRender_setupStereoscopicProjection(CallbackInfoReturnable ci) { + if (replayModRender_getHandler() != null) { + Matrix4f offset; + if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE) { + offset = Matrix4f.translate(0.07f, 0, 0); + } else if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.RIGHT_EYE) { + offset = Matrix4f.translate(-0.07f, 0, 0); + } else { + return; + } + offset.multiply(ci.getReturnValue()); + ci.setReturnValue(offset); + } + } + + @Inject(method = "renderWorld", at = @At("HEAD")) + private void replayModRender_setupStereoscopicProjection(float partialTicks, long frameStartNano, MatrixStack matrixStack, CallbackInfo ci) { + if (replayModRender_getHandler() != null) { + if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE) { + matrixStack.translate(0.1, 0, 0); + } else if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.RIGHT_EYE) { + matrixStack.translate(-0.1, 0, 0); + } + } + } +} diff --git a/src/main/java/com/replaymod/render/mixin/Mixin_Stereoscopic_HandRenderPass.java b/src/main/java/com/replaymod/render/mixin/Mixin_Stereoscopic_HandRenderPass.java new file mode 100644 index 00000000..c4facf6d --- /dev/null +++ b/src/main/java/com/replaymod/render/mixin/Mixin_Stereoscopic_HandRenderPass.java @@ -0,0 +1,10 @@ +package com.replaymod.render.mixin; + +import net.minecraft.client.render.GameRenderer; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(GameRenderer.class) +public abstract class Mixin_Stereoscopic_HandRenderPass { + // MC's builtin stereoscopic 3D feature was dropped + // 1.12.2 and before +} diff --git a/src/main/java/com/replaymod/render/rendering/Pipeline.java b/src/main/java/com/replaymod/render/rendering/Pipeline.java index 23c22895..cf7f057d 100644 --- a/src/main/java/com/replaymod/render/rendering/Pipeline.java +++ b/src/main/java/com/replaymod/render/rendering/Pipeline.java @@ -6,14 +6,9 @@ import com.replaymod.render.capturer.WorldRenderer; import com.replaymod.render.frame.BitmapFrame; import com.replaymod.render.processor.GlToAbsoluteDepthProcessor; import net.minecraft.client.MinecraftClient; -import net.minecraft.util.crash.CrashReport; import net.minecraft.util.crash.CrashException; - -//#if MC>=11400 +import net.minecraft.util.crash.CrashReport; import org.lwjgl.glfw.GLFW; -//#else -//$$ import org.lwjgl.opengl.Display; -//#endif import java.util.HashMap; import java.util.Map; @@ -23,9 +18,6 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import static com.replaymod.core.versions.MCVer.getMinecraft; -//#if MC>=11400 -import static com.replaymod.core.versions.MCVer.getWindow; -//#endif public class Pipeline implements Runnable { @@ -72,11 +64,7 @@ public class Pipeline implements Runnable { MinecraftClient mc = MCVer.getMinecraft(); while (!capturer.isDone() && !abort) { - //#if MC>=11400 - if (GLFW.glfwWindowShouldClose(getWindow(mc).getHandle()) || ((MinecraftAccessor) mc).getCrashReporter() != null) { - //#else - //$$ if (Display.isCloseRequested() || ((MinecraftAccessor) mc).getCrashReporter() != null) { - //#endif + if (GLFW.glfwWindowShouldClose(mc.getWindow().getHandle()) || ((MinecraftAccessor) mc).getCrashReporter() != null) { processService.shutdown(); return; } diff --git a/src/main/java/com/replaymod/render/rendering/Pipelines.java b/src/main/java/com/replaymod/render/rendering/Pipelines.java index 62a2b502..e012fb41 100644 --- a/src/main/java/com/replaymod/render/rendering/Pipelines.java +++ b/src/main/java/com/replaymod/render/rendering/Pipelines.java @@ -105,8 +105,14 @@ public class Pipelines { ODSToBitmapProcessor processor = new ODSToBitmapProcessor(settings.getVideoWidth(), settings.getVideoHeight(), settings.getSphericalFovX()); - FrameCapturer capturer = - new ODSFrameCapturer(worldRenderer, renderInfo, processor.getFrameSize()); + //#if MC>=11600 + boolean iris = net.fabricmc.loader.api.FabricLoader.getInstance().isModLoaded("iris"); + FrameCapturer capturer = iris + ? new com.replaymod.render.capturer.IrisODSFrameCapturer(worldRenderer, renderInfo, processor.getFrameSize()) + : new ODSFrameCapturer(worldRenderer, renderInfo, processor.getFrameSize()); + //#else + //$$ FrameCapturer capturer = new ODSFrameCapturer(worldRenderer, renderInfo, processor.getFrameSize()); + //#endif return new Pipeline<>(worldRenderer, capturer, processor, consumer); } diff --git a/src/main/java/com/replaymod/render/rendering/VideoRenderer.java b/src/main/java/com/replaymod/render/rendering/VideoRenderer.java index 46f56a04..dfa7e096 100644 --- a/src/main/java/com/replaymod/render/rendering/VideoRenderer.java +++ b/src/main/java/com/replaymod/render/rendering/VideoRenderer.java @@ -1,5 +1,6 @@ package com.replaymod.render.rendering; +import com.mojang.blaze3d.platform.GlStateManager; import com.replaymod.core.mixin.MinecraftAccessor; import com.replaymod.core.mixin.TimerAccessor; import com.replaymod.core.utils.WrappedTimer; @@ -8,6 +9,7 @@ import com.replaymod.pathing.player.AbstractTimelinePlayer; import com.replaymod.pathing.player.ReplayTimer; import com.replaymod.pathing.properties.TimestampProperty; import com.replaymod.render.CameraPathExporter; +import com.replaymod.render.PNGWriter; import com.replaymod.render.RenderSettings; import com.replaymod.render.ReplayModRender; import com.replaymod.render.FFmpegWriter; @@ -17,8 +19,11 @@ import com.replaymod.render.events.ReplayRenderCallback; import com.replaymod.render.frame.BitmapFrame; import com.replaymod.render.gui.GuiRenderingDone; import com.replaymod.render.gui.GuiVideoRenderer; +import com.replaymod.render.hooks.ForceChunkLoadingHook; import com.replaymod.render.metadata.MetadataInjector; +import com.replaymod.render.mixin.MainWindowAccessor; import com.replaymod.render.mixin.WorldRendererAccessor; +import com.replaymod.render.utils.FlawlessFrames; import com.replaymod.replay.ReplayHandler; import com.replaymod.replaystudio.pathing.path.Keyframe; import com.replaymod.replaystudio.pathing.path.Path; @@ -28,10 +33,20 @@ import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; import net.minecraft.client.MinecraftClient; import com.mojang.blaze3d.platform.GLX; import net.minecraft.client.gl.Framebuffer; +import net.minecraft.client.sound.PositionedSoundInstance; +import net.minecraft.client.util.Window; +import net.minecraft.sound.SoundEvent; import net.minecraft.util.Identifier; import net.minecraft.util.crash.CrashException; import net.minecraft.sound.SoundCategory; import net.minecraft.client.render.RenderTickCounter; +import org.lwjgl.glfw.GLFW; + +//#if MC>=11700 +//$$ import net.minecraft.client.gl.WindowFramebuffer; +//$$ import net.minecraft.client.render.DiffuseLighting; +//$$ import net.minecraft.util.math.Matrix4f; +//#endif //#if MC>=11600 import net.minecraft.client.util.math.MatrixStack; @@ -39,39 +54,33 @@ import net.minecraft.client.util.math.MatrixStack; //#if MC>=11500 import com.mojang.blaze3d.systems.RenderSystem; -import net.minecraft.client.util.Window; import org.lwjgl.opengl.GL11; //#endif //#if MC>=11400 import com.replaymod.render.EXRWriter; -import com.replaymod.render.mixin.MainWindowAccessor; import net.minecraft.client.gui.screen.Screen; -import org.lwjgl.glfw.GLFW; import java.util.concurrent.CompletableFuture; //#else -//$$ import net.minecraft.client.gui.ScaledResolution; //$$ import org.lwjgl.input.Mouse; //$$ import org.lwjgl.opengl.Display; -//$$ import static com.replaymod.core.versions.MCVer.newScaledResolution; //#endif //#if MC>=10800 -import com.replaymod.render.hooks.ChunkLoadingRenderGlobal; -import static com.mojang.blaze3d.platform.GlStateManager.*; //#else //$$ import com.replaymod.replay.gui.screen.GuiOpeningReplay; -//$$ import static com.replaymod.core.versions.MCVer.GlStateManager.*; //#endif import java.io.IOException; import java.util.Collection; import java.util.EnumMap; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.Queue; import java.util.concurrent.Future; import java.util.concurrent.FutureTask; +import java.util.stream.Stream; import static com.google.common.collect.Iterables.getLast; import static com.replaymod.core.versions.MCVer.*; @@ -80,6 +89,7 @@ import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; public class VideoRenderer implements RenderInfo { + private static final Identifier SOUND_RENDER_SUCCESS = new Identifier("replaymod", "render_success"); private final MinecraftClient mc = MCVer.getMinecraft(); private final RenderSettings settings; private final ReplayHandler replayHandler; @@ -95,9 +105,7 @@ public class VideoRenderer implements RenderInfo { private TimelinePlayer timelinePlayer; private Future timelinePlayerFuture; - //#if MC>=10800 - private ChunkLoadingRenderGlobal chunkLoadingRenderGlobal; - //#endif + private ForceChunkLoadingHook forceChunkLoadingHook; //#if MC<10800 //$$ private GuiOpeningReplay guiOpeningReplay; //#endif @@ -126,15 +134,17 @@ public class VideoRenderer implements RenderInfo { } else { FrameConsumer frameConsumer; if (settings.getEncodingPreset() == RenderSettings.EncodingPreset.EXR) { - ffmpegWriter = null; //#if MC>=11400 frameConsumer = new EXRWriter(settings.getOutputFile().toPath()); //#else //$$ throw new UnsupportedOperationException("EXR requires LWJGL3"); //#endif + } else if (settings.getEncodingPreset() == RenderSettings.EncodingPreset.PNG) { + frameConsumer = new PNGWriter(settings.getOutputFile().toPath()); } else { - frameConsumer = ffmpegWriter = new FFmpegWriter(this); + frameConsumer = new FFmpegWriter(this); } + ffmpegWriter = frameConsumer instanceof FFmpegWriter ? (FFmpegWriter) frameConsumer : null; FrameConsumer previewingFrameConsumer = new FrameConsumer() { @Override public void consume(Map channels) { @@ -234,19 +244,11 @@ public class VideoRenderer implements RenderInfo { @Override public float updateForNextFrame() { // because the jGui lib uses Minecraft's displayWidth and displayHeight values, update these temporarily - //#if MC>=11400 - int displayWidthBefore = getWindow(mc).getFramebufferWidth(); - int displayHeightBefore = getWindow(mc).getFramebufferHeight(); - //noinspection ConstantConditions - MainWindowAccessor acc = (MainWindowAccessor) (Object) getWindow(mc); + MainWindowAccessor acc = (MainWindowAccessor) (Object) mc.getWindow(); + int displayWidthBefore = acc.getFramebufferWidth(); + int displayHeightBefore = acc.getFramebufferHeight(); acc.setFramebufferWidth(displayWidth); acc.setFramebufferHeight(displayHeight); - //#else - //$$ int displayWidthBefore = mc.displayWidth; - //$$ int displayHeightBefore = mc.displayHeight; - //$$ mc.displayWidth = displayWidth; - //$$ mc.displayHeight = displayHeight; - //#endif if (!settings.isHighPerformance() || framesDone % fps == 0) { while (drawGui() && paused) { @@ -287,13 +289,8 @@ public class VideoRenderer implements RenderInfo { } // change Minecraft's display size back - //#if MC>=11400 acc.setFramebufferWidth(displayWidthBefore); acc.setFramebufferHeight(displayHeightBefore); - //#else - //$$ mc.displayWidth = displayWidthBefore; - //$$ mc.displayHeight = displayHeightBefore; - //#endif if (cameraPathExporter != null) { cameraPathExporter.recordFrame(timer.tickDelta); @@ -366,21 +363,20 @@ public class VideoRenderer implements RenderInfo { updateDisplaySize(); - //#if MC<=10809 - //$$ ScaledResolution scaled = newScaledResolution(mc); - //$$ gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight()); - //#endif + gui.toMinecraft().init(mc, mc.getWindow().getScaledWidth(), mc.getWindow().getScaledHeight()); - //#if MC>=10800 - chunkLoadingRenderGlobal = new ChunkLoadingRenderGlobal(mc.worldRenderer); - //#endif + forceChunkLoadingHook = new ForceChunkLoadingHook(mc.worldRenderer); // Set up our own framebuffer to render the GUI to + //#if MC>=11700 + //$$ guiFramebuffer = new WindowFramebuffer(displayWidth, displayHeight); + //#else guiFramebuffer = new Framebuffer(displayWidth, displayHeight, true //#if MC>=11400 , false //#endif ); + //#endif } private void finish() { @@ -408,11 +404,7 @@ public class VideoRenderer implements RenderInfo { mc.options.setSoundVolume(entry.getKey(), entry.getValue()); } mc.openScreen(null); - //#if MC>=10800 - if (chunkLoadingRenderGlobal != null) { - chunkLoadingRenderGlobal.uninstall(); - } - //#endif + forceChunkLoadingHook.uninstall(); if (!hasFailed() && cameraPathExporter != null) { try { @@ -422,7 +414,7 @@ public class VideoRenderer implements RenderInfo { } } - MCVer.playSound(new Identifier("replaymod", "render_success")); + mc.getSoundManager().play(PositionedSoundInstance.master(new SoundEvent(SOUND_RENDER_SUCCESS), 1)); try { if (!hasFailed() && ffmpegWriter != null) { @@ -433,28 +425,13 @@ public class VideoRenderer implements RenderInfo { } // Finally, resize the Minecraft framebuffer to the actual width/height of the window - //#if MC>=11400 - mc.getFramebuffer().resize(displayWidth, displayHeight - //#if MC>=11400 - , false - //#endif - ); - //noinspection ConstantConditions - MainWindowAccessor acc = (MainWindowAccessor) (Object) getWindow(mc); - acc.setFramebufferWidth(displayWidth); - acc.setFramebufferHeight(displayHeight); - //#if MC>=11500 - mc.gameRenderer.onResized(displayWidth, displayHeight); - //#endif - //#else - //$$ mc.resize(displayWidth, displayHeight); - //#endif + resizeMainWindow(mc, displayWidth, displayHeight); } private void executeTaskQueue() { //#if MC>=11400 while (true) { - while (mc.overlay != null) { + while (mc.getOverlay() != null) { drawGui(); ((MinecraftMethodAccessor) mc).replayModExecuteTaskQueue(); } @@ -500,12 +477,9 @@ public class VideoRenderer implements RenderInfo { } public boolean drawGui() { + Window window = mc.getWindow(); do { - //#if MC>=11400 - if (GLFW.glfwWindowShouldClose(getWindow(mc).getHandle()) || ((MinecraftAccessor) mc).getCrashReporter() != null) { - //#else - //$$ if (Display.isCloseRequested() || ((MinecraftAccessor) mc).getCrashReporter() != null) { - //#endif + if (GLFW.glfwWindowShouldClose(window.getHandle()) || ((MinecraftAccessor) mc).getCrashReporter() != null) { return false; } @@ -524,26 +498,41 @@ public class VideoRenderer implements RenderInfo { } pushMatrix(); - clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT + GlStateManager.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT //#if MC>=11400 , false //#endif ); - enableTexture(); + GlStateManager.enableTexture(); guiFramebuffer.beginWrite(true); //#if MC>=11500 - Window window = getWindow(mc); RenderSystem.clear(256, MinecraftClient.IS_SYSTEM_MAC); + //#if MC>=11700 + //$$ RenderSystem.setProjectionMatrix(Matrix4f.projectionMatrix( + //$$ 0, + //$$ (float) (window.getFramebufferWidth() / window.getScaleFactor()), + //$$ 0, + //$$ (float) (window.getFramebufferHeight() / window.getScaleFactor()), + //$$ 1000, + //$$ 3000 + //$$ )); + //$$ MatrixStack matrixStack = RenderSystem.getModelViewStack(); + //$$ matrixStack.loadIdentity(); + //$$ matrixStack.translate(0, 0, -2000); + //$$ RenderSystem.applyModelViewMatrix(); + //$$ DiffuseLighting.enableGuiDepthLighting(); + //#else RenderSystem.matrixMode(GL11.GL_PROJECTION); RenderSystem.loadIdentity(); RenderSystem.ortho(0, window.getFramebufferWidth() / window.getScaleFactor(), window.getFramebufferHeight() / window.getScaleFactor(), 0, 1000, 3000); RenderSystem.matrixMode(GL11.GL_MODELVIEW); RenderSystem.loadIdentity(); RenderSystem.translatef(0, 0, -2000); + //#endif //#else //#if MC>=11400 - //$$ getWindow(mc).method_4493( + //$$ window.method_4493( //#if MC>=11400 //$$ false //#endif @@ -553,12 +542,7 @@ public class VideoRenderer implements RenderInfo { //#endif //#endif - //#if MC>=11400 - gui.toMinecraft().init(mc, getWindow(mc).getScaledWidth(), getWindow(mc).getScaledHeight()); - //#else - //$$ ScaledResolution scaled = newScaledResolution(mc); - //$$ gui.toMinecraft().setWorldAndResolution(mc, scaled.getScaledWidth(), scaled.getScaledHeight()); - //#endif + gui.toMinecraft().init(mc, window.getScaledWidth(), window.getScaledHeight()); // Events are polled on 1.13+ in mainWindow.update which is called later //#if MC<11400 @@ -576,14 +560,14 @@ public class VideoRenderer implements RenderInfo { //#endif //#if MC>=11400 - int mouseX = (int) mc.mouse.getX() * getWindow(mc).getScaledWidth() / displayWidth; - int mouseY = (int) mc.mouse.getY() * getWindow(mc).getScaledHeight() / displayHeight; + int mouseX = (int) mc.mouse.getX() * window.getScaledWidth() / displayWidth; + int mouseY = (int) mc.mouse.getY() * window.getScaledHeight() / displayHeight; - if (mc.overlay != null) { + if (mc.getOverlay() != null) { Screen orgScreen = mc.currentScreen; try { mc.currentScreen = gui.toMinecraft(); - mc.overlay.render( + mc.getOverlay().render( //#if MC>=11600 new MatrixStack(), //#endif @@ -600,8 +584,8 @@ public class VideoRenderer implements RenderInfo { mouseX, mouseY, 0); } //#else - //$$ int mouseX = Mouse.getX() * scaled.getScaledWidth() / mc.displayWidth; - //$$ int mouseY = scaled.getScaledHeight() - Mouse.getY() * scaled.getScaledHeight() / mc.displayHeight - 1; + //$$ int mouseX = Mouse.getX() * window.getScaledWidth() / mc.displayWidth; + //$$ int mouseY = window.getScaledHeight() - Mouse.getY() * window.getScaledHeight() / mc.displayHeight - 1; //$$ //$$ gui.toMinecraft().updateScreen(); //$$ gui.toMinecraft().drawScreen(mouseX, mouseY, 0); @@ -614,10 +598,10 @@ public class VideoRenderer implements RenderInfo { popMatrix(); //#if MC>=11500 - getWindow(mc).swapBuffers(); + window.swapBuffers(); //#else //#if MC>=11400 - //$$ getWindow(mc).setFullscreen(false); + //$$ window.setFullscreen(false); //#else //$$ // if not in high performance mode, update the gui size if screen size changed //$$ // otherwise just swap the progress gui to screen @@ -647,13 +631,8 @@ public class VideoRenderer implements RenderInfo { } private boolean displaySizeChanged() { - //#if MC>=11400 - int realWidth = getWindow(mc).getWidth(); - int realHeight = getWindow(mc).getHeight(); - //#else - //$$ int realWidth = Display.getWidth(); - //$$ int realHeight = Display.getHeight(); - //#endif + int realWidth = mc.getWindow().getWidth(); + int realHeight = mc.getWindow().getHeight(); if (realWidth == 0 || realHeight == 0) { // These can be zero on Windows if minimized. // Creating zero-sized framebuffers however will throw an error, so we never want to switch to zero values. @@ -663,13 +642,8 @@ public class VideoRenderer implements RenderInfo { } private void updateDisplaySize() { - //#if MC>=11400 - displayWidth = getWindow(mc).getWidth(); - displayHeight = getWindow(mc).getHeight(); - //#else - //$$ displayWidth = Display.getWidth(); - //$$ displayHeight = Display.getHeight(); - //#endif + displayWidth = mc.getWindow().getWidth(); + displayHeight = mc.getWindow().getHeight(); } public int getFramesDone() { @@ -728,15 +702,29 @@ public class VideoRenderer implements RenderInfo { } } - public static String[] checkCompat() { + public static String[] checkCompat(Stream settings) { + return settings.map(VideoRenderer::checkCompat).filter(Objects::nonNull).findFirst().orElse(null); + } + + public static String[] checkCompat(RenderSettings settings) { //#if FABRIC>=1 - if (net.fabricmc.loader.api.FabricLoader.getInstance().isModLoaded("sodium")) { + if (net.fabricmc.loader.api.FabricLoader.getInstance().isModLoaded("sodium") && !FlawlessFrames.hasSodium()) { return new String[] { - "Rendering is not currently supported while Sodium is installed.", - "See https://github.com/ReplayMod/ReplayMod/issues/150", - "For now, you need to uninstall Sodium before rendering!" + "Rendering is not supported with your Sodium version.", + "It is missing support for the FREX Flawless Frames API.", + "Either update to the latest version or uninstall Sodium before rendering!", }; } + //#if MC>=11700 + //$$ if (settings.getRenderMethod() == RenderSettings.RenderMethod.ODS + //$$ && !net.fabricmc.loader.api.FabricLoader.getInstance().isModLoaded("iris")) { + //$$ return new String[] { + //$$ "ODS export requires Iris to be installed for Minecraft 1.17 and above.", + //$$ "Note that it is nevertheless incompatible with other shaders and will simply replace them.", + //$$ "Get it from: https://irisshaders.net/", + //$$ }; + //$$ } + //#endif //#endif return null; } diff --git a/src/main/java/com/replaymod/render/utils/FlawlessFrames.java b/src/main/java/com/replaymod/render/utils/FlawlessFrames.java new file mode 100644 index 00000000..e419adef --- /dev/null +++ b/src/main/java/com/replaymod/render/utils/FlawlessFrames.java @@ -0,0 +1,40 @@ +package com.replaymod.render.utils; + +import java.util.List; +import java.util.concurrent.CopyOnWriteArrayList; +import java.util.function.Consumer; +import java.util.function.Function; + +import static com.replaymod.core.ReplayMod.MOD_ID; + +/** + * Uses the "Flawless Frames" FREX feature which allows us to instruct third-party mods to sacrifice performance (even + * beyond the point where it can no longer achieve interactive frame rates) in exchange for a noticeable boost to + * quality. + * In particular also to force-load all chunks with Canvas/Sodium/Bobby. + * + * See https://github.com/grondag/frex/pull/9 + */ +public class FlawlessFrames { + private static final List> CONSUMERS = new CopyOnWriteArrayList<>(); + private static boolean hasSodium; + + private FlawlessFrames() {} + + public static void registerConsumer(Function> provider) { + Consumer consumer = provider.apply(MOD_ID); + CONSUMERS.add(consumer); + + if (provider.getClass().getName().contains(".sodium.") || consumer.getClass().getName().contains(".sodium.")) { + hasSodium = true; + } + } + + public static void setEnabled(boolean enabled) { + CONSUMERS.forEach(it -> it.accept(enabled)); + } + + public static boolean hasSodium() { + return hasSodium; + } +} diff --git a/src/main/java/com/replaymod/replay/FullReplaySender.java b/src/main/java/com/replaymod/replay/FullReplaySender.java index 20ab761d..c4cc978e 100644 --- a/src/main/java/com/replaymod/replay/FullReplaySender.java +++ b/src/main/java/com/replaymod/replay/FullReplaySender.java @@ -12,6 +12,7 @@ import com.replaymod.replay.camera.CameraEntity; import com.replaymod.replaystudio.io.ReplayInputStream; import com.replaymod.replaystudio.replay.ReplayFile; import de.johni0702.minecraft.gui.utils.EventRegistrations; +import de.johni0702.minecraft.gui.versions.callbacks.PreTickCallback; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelDuplexHandler; @@ -57,13 +58,6 @@ import net.minecraft.util.math.MathHelper; import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOUtils; -//#if FABRIC>=1 -import de.johni0702.minecraft.gui.versions.callbacks.PreTickCallback; -//#else -//$$ import net.minecraftforge.eventbus.api.SubscribeEvent; -//$$ import net.minecraftforge.event.TickEvent; -//#endif - //#if MC>=11600 //#else //$$ import net.minecraft.network.packet.s2c.play.EntitySpawnGlobalS2CPacket; @@ -364,34 +358,24 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend } } - //#if MC>=10800 - private - //#else - //$$ public // All event handlers need to be public in 1.7.10 - //#endif - class EventHandler extends EventRegistrations { - //#if FABRIC>=1 + private class EventHandler extends EventRegistrations { { on(PreTickCallback.EVENT, this::onWorldTick); } private void onWorldTick() { - //#else - //$$ @SubscribeEvent - //$$ public void onWorldTick(TickEvent.ClientTickEvent event) { - //$$ // Unfortunately the WorldTickEvent doesn't seem to be emitted on the CLIENT side - //$$ if (event.phase != TickEvent.Phase.START) return; - //#endif - // Spawning a player into an empty chunk (which we might do with the recording player) // prevents it from being moved by teleport packets (it essentially gets stuck) because // Entity#addedToChunk is not set and it is therefore not updated every tick. // To counteract this, we need to manually update it's position if it hasn't been added // to any chunk yet. + // The `updateNeeded` flag appears to have been removed in 1.17, so this should no longer be an issue. + //#if MC<11700 if (mc.world != null) { - for (PlayerEntity playerEntity : playerEntities(mc.world)) { + for (PlayerEntity playerEntity : mc.world.getPlayers()) { if (!playerEntity.updateNeeded && playerEntity instanceof OtherClientPlayerEntity) { playerEntity.tickMovement(); } } } + //#endif } } @@ -432,11 +416,13 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend || p instanceof ExperienceOrbSpawnS2CPacket || p instanceof EntitiesDestroyS2CPacket) { ClientWorld world = mc.world; - //#if MC>=11400 + //#if MC>=11700 + //$$ // From the looks of it, this has now been resolved (thanks to EntityChangeListener) + //#elseif MC>=11400 // Note: Not sure if it's still required but there's this really handy method anyway world.finishRemovingEntities(); //#else - //$$ Iterator iter = loadedEntityList(world).iterator(); + //$$ Iterator iter = world.loadedEntityList.iterator(); //$$ while (iter.hasNext()) { //$$ Entity entity = iter.next(); //$$ if (entity.isDead) { @@ -474,10 +460,10 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend } } }; - if (MCVer.isOnMainThread()) { + if (mc.isOnThread()) { doLightUpdates.run(); } else { - MCVer.scheduleOnMainThread(doLightUpdates); + mc.send(doLightUpdates); } } //#endif @@ -497,12 +483,16 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend int i = pb.readVarInt(); NetworkState state = loginPhase ? NetworkState.LOGIN : NetworkState.PLAY; + //#if MC>=11700 + //$$ Packet p = state.getPacketHandler(NetworkSide.CLIENTBOUND, i, pb); + //#else //#if MC>=10800 Packet p = state.getPacketHandler(NetworkSide.CLIENTBOUND, i); //#else //$$ Packet p = Packet.generatePacket(state.func_150755_b(), i); //#endif p.read(pb); + //#endif return p; } @@ -761,8 +751,8 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend //#endif if(cent != null) { - if(!allowMovement && !((Math.abs(Entity_getX(cent) - ppl.getX()) > TP_DISTANCE_LIMIT) || - (Math.abs(Entity_getZ(cent) - ppl.getZ()) > TP_DISTANCE_LIMIT))) { + if(!allowMovement && !((Math.abs(cent.getX() - ppl.getX()) > TP_DISTANCE_LIMIT) || + (Math.abs(cent.getZ() - ppl.getZ()) > TP_DISTANCE_LIMIT))) { return null; } else { allowMovement = false; @@ -773,7 +763,7 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend @Override @SuppressWarnings("unchecked") public void run() { - if (mc.world == null || !isOnMainThread()) { + if (mc.world == null || !mc.isOnThread()) { ReplayMod.instance.runLater(this); return; } @@ -1190,9 +1180,17 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend //#endif List entitiesInChunk = new ArrayList<>(); // Gather all entities in that chunk - for (Collection entityList : getEntityLists(chunk)) { + //#if MC>=11700 + //$$ for (Entity entity : mc.world.getEntities()) { + //$$ if (entity.getChunkPos().equals(chunk.getPos())) { + //$$ entitiesInChunk.add(entity); + //$$ } + //$$ } + //#else + for (Collection entityList : chunk.getEntitySectionArray()) { entitiesInChunk.addAll(entityList); } + //#endif for (Entity entity : entitiesInChunk) { // Skip interpolation of position updates coming from server // (See: newX in EntityLivingBase or otherPlayerMPX in EntityOtherPlayerMP) @@ -1207,10 +1205,12 @@ public class FullReplaySender extends ChannelDuplexHandler implements ReplaySend } // Check whether the entity has left the chunk - //#if MC>=11404 - int chunkX = MathHelper.floor(Entity_getX(entity) / 16); - int chunkY = MathHelper.floor(Entity_getY(entity) / 16); - int chunkZ = MathHelper.floor(Entity_getZ(entity) / 16); + //#if MC>=11700 + //$$ // This is now handled automatically in Entity.setPos (called from tick()) + //#elseif MC>=11404 + int chunkX = MathHelper.floor(entity.getX() / 16); + int chunkY = MathHelper.floor(entity.getY() / 16); + int chunkZ = MathHelper.floor(entity.getZ() / 16); if (entity.chunkX != chunkX || entity.chunkY != chunkY || entity.chunkZ != chunkZ) { if (entity.updateNeeded) { // Entity has left the chunk diff --git a/src/main/java/com/replaymod/replay/InputReplayTimer.java b/src/main/java/com/replaymod/replay/InputReplayTimer.java index 3f5c8ec5..394fdd50 100644 --- a/src/main/java/com/replaymod/replay/InputReplayTimer.java +++ b/src/main/java/com/replaymod/replay/InputReplayTimer.java @@ -1,5 +1,6 @@ package com.replaymod.replay; +import com.replaymod.core.ReplayMod; import com.replaymod.core.utils.WrappedTimer; import com.replaymod.core.versions.MCVer; import com.replaymod.replay.camera.CameraController; @@ -7,10 +8,6 @@ import com.replaymod.replay.camera.CameraEntity; import net.minecraft.client.MinecraftClient; import net.minecraft.client.render.RenderTickCounter; -//#if MC>=11400 -import com.replaymod.core.ReplayMod; -//#endif - //#if MC>=11400 import org.lwjgl.glfw.GLFW; //#else @@ -22,10 +19,7 @@ import org.lwjgl.glfw.GLFW; //$$ import java.io.IOException; //#else //$$ import com.replaymod.replay.gui.screen.GuiOpeningReplay; -//$$ import cpw.mods.fml.common.eventhandler.Event; //$$ import net.minecraft.client.renderer.entity.RenderManager; -//$$ -//$$ import static com.replaymod.core.versions.MCVer.FML_BUS; //#endif //#endif @@ -65,10 +59,9 @@ public class InputReplayTimer extends WrappedTimer { //#endif ); - // 1.7.10: We have to run the scheduled executables (ours only) because MC would only run them every tick + ReplayMod.instance.runTasks(); + //#if MC<=10710 - //$$ FML_BUS.post(new RunScheduledTasks()); - //$$ //$$ // Code below only updates the current screen when a world and player is loaded. This may not be the case for //$$ // the GuiOpeningReplay screen resulting in a livelock. //$$ // To counteract that, we always update that screen (doesn't matter if we do it twice). @@ -77,10 +70,6 @@ public class InputReplayTimer extends WrappedTimer { //$$ } //#endif - //#if MC>=11400 - ReplayMod.instance.executor.runTasks(); - //#endif - // If we are in a replay, we have to manually process key and mouse events as the // tick speed may vary or there may not be any ticks at all (when the replay is paused) if (mod.getReplayHandler() != null && mc.world != null && mc.player != null) { @@ -147,8 +136,4 @@ public class InputReplayTimer extends WrappedTimer { } } } - - //#if MC<=10710 - //$$ public static class RunScheduledTasks extends Event {} - //#endif } diff --git a/src/main/java/com/replaymod/replay/NoGuiScreenshot.java b/src/main/java/com/replaymod/replay/NoGuiScreenshot.java index c438144b..a3cd6d56 100644 --- a/src/main/java/com/replaymod/replay/NoGuiScreenshot.java +++ b/src/main/java/com/replaymod/replay/NoGuiScreenshot.java @@ -3,20 +3,18 @@ package com.replaymod.replay; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; import com.replaymod.core.ReplayMod; -import com.replaymod.core.versions.MCVer; import de.johni0702.minecraft.gui.versions.Image; import net.minecraft.client.MinecraftClient; import com.mojang.blaze3d.platform.GlStateManager; import net.minecraft.client.util.ScreenshotUtils; +import static com.replaymod.core.versions.MCVer.popMatrix; +import static com.replaymod.core.versions.MCVer.pushMatrix; + //#if MC>=11500 import net.minecraft.client.util.math.MatrixStack; //#endif -//#if MC>=11400 -import static com.replaymod.core.versions.MCVer.getWindow; -//#endif - //#if MC<11400 //$$ import com.google.common.io.Files; //$$ import org.apache.commons.io.FileUtils; @@ -55,18 +53,15 @@ public class NoGuiScreenshot { return; } - //#if MC>=11400 - int frameWidth = getWindow(mc).getFramebufferWidth(), frameHeight = getWindow(mc).getFramebufferHeight(); - //#else - //$$ int frameWidth = mc.displayWidth, frameHeight = mc.displayHeight; - //#endif + int frameWidth = mc.getWindow().getFramebufferWidth(); + int frameHeight = mc.getWindow().getFramebufferHeight(); final boolean guiHidden = mc.options.hudHidden; try { mc.options.hudHidden = true; // Render frame without GUI - GlStateManager.pushMatrix(); + pushMatrix(); GlStateManager.clear( 16640 //#if MC>=11400 @@ -76,25 +71,26 @@ public class NoGuiScreenshot { mc.getFramebuffer().beginWrite(true); GlStateManager.enableTexture(); + float tickDelta = mc.getTickDelta(); //#if MC>=11500 - mc.gameRenderer.renderWorld(MCVer.getRenderPartialTicks(), System.nanoTime(), new MatrixStack()); + mc.gameRenderer.renderWorld(tickDelta, System.nanoTime(), new MatrixStack()); //#else //#if MC>=11400 - //$$ mc.gameRenderer.renderWorld(MCVer.getRenderPartialTicks(), System.nanoTime()); + //$$ mc.gameRenderer.renderWorld(tickDelta, System.nanoTime()); //#else //#if MC>=10809 - //$$ mc.entityRenderer.updateCameraAndRender(MCVer.getRenderPartialTicks(), System.nanoTime()); + //$$ mc.entityRenderer.updateCameraAndRender(tickDelta, System.nanoTime()); //#else - //$$ mc.entityRenderer.updateCameraAndRender(MCVer.getRenderPartialTicks()); + //$$ mc.entityRenderer.updateCameraAndRender(tickDelta); //#endif //#endif //#endif mc.getFramebuffer().endWrite(); - GlStateManager.popMatrix(); - GlStateManager.pushMatrix(); + popMatrix(); + pushMatrix(); mc.getFramebuffer().draw(frameWidth, frameHeight); - GlStateManager.popMatrix(); + popMatrix(); } catch (Throwable t) { future.setException(t); return; @@ -107,7 +103,12 @@ public class NoGuiScreenshot { // Read it, create the screenshot and finish the future try { //#if MC>=11400 - Image image = new Image(ScreenshotUtils.takeScreenshot(frameWidth, frameHeight, mc.getFramebuffer())); + Image image = new Image(ScreenshotUtils.takeScreenshot( + //#if MC<11701 + frameWidth, frameHeight, + //#endif + mc.getFramebuffer() + )); //#else //$$ // We're using Minecraft's ScreenShotHelper even though it writes the screenshot to //$$ // disk for better maintainability diff --git a/src/main/java/com/replaymod/replay/QuickReplaySender.java b/src/main/java/com/replaymod/replay/QuickReplaySender.java index 601efec8..5f5d7462 100644 --- a/src/main/java/com/replaymod/replay/QuickReplaySender.java +++ b/src/main/java/com/replaymod/replay/QuickReplaySender.java @@ -1,8 +1,6 @@ //#if MC>=10904 package com.replaymod.replay; -import com.github.steveice10.packetlib.io.NetInput; -import com.github.steveice10.packetlib.tcp.io.ByteBufNetInput; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; @@ -10,61 +8,28 @@ import com.google.common.util.concurrent.SettableFuture; import com.replaymod.core.mixin.MinecraftAccessor; import com.replaymod.core.mixin.TimerAccessor; import com.replaymod.replaystudio.replay.ReplayFile; -import com.replaymod.replaystudio.util.RandomAccessReplay; +import com.replaymod.replaystudio.rar.RandomAccessReplay; import de.johni0702.minecraft.gui.utils.EventRegistrations; +import de.johni0702.minecraft.gui.versions.callbacks.PreTickCallback; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandlerAdapter; import io.netty.channel.ChannelHandlerContext; import net.minecraft.client.MinecraftClient; -import net.minecraft.network.packet.s2c.play.PlayerPositionLookS2CPacket; -import net.minecraft.network.packet.s2c.play.PlayerRespawnS2CPacket; import net.minecraft.network.NetworkState; import net.minecraft.network.NetworkSide; import net.minecraft.network.Packet; import net.minecraft.network.PacketByteBuf; -//#if FABRIC>=1 -import de.johni0702.minecraft.gui.versions.callbacks.PreTickCallback; -//#else -//$$ import net.minecraftforge.eventbus.api.SubscribeEvent; -//$$ import net.minecraftforge.event.TickEvent; -//$$ -//$$ import static com.replaymod.core.versions.MCVer.FML_BUS; -//#endif - import javax.annotation.Nullable; import java.io.IOException; -import java.util.Collections; import java.util.function.Consumer; -//#if MC>=11602 -import net.minecraft.util.registry.DynamicRegistryManager; -import net.minecraft.util.registry.Registry; -//#endif - -//#if MC>=11600 -import net.minecraft.world.World; -//#else -//$$ import net.minecraft.world.level.LevelGeneratorType; -//#endif -//#if MC>=11400 -import net.minecraft.world.dimension.DimensionType; -//#else -//$$ import net.minecraft.world.EnumDifficulty; -//#endif - //#if MC>=11200 import com.replaymod.core.utils.WrappedTimer; //#endif -//#if MC>=11002 -import net.minecraft.world.GameMode; -//#else -//$$ import net.minecraft.world.WorldSettings.GameType; -//#endif - import static com.replaymod.core.versions.MCVer.getMinecraft; import static com.replaymod.core.versions.MCVer.getPacketTypeRegistry; import static com.replaymod.replay.ReplayModReplay.LOGGER; @@ -80,7 +45,7 @@ public class QuickReplaySender extends ChannelHandlerAdapter implements ReplaySe private final MinecraftClient mc = getMinecraft(); private final ReplayModReplay mod; - private final RandomAccessReplay> replay; + private final RandomAccessReplay replay; private final EventHandler eventHandler = new EventHandler(); private ChannelHandlerContext ctx; @@ -96,43 +61,48 @@ public class QuickReplaySender extends ChannelHandlerAdapter implements ReplaySe private ListenableFuture initPromise; - private com.github.steveice10.netty.buffer.ByteBuf buf; - private NetInput bufInput; - public QuickReplaySender(ReplayModReplay mod, ReplayFile replayFile) { this.mod = mod; - this.replay = new RandomAccessReplay>(replayFile, getPacketTypeRegistry(false)) { + this.replay = new RandomAccessReplay(replayFile, getPacketTypeRegistry(false)) { private byte[] buf = new byte[0]; @Override - protected Packet decode(com.github.steveice10.netty.buffer.ByteBuf byteBuf) throws IOException { - int packetId = new ByteBufNetInput(byteBuf).readVarInt(); - //#if MC>=11500 - Packet mcPacket = NetworkState.PLAY.getPacketHandler(NetworkSide.CLIENTBOUND, packetId); + protected void dispatch(com.replaymod.replaystudio.protocol.Packet packet) { + com.github.steveice10.netty.buffer.ByteBuf byteBuf = packet.getBuf(); + int size = byteBuf.readableBytes(); + if (buf.length < size) { + buf = new byte[size]; + } + byteBuf.getBytes(byteBuf.readerIndex(), buf, 0, size); + ByteBuf wrappedBuf = Unpooled.wrappedBuffer(buf); + wrappedBuf.writerIndex(size); + PacketByteBuf packetByteBuf = new PacketByteBuf(wrappedBuf); + + Packet mcPacket; + //#if MC>=11700 + //$$ mcPacket = NetworkState.PLAY.getPacketHandler(NetworkSide.CLIENTBOUND, packet.getId(), packetByteBuf); + //#elseif MC>=11500 + mcPacket = NetworkState.PLAY.getPacketHandler(NetworkSide.CLIENTBOUND, packet.getId()); //#else - //$$ Packet mcPacket; //$$ try { - //$$ mcPacket = NetworkState.PLAY.getPacketHandler(NetworkSide.CLIENTBOUND, packetId); + //$$ mcPacket = NetworkState.PLAY.getPacketHandler(NetworkSide.CLIENTBOUND, packet.getId()); //$$ } catch (IllegalAccessException | InstantiationException e) { - //$$ throw new IOException(e); + //$$ e.printStackTrace(); + //$$ return; //$$ } //#endif if (mcPacket != null) { - int size = byteBuf.readableBytes(); - if (buf.length < size) { - buf = new byte[size]; + //#if MC<11700 + try { + mcPacket.read(packetByteBuf); + } catch (IOException e) { + e.printStackTrace(); + return; } - byteBuf.readBytes(buf, 0, size); - ByteBuf wrappedBuf = Unpooled.wrappedBuffer(buf); - wrappedBuf.writerIndex(size); - mcPacket.read(new PacketByteBuf(wrappedBuf)); - } - return mcPacket; - } + //#endif - @Override - protected void dispatch(Packet packet) { - ctx.fireChannelRead(packet); + ctx.fireChannelRead(mcPacket); + } } }; } @@ -198,37 +168,6 @@ public class QuickReplaySender extends ChannelHandlerAdapter implements ReplaySe public void restart() { replay.reset(); - ctx.fireChannelRead(new PlayerRespawnS2CPacket( - //#if MC>=11600 - //#if MC>=11602 - DimensionType.addRegistryDefaults(new DynamicRegistryManager.Impl()).get(Registry.DIMENSION_TYPE_KEY).get(DimensionType.OVERWORLD_REGISTRY_KEY), - //#else - //$$ DimensionType.OVERWORLD_REGISTRY_KEY, - //#endif - World.OVERWORLD, - 0, - GameMode.SPECTATOR, - GameMode.SPECTATOR, - false, - false, - false - //#else - //#if MC>=11400 - //$$ DimensionType.OVERWORLD, - //#else - //$$ 0, - //#endif - //#if MC>=11500 - //$$ 0, - //#endif - //#if MC<11400 - //$$ EnumDifficulty.NORMAL, - //#endif - //$$ LevelGeneratorType.DEFAULT, - //$$ GameMode.SPECTATOR - //#endif - )); - ctx.fireChannelRead(new PlayerPositionLookS2CPacket(0, 0, 0, 0, 0, Collections.emptySet(), 0)); } @Override @@ -285,14 +224,8 @@ public class QuickReplaySender extends ChannelHandlerAdapter implements ReplaySe } private class EventHandler extends EventRegistrations { - //#if FABRIC>=1 { on(PreTickCallback.EVENT, this::onTick); } private void onTick() { - //#else - //$$ @SubscribeEvent - //$$ public void onTick(TickEvent.ClientTickEvent event) { - //$$ if (event.phase != TickEvent.Phase.START) return; - //#endif if (!asyncMode || paused()) return; long now = System.currentTimeMillis(); diff --git a/src/main/java/com/replaymod/replay/ReplayHandler.java b/src/main/java/com/replaymod/replay/ReplayHandler.java index 6669145a..adac3806 100644 --- a/src/main/java/com/replaymod/replay/ReplayHandler.java +++ b/src/main/java/com/replaymod/replay/ReplayHandler.java @@ -4,6 +4,7 @@ import com.google.common.base.Preconditions; import com.google.common.util.concurrent.FutureCallback; import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; +import com.mojang.blaze3d.platform.GlStateManager; import com.replaymod.core.ReplayMod; import com.replaymod.core.mixin.MinecraftAccessor; import com.replaymod.core.mixin.TimerAccessor; @@ -31,6 +32,7 @@ import io.netty.channel.embedded.EmbeddedChannel; import net.minecraft.client.MinecraftClient; import net.minecraft.client.gui.screen.DownloadingTerrainScreen; import net.minecraft.client.network.ClientLoginNetworkHandler; +import net.minecraft.client.util.Window; import net.minecraft.util.crash.CrashReport; import net.minecraft.entity.Entity; import net.minecraft.entity.player.PlayerEntity; @@ -39,6 +41,11 @@ import net.minecraft.network.ClientConnection; import java.io.IOException; import java.util.*; +//#if MC>=11700 +//$$ import net.minecraft.client.render.DiffuseLighting; +//$$ import net.minecraft.util.math.Matrix4f; +//#endif + //#if MC>=11600 import net.minecraft.client.util.math.MatrixStack; //#endif @@ -50,12 +57,10 @@ import org.lwjgl.opengl.GL11; //#if MC>=11400 import com.replaymod.replay.mixin.EntityLivingBaseAccessor; -import net.minecraft.client.util.Window; import net.minecraft.entity.LivingEntity; //#else //$$ import com.replaymod.replay.mixin.EntityOtherPlayerMPAccessor; //$$ import net.minecraft.client.entity.EntityOtherPlayerMP; -//$$ import net.minecraft.client.gui.ScaledResolution; //$$ import org.lwjgl.opengl.Display; //#endif @@ -98,7 +103,6 @@ import javax.annotation.Nullable; import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.replay.ReplayModReplay.LOGGER; -import static com.mojang.blaze3d.platform.GlStateManager.*; import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT; import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT; @@ -148,7 +152,7 @@ public class ReplayHandler { private UUID spectating; public ReplayHandler(ReplayFile replayFile, boolean asyncMode) throws IOException { - Preconditions.checkState(isOnMainThread(), "Must be called from Minecraft thread."); + Preconditions.checkState(mc.isOnThread(), "Must be called from Minecraft thread."); this.replayFile = replayFile; replayDuration = replayFile.getMetaData().getDuration(); @@ -171,7 +175,7 @@ public class ReplayHandler { } void restartedReplay() { - Preconditions.checkState(isOnMainThread(), "Must be called from Minecraft thread."); + Preconditions.checkState(mc.isOnThread(), "Must be called from Minecraft thread."); channel.close(); @@ -197,7 +201,7 @@ public class ReplayHandler { } public void endReplay() throws IOException { - Preconditions.checkState(isOnMainThread(), "Must be called from Minecraft thread."); + Preconditions.checkState(mc.isOnThread(), "Must be called from Minecraft thread."); ReplayClosingCallback.EVENT.invoker().replayClosing(this); @@ -214,11 +218,13 @@ public class ReplayHandler { channel.close().awaitUninterruptibly(); if (mc.player instanceof CameraEntity) { + //#if MC<11700 //#if MC>=11400 mc.player.remove(); //#else //$$ mc.player.setDead(); //#endif + //#endif } if (mc.world != null) { @@ -246,7 +252,7 @@ public class ReplayHandler { } private void setup() { - Preconditions.checkState(isOnMainThread(), "Must be called from Minecraft thread."); + Preconditions.checkState(mc.isOnThread(), "Must be called from Minecraft thread."); //#if MC>=11100 mc.inGameHud.getChatHud().clear(false); @@ -309,8 +315,8 @@ public class ReplayHandler { channel.pipeline().addLast("packet_handler", networkManager); channel.pipeline().fireChannelActive(); - //#if MC>=11400 && MC<11400 - //$$ NetworkHooks.registerClientLoginChannel(networkManager); + //#if MC>=11400 + ((MinecraftAccessor) mc).setConnection(networkManager); //#endif } @@ -403,7 +409,7 @@ public class ReplayHandler { CameraEntity cam = getCameraEntity(); if (cam != null) { - targetCameraPosition = new Location(Entity_getX(cam), Entity_getY(cam), Entity_getZ(cam), cam.yaw, cam.pitch); + targetCameraPosition = new Location(cam.getX(), cam.getY(), cam.getZ(), cam.yaw, cam.pitch); } else { targetCameraPosition = null; } @@ -486,8 +492,8 @@ public class ReplayHandler { cameraEntity.setCameraController(new SpectatorCameraController(cameraEntity)); } - if (getRenderViewEntity(mc) != e) { - setRenderViewEntity(mc, e); + if (mc.getCameraEntity() != e) { + mc.setCameraEntity(e); cameraEntity.setCameraPosRot(e); } } @@ -505,7 +511,7 @@ public class ReplayHandler { * @return {@code true} if the camera is the view entity, {@code false} otherwise */ public boolean isCameraView() { - return mc.player instanceof CameraEntity && mc.player == getRenderViewEntity(mc); + return mc.player instanceof CameraEntity && mc.player == mc.getCameraEntity(); } /** @@ -539,11 +545,11 @@ public class ReplayHandler { } // Update all entity positions (especially prev/lastTick values) - for (Entity entity : loadedEntityList(mc.world)) { + for (Entity entity : mc.world.getEntities()) { skipTeleportInterpolation(entity); - entity.lastRenderX = entity.prevX = Entity_getX(entity); - entity.lastRenderY = entity.prevY = Entity_getY(entity); - entity.lastRenderZ = entity.prevZ = Entity_getZ(entity); + entity.lastRenderX = entity.prevX = entity.getX(); + entity.lastRenderY = entity.prevY = entity.getY(); + entity.lastRenderZ = entity.prevZ = entity.getZ(); entity.prevYaw = entity.yaw; entity.prevPitch = entity.pitch; } @@ -563,7 +569,7 @@ public class ReplayHandler { quickReplaySender.sendPacketsTill(targetTime); // Immediately apply player teleport interpolation - for (Entity entity : loadedEntityList(mc.world)) { + for (Entity entity : mc.world.getEntities()) { skipTeleportInterpolation(entity); } return; @@ -582,7 +588,7 @@ public class ReplayHandler { if (retainCameraPosition) { CameraEntity cam = getCameraEntity(); if (cam != null) { - targetCameraPosition = new Location(Entity_getX(cam), Entity_getY(cam), Entity_getZ(cam), + targetCameraPosition = new Location(cam.getX(), cam.getY(), cam.getZ(), cam.yaw, cam.pitch); } else { targetCameraPosition = null; @@ -605,41 +611,47 @@ public class ReplayHandler { // Perform the rendering using OpenGL pushMatrix(); - clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT + GlStateManager.clear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT //#if MC>=11400 , true //#endif ); - enableTexture(); + GlStateManager.enableTexture(); mc.getFramebuffer().beginWrite(true); + Window window = mc.getWindow(); //#if MC>=11500 - Window window = getWindow(mc); RenderSystem.clear(256, MinecraftClient.IS_SYSTEM_MAC); + //#if MC>=11700 + //$$ RenderSystem.setProjectionMatrix(Matrix4f.projectionMatrix( + //$$ 0, + //$$ (float) (window.getFramebufferWidth() / window.getScaleFactor()), + //$$ 0, + //$$ (float) (window.getFramebufferHeight() / window.getScaleFactor()), + //$$ 1000, + //$$ 3000 + //$$ )); + //$$ MatrixStack matrixStack = RenderSystem.getModelViewStack(); + //$$ matrixStack.loadIdentity(); + //$$ matrixStack.translate(0, 0, -2000); + //$$ RenderSystem.applyModelViewMatrix(); + //$$ DiffuseLighting.enableGuiDepthLighting(); + //#else RenderSystem.matrixMode(GL11.GL_PROJECTION); RenderSystem.loadIdentity(); RenderSystem.ortho(0, window.getFramebufferWidth() / window.getScaleFactor(), window.getFramebufferHeight() / window.getScaleFactor(), 0, 1000, 3000); RenderSystem.matrixMode(GL11.GL_MODELVIEW); RenderSystem.loadIdentity(); RenderSystem.translatef(0, 0, -2000); + //#endif //#else //#if MC>=11400 - //$$ getWindow(mc).method_4493(true); - //#else - //#if MC>=11400 - //$$ mc.mainWindow.setupOverlayRendering(); + //$$ window.method_4493(true); //#else //$$ mc.entityRenderer.setupOverlayRendering(); //#endif //#endif - //#endif - //#if MC>=11400 - Window - //#else - //$$ ScaledResolution - //#endif - resolution = newScaledResolution(mc); - guiScreen.toMinecraft().init(mc, resolution.getScaledWidth(), resolution.getScaledHeight()); + guiScreen.toMinecraft().init(mc, window.getScaledWidth(), window.getScaledHeight()); //#if MC>=11600 guiScreen.toMinecraft().render(new MatrixStack(), 0, 0, 0); //#else @@ -654,18 +666,14 @@ public class ReplayHandler { mc.getFramebuffer().endWrite(); popMatrix(); pushMatrix(); - //#if MC>=11400 - mc.getFramebuffer().draw(getWindow(mc).getFramebufferWidth(), getWindow(mc).getFramebufferHeight()); - //#else - //$$ mc.getFramebuffer().framebufferRender(mc.displayWidth, mc.displayHeight); - //#endif + mc.getFramebuffer().draw(mc.getWindow().getFramebufferWidth(), mc.getWindow().getFramebufferHeight()); popMatrix(); //#if MC>=11500 - getWindow(mc).swapBuffers(); + mc.getWindow().swapBuffers(); //#else //#if MC>=11400 - //$$ getWindow(mc).setFullscreen(true); + //$$ mc.window.setFullscreen(true); //#else //$$ Display.update(); //#endif @@ -691,11 +699,11 @@ public class ReplayHandler { //#else //$$ .processReceivedPackets(); //#endif - for (Entity entity : loadedEntityList(mc.world)) { + for (Entity entity : mc.world.getEntities()) { skipTeleportInterpolation(entity); - entity.lastRenderX = entity.prevX = Entity_getX(entity); - entity.lastRenderY = entity.prevY = Entity_getY(entity); - entity.lastRenderZ = entity.prevZ = Entity_getZ(entity); + entity.lastRenderX = entity.prevX = entity.getX(); + entity.lastRenderY = entity.prevY = entity.getY(); + entity.lastRenderZ = entity.prevZ = entity.getZ(); entity.prevYaw = entity.yaw; entity.prevPitch = entity.pitch; } diff --git a/src/main/java/com/replaymod/replay/ReplayModReplay.java b/src/main/java/com/replaymod/replay/ReplayModReplay.java index 0fd60a70..05113411 100644 --- a/src/main/java/com/replaymod/replay/ReplayModReplay.java +++ b/src/main/java/com/replaymod/replay/ReplayModReplay.java @@ -32,8 +32,6 @@ import java.io.File; import java.io.IOException; import java.util.Optional; -import static com.replaymod.core.versions.MCVer.*; - public class ReplayModReplay implements Module { { instance = this; } @@ -68,9 +66,9 @@ public class ReplayModReplay implements Module { if (camera != null) { Marker marker = new Marker(); marker.setTime(replayHandler.getReplaySender().currentTimeStamp()); - marker.setX(Entity_getX(camera)); - marker.setY(Entity_getY(camera)); - marker.setZ(Entity_getZ(camera)); + marker.setX(camera.getX()); + marker.setY(camera.getY()); + marker.setZ(camera.getZ()); marker.setYaw(camera.yaw); marker.setPitch(camera.pitch); marker.setRoll(camera.roll); @@ -188,18 +186,14 @@ public class ReplayModReplay implements Module { } } replayHandler = new ReplayHandler(replayFile, asyncMode); - //#if MC>=11400 KeyBinding.updateKeysByCode(); // see Mixin_ContextualKeyBindings - //#endif return replayHandler; } public void forcefullyStopReplay() { replayHandler = null; - //#if MC>=11400 KeyBinding.updateKeysByCode(); // see Mixin_ContextualKeyBindings - //#endif } public ReplayMod getCore() { diff --git a/src/main/java/com/replaymod/replay/Setting.java b/src/main/java/com/replaymod/replay/Setting.java index a16e42e7..b2303c61 100644 --- a/src/main/java/com/replaymod/replay/Setting.java +++ b/src/main/java/com/replaymod/replay/Setting.java @@ -1,6 +1,10 @@ package com.replaymod.replay; import com.replaymod.core.SettingsRegistry; +import com.replaymod.replay.handler.GuiHandler.MainMenuButtonPosition; + +import java.util.Arrays; +import java.util.stream.Collectors; public final class Setting extends SettingsRegistry.SettingKeys { public static final Setting SHOW_CHAT = make("showChat", "showchat", true); @@ -8,6 +12,12 @@ public final class Setting extends SettingsRegistry.SettingKeys { public static final SettingsRegistry.MultipleChoiceSettingKeys CAMERA = new SettingsRegistry.MultipleChoiceSettingKeys<>( "replay", "camera", "replaymod.gui.settings.camera", "replaymod.camera.classic"); + public static final SettingsRegistry.MultipleChoiceSettingKeys MAIN_MENU_BUTTON = + new SettingsRegistry.MultipleChoiceSettingKeys<>( + "replay", "mainMenuButton", null, MainMenuButtonPosition.DEFAULT.name()); + static { + MAIN_MENU_BUTTON.setChoices(Arrays.stream(MainMenuButtonPosition.values()).map(Enum::name).collect(Collectors.toList())); + } private static Setting make(String key, String displayName, T defaultValue) { return new Setting<>(key, displayName, defaultValue); diff --git a/src/main/java/com/replaymod/replay/camera/CameraEntity.java b/src/main/java/com/replaymod/replay/camera/CameraEntity.java index 591a37d2..973e4fbc 100644 --- a/src/main/java/com/replaymod/replay/camera/CameraEntity.java +++ b/src/main/java/com/replaymod/replay/camera/CameraEntity.java @@ -3,9 +3,15 @@ package com.replaymod.replay.camera; import com.replaymod.core.KeyBindingRegistry; import com.replaymod.core.ReplayMod; import com.replaymod.core.SettingsRegistry; +import com.replaymod.core.events.KeyBindingEventCallback; +import com.replaymod.core.events.PreRenderCallback; +import com.replaymod.core.events.PreRenderHandCallback; import com.replaymod.core.events.SettingsChangedCallback; import com.replaymod.replay.ReplayHandler; +import com.replaymod.replay.events.RenderHotbarCallback; +import com.replaymod.replay.events.RenderSpectatorCrosshairCallback; import de.johni0702.minecraft.gui.utils.EventRegistrations; +import de.johni0702.minecraft.gui.versions.callbacks.PreTickCallback; import com.replaymod.core.utils.Utils; import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.Setting; @@ -24,20 +30,12 @@ import net.minecraft.util.Identifier; import net.minecraft.util.math.Box; //#if FABRIC>=1 -import com.replaymod.core.events.KeyBindingEventCallback; -import com.replaymod.core.events.PreRenderCallback; -import com.replaymod.core.events.PreRenderHandCallback; -import com.replaymod.replay.events.RenderSpectatorCrosshairCallback; -import de.johni0702.minecraft.gui.versions.callbacks.PreTickCallback; //#else -//$$ import com.replaymod.core.versions.MCVer; //$$ import net.minecraftforge.client.event.EntityViewRenderEvent; //$$ import net.minecraftforge.client.event.RenderGameOverlayEvent; -//$$ import net.minecraftforge.client.event.RenderHandEvent; //$$ import net.minecraftforge.common.MinecraftForge; //$$ import net.minecraftforge.eventbus.api.EventPriority; //$$ import net.minecraftforge.eventbus.api.SubscribeEvent; -//$$ import net.minecraftforge.event.TickEvent; //#endif //#if MC>=11400 @@ -51,7 +49,6 @@ import net.minecraft.util.hit.HitResult; //$$ import net.minecraft.util.math.RayTraceResult; //$$ import net.minecraft.util.text.ITextComponent; //$$ import net.minecraft.world.World; -//$$ import net.minecraftforge.fml.common.gameevent.InputEvent; //$$ //#if MC>=11400 //$$ import net.minecraft.util.math.RayTraceFluidMode; @@ -187,7 +184,7 @@ public class CameraEntity * @param z Delta in Z direction */ public void moveCamera(double x, double y, double z) { - setCameraPosition(Entity_getX(this) + x, Entity_getY(this) + y, Entity_getZ(this) + z); + setCameraPosition(this.getX() + x, this.getY() + y, this.getZ() + z); } /** @@ -200,7 +197,7 @@ public class CameraEntity this.lastRenderX = this.prevX = x; this.lastRenderY = this.prevY = y; this.lastRenderZ = this.prevZ = z; - Entity_setPos(this, x, y, z); + this.setPos(x, y, z); updateBoundingBox(); } @@ -211,8 +208,10 @@ public class CameraEntity * @param roll Roll in degrees */ public void setCameraRotation(float yaw, float pitch, float roll) { - this.prevYaw = this.yaw = yaw; - this.prevPitch = this.pitch = pitch; + this.prevYaw = yaw; + this.prevPitch = pitch; + this.yaw = yaw; + this.pitch = pitch; this.roll = roll; } @@ -241,7 +240,7 @@ public class CameraEntity this.prevZ = to.prevZ; this.prevYaw = to.prevYaw; this.prevPitch = to.prevPitch; - Entity_setPos(this, Entity_getX(to), Entity_getY(to), Entity_getZ(to)); + this.setPos(to.getX(), to.getY(), to.getZ()); this.yaw = to.yaw; this.pitch = to.pitch; this.lastRenderX = to.lastRenderX; @@ -260,17 +259,18 @@ public class CameraEntity //#else //$$ this.boundingBox.setBB(AxisAlignedBB.getBoundingBox( //#endif - Entity_getX(this) - width / 2, Entity_getY(this), Entity_getZ(this) - width / 2, - Entity_getX(this) + width / 2, Entity_getY(this) + height, Entity_getZ(this) + width / 2)); + this.getX() - width / 2, this.getY(), this.getZ() - width / 2, + this.getX() + width / 2, this.getY() + height, this.getZ() + width / 2)); } @Override public void tick() { //#if MC>=10800 - Entity view = getRenderViewEntity(this.client); + Entity view = //#else - //$$ EntityLivingBase view = getRenderViewEntity(this.mc); + //$$ EntityLivingBase view = //#endif + this.client.getCameraEntity(); if (view != null) { // Make sure we're always spectating the right entity // This is important if the spectated player respawns as their @@ -286,9 +286,9 @@ public class CameraEntity } view = this.world.getPlayerByUuid(spectating); if (view != null) { - setRenderViewEntity(this.client, view); + this.client.setCameraEntity(view); } else { - setRenderViewEntity(this.client, this); + this.client.setCameraEntity(this); return; } } @@ -311,7 +311,7 @@ public class CameraEntity @Override public void setRotation(float yaw, float pitch) { - if (getRenderViewEntity(this.client) == this) { + if (this.client.getCameraEntity() == this) { // Only update camera rotation when the camera is the view super.setRotation(yaw, pitch); } @@ -359,7 +359,7 @@ public class CameraEntity } private boolean falseUnlessSpectating(Function property) { - Entity view = getRenderViewEntity(this.client); + Entity view = this.client.getCameraEntity(); if (view != null && view != this) { return property.apply(view); } @@ -409,7 +409,7 @@ public class CameraEntity //#if MC>=10800 @Override public float getSpeed() { - Entity view = getRenderViewEntity(this.client); + Entity view = this.client.getCameraEntity(); if (view != this && view instanceof AbstractClientPlayerEntity) { return ((AbstractClientPlayerEntity) view).getSpeed(); } @@ -424,7 +424,7 @@ public class CameraEntity @Override public boolean isInvisible() { - Entity view = getRenderViewEntity(this.client); + Entity view = this.client.getCameraEntity(); if (view != this) { return view.isInvisible(); } @@ -433,7 +433,7 @@ public class CameraEntity @Override public Identifier getSkinTexture() { - Entity view = getRenderViewEntity(this.client); + Entity view = this.client.getCameraEntity(); if (view != this && view instanceof PlayerEntity) { return Utils.getResourceLocationForPlayerUUID(view.getUuid()); } @@ -452,7 +452,7 @@ public class CameraEntity @Override public boolean isPartVisible(PlayerModelPart modelPart) { - Entity view = getRenderViewEntity(this.client); + Entity view = this.client.getCameraEntity(); if (view != this && view instanceof PlayerEntity) { return ((PlayerEntity) view).isPartVisible(modelPart); } @@ -462,7 +462,7 @@ public class CameraEntity @Override public float getHandSwingProgress(float renderPartialTicks) { - Entity view = getRenderViewEntity(this.client); + Entity view = this.client.getCameraEntity(); if (view != this && view instanceof PlayerEntity) { return ((PlayerEntity) view).getHandSwingProgress(renderPartialTicks); } @@ -577,8 +577,13 @@ public class CameraEntity //#endif @Override + //#if MC>=11700 + //$$ public void remove(RemovalReason reason) { + //$$ super.remove(reason); + //#else public void remove() { super.remove(); + //#endif if (eventHandler != null) { eventHandler.unregister(); eventHandler = null; @@ -671,60 +676,27 @@ public class CameraEntity private EventHandler() {} - //#if FABRIC>=1 { on(PreTickCallback.EVENT, this::onPreClientTick); } private void onPreClientTick() { - //#else - //$$ @SubscribeEvent - //$$ public void onPreClientTick(TickEvent.ClientTickEvent event) { - //$$ if (event.phase != TickEvent.Phase.START) return; - //#endif updateArmYawAndPitch(); } - //#if FABRIC>=1 { on(PreRenderCallback.EVENT, this::onRenderUpdate); } private void onRenderUpdate() { - //#else - //$$ @SubscribeEvent - //$$ public void onRenderUpdate(TickEvent.RenderTickEvent event) { - //$$ if (event.phase != TickEvent.Phase.START) return; - //#endif update(); } - //#if FABRIC>=1 { on(KeyBindingEventCallback.EVENT, CameraEntity.this::handleInputEvents); } - //#elseif MC<11400 - //$$ @SubscribeEvent - //$$ public void onKeyEvent(InputEvent.KeyInputEvent event) { - //$$ handleInputEvents(); - //$$ } - //$$ - //$$ @SubscribeEvent - //$$ public void onMouseInput(InputEvent.MouseInputEvent event) { - //$$ handleInputEvents(); - //$$ } - //#endif - //#if FABRIC>=1 { on(RenderSpectatorCrosshairCallback.EVENT, this::shouldRenderSpectatorCrosshair); } private Boolean shouldRenderSpectatorCrosshair() { return canSpectate(mc.targetedEntity); } - //#else - //$$ @SubscribeEvent - //$$ public void preCrosshairRender(RenderGameOverlayEvent.Pre event) { - //$$ // The crosshair should only render if targeted entity can actually be spectated - //$$ if (MCVer.getType(event) == RenderGameOverlayEvent.ElementType.CROSSHAIRS) { - //$$ event.setCanceled(!canSpectate(mc.pointedEntity)); - //$$ } - //$$ // Hotbar should never be rendered - //$$ if (MCVer.getType(event) == RenderGameOverlayEvent.ElementType.HOTBAR) { - //$$ event.setCanceled(true); - //$$ } - //$$ } - //#endif + + { on(RenderHotbarCallback.EVENT, this::shouldRenderHotbar); } + private Boolean shouldRenderHotbar() { + return false; + } { on(SettingsChangedCallback.EVENT, this::onSettingsChanged); } private void onSettingsChanged(SettingsRegistry registry, SettingsRegistry.SettingKey key) { @@ -737,35 +709,13 @@ public class CameraEntity } } - //#if FABRIC>=1 { on(PreRenderHandCallback.EVENT, this::onRenderHand); } private boolean onRenderHand() { // Unless we are spectating another player, don't render our hand - if (getRenderViewEntity(mc) == CameraEntity.this || !(getRenderViewEntity(mc) instanceof PlayerEntity)) { + Entity view = mc.getCameraEntity(); + if (view == CameraEntity.this || !(view instanceof PlayerEntity)) { return true; // cancel hand rendering } else { - onRenderHandMonitor(); - return false; - } - } - //#else - //$$ @SubscribeEvent - //$$ public void onRenderHand(RenderHandEvent event) { - //$$ // Unless we are spectating another player, don't render our hand - //$$ if (getRenderViewEntity(mc) == CameraEntity.this || !(getRenderViewEntity(mc) instanceof PlayerEntity)) { - //$$ event.setCanceled(true); - //$$ } - //$$ } - //#endif - - //#if FABRIC>=1 - private void onRenderHandMonitor() { - //#else - //$$ @SubscribeEvent(priority = EventPriority.LOWEST) - //$$ public void onRenderHandMonitor(RenderHandEvent event) { - //#endif - Entity view = getRenderViewEntity(mc); - if (view instanceof PlayerEntity) { PlayerEntity player = (PlayerEntity) view; // When the spectated player has changed, force equip their items to prevent the equip animation if (lastHandRendered != player) { @@ -790,6 +740,7 @@ public class CameraEntity mc.player.renderYaw = mc.player.lastRenderYaw = player.yaw; mc.player.renderPitch = mc.player.lastRenderPitch = player.pitch; } + return false; } } @@ -817,7 +768,7 @@ public class CameraEntity //#else //$$ @SubscribeEvent //$$ public void preRenderGameOverlay(RenderGameOverlayEvent.Pre event) { - //$$ switch (MCVer.getType(event)) { + //$$ switch (event.getType()) { //$$ case ALL: //$$ heldItemTooltipsWasTrue = mc.gameSettings.heldItemTooltips; //$$ mc.gameSettings.heldItemTooltips = false; @@ -853,7 +804,7 @@ public class CameraEntity //$$ //$$ @SubscribeEvent //$$ public void postRenderGameOverlay(RenderGameOverlayEvent.Post event) { - //$$ if (MCVer.getType(event) != RenderGameOverlayEvent.ElementType.ALL) return; + //$$ if (event.getType() != RenderGameOverlayEvent.ElementType.ALL) return; //$$ mc.gameSettings.heldItemTooltips = heldItemTooltipsWasTrue; //$$ } //#endif diff --git a/src/main/java/com/replaymod/replay/camera/ClassicCameraController.java b/src/main/java/com/replaymod/replay/camera/ClassicCameraController.java index 9e0c02f7..316b4e25 100644 --- a/src/main/java/com/replaymod/replay/camera/ClassicCameraController.java +++ b/src/main/java/com/replaymod/replay/camera/ClassicCameraController.java @@ -1,9 +1,11 @@ package com.replaymod.replay.camera; import de.johni0702.minecraft.gui.utils.lwjgl.vector.Vector3f; +import net.minecraft.client.MinecraftClient; import net.minecraft.client.options.KeyBinding; -import static com.replaymod.core.versions.MCVer.*; +import static net.minecraft.util.math.MathHelper.cos; +import static net.minecraft.util.math.MathHelper.sin; // TODO: Marius is responsible for this. Please, someone clean it up. public class ClassicCameraController implements CameraController { @@ -32,7 +34,7 @@ public class ClassicCameraController implements CameraController { public void update(float partialTicksPassed) { boolean forward = false, backward = false, left = false, right = false, up = false, down = false; speedup = false; - for(KeyBinding kb : getMinecraft().options.keysAll) { + for(KeyBinding kb : MinecraftClient.getInstance().options.keysAll) { if(!kb.isPressed()) continue; if(kb.getTranslationKey().equals("key.forward")) { forward = true; diff --git a/src/main/java/com/replaymod/replay/camera/SpectatorCameraController.java b/src/main/java/com/replaymod/replay/camera/SpectatorCameraController.java index faf15be6..30a15018 100644 --- a/src/main/java/com/replaymod/replay/camera/SpectatorCameraController.java +++ b/src/main/java/com/replaymod/replay/camera/SpectatorCameraController.java @@ -52,9 +52,9 @@ public class SpectatorCameraController implements CameraController { // Always make sure the camera is in the exact same spot as the spectated entity // This is necessary as some rendering code for the hand doesn't respect the view entity // and always uses mc.thePlayer - Entity view = getRenderViewEntity(mc); + Entity view = mc.getCameraEntity(); if (view != null && view != camera) { - camera.setCameraPosRot(getRenderViewEntity(mc)); + camera.setCameraPosRot(mc.getCameraEntity()); // If it's a player, also 'steal' its inventory so the rendering code knows what item to render if (view instanceof PlayerEntity) { PlayerEntity viewPlayer = (PlayerEntity) view; diff --git a/src/main/java/com/replaymod/replay/events/RenderHotbarCallback.java b/src/main/java/com/replaymod/replay/events/RenderHotbarCallback.java new file mode 100644 index 00000000..36e1667c --- /dev/null +++ b/src/main/java/com/replaymod/replay/events/RenderHotbarCallback.java @@ -0,0 +1,19 @@ +package com.replaymod.replay.events; + +import de.johni0702.minecraft.gui.utils.Event; + +public interface RenderHotbarCallback { + Event EVENT = Event.create((listeners) -> + () -> { + for (RenderHotbarCallback listener : listeners) { + Boolean state = listener.shouldRenderHotbar(); + if (state != null) { + return state; + } + } + return null; + } + ); + + Boolean shouldRenderHotbar(); +} diff --git a/src/main/java/com/replaymod/replay/events/RenderSpectatorCrosshairCallback.java b/src/main/java/com/replaymod/replay/events/RenderSpectatorCrosshairCallback.java index 47ce1418..bad49102 100644 --- a/src/main/java/com/replaymod/replay/events/RenderSpectatorCrosshairCallback.java +++ b/src/main/java/com/replaymod/replay/events/RenderSpectatorCrosshairCallback.java @@ -1,4 +1,3 @@ -//#if MC>=11400 package com.replaymod.replay.events; import de.johni0702.minecraft.gui.utils.Event; @@ -18,4 +17,3 @@ public interface RenderSpectatorCrosshairCallback { Boolean shouldRenderSpectatorCrosshair(); } -//#endif diff --git a/src/main/java/com/replaymod/replay/gui/overlay/GuiEditMarkerPopup.java b/src/main/java/com/replaymod/replay/gui/overlay/GuiEditMarkerPopup.java index 2d0d5be4..ab2a3d78 100644 --- a/src/main/java/com/replaymod/replay/gui/overlay/GuiEditMarkerPopup.java +++ b/src/main/java/com/replaymod/replay/gui/overlay/GuiEditMarkerPopup.java @@ -93,7 +93,7 @@ public class GuiEditMarkerPopup extends AbstractGuiPopup imp popup.setLayout(new VerticalLayout().setSpacing(5)) .addElements(new VerticalLayout.Data(0.5), title, inputs, buttons); - popup.forEach(IGuiLabel.class).setColor(Colors.BLACK); + popup.invokeAll(IGuiLabel.class, e -> e.setColor(Colors.BLACK)); nameField.setText(Strings.nullToEmpty(marker.getName())); timeField.setValue(marker.getTime()); diff --git a/src/main/java/com/replaymod/replay/gui/overlay/GuiReplayOverlay.java b/src/main/java/com/replaymod/replay/gui/overlay/GuiReplayOverlay.java index 7a53b071..14bef7b1 100644 --- a/src/main/java/com/replaymod/replay/gui/overlay/GuiReplayOverlay.java +++ b/src/main/java/com/replaymod/replay/gui/overlay/GuiReplayOverlay.java @@ -170,12 +170,7 @@ public class GuiReplayOverlay extends AbstractGuiOverlay { return this; } - //#if MC>=10800 - private - //#else - //$$ public // All event handlers need to be public in 1.7.10 - //#endif - class EventHandler extends EventRegistrations { + private class EventHandler extends EventRegistrations { { on(KeyBindingEventCallback.EVENT, this::onKeyBindingEvent); } private void onKeyBindingEvent() { GameOptions gameSettings = getMinecraft().options; diff --git a/src/main/java/com/replaymod/replay/gui/screen/GuiReplayViewer.java b/src/main/java/com/replaymod/replay/gui/screen/GuiReplayViewer.java index 5c227644..bd889eb0 100644 --- a/src/main/java/com/replaymod/replay/gui/screen/GuiReplayViewer.java +++ b/src/main/java/com/replaymod/replay/gui/screen/GuiReplayViewer.java @@ -1,13 +1,9 @@ package com.replaymod.replay.gui.screen; -import com.google.common.base.Supplier; -import com.google.common.util.concurrent.FutureCallback; -import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.SettableFuture; import com.replaymod.render.gui.GuiRenderQueue; import com.replaymod.render.rendering.VideoRenderer; import com.replaymod.render.utils.RenderJob; -import com.replaymod.replaystudio.us.myles.ViaVersion.api.Pair; import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.RenderInfo; import de.johni0702.minecraft.gui.versions.Image; @@ -47,6 +43,7 @@ import org.apache.commons.io.FileUtils; import org.apache.commons.io.IOCase; import org.apache.commons.io.filefilter.SuffixFileFilter; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.tuple.Pair; import java.io.File; import java.io.FileFilter; @@ -63,6 +60,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Supplier; import java.util.stream.Collectors; import static com.replaymod.replay.ReplayModReplay.LOGGER; @@ -105,7 +103,7 @@ public class GuiReplayViewer extends GuiScreen { } else { Iterator>> replays = selected.stream() .filter(it -> !it.renderQueue.isEmpty()) - .map(it -> new Pair<>(it.file, it.renderQueue)) + .map(it -> Pair.of(it.file, it.renderQueue)) .iterator(); GuiRenderQueue.processMultipleReplays(GuiReplayViewer.this, mod, replays, () -> { loading = false; @@ -151,67 +149,46 @@ public class GuiReplayViewer extends GuiScreen { popup.getYesButton().setEnabled(!nameField.getText().isEmpty() && !new File(file.getParentFile(), Utils.replayNameToFileName(nameField.getText())).exists()); }); - Futures.addCallback(popup.getFuture(), new FutureCallback() { - @Override - public void onSuccess(Boolean delete) { - if (delete) { - // Sanitize their input - String name = nameField.getText().trim(); - // This file is what they want - File targetFile = new File(file.getParentFile(), Utils.replayNameToFileName(name)); - try { - // Finally, try to move it - FileUtils.moveFile(file, targetFile); - } catch (IOException e) { - // We failed (might also be their OS) - e.printStackTrace(); - getMinecraft().openScreen(new NoticeScreen( - //#if MC>=11400 - GuiReplayViewer.this::display, - new TranslatableText("replaymod.gui.viewer.delete.failed1"), - new TranslatableText("replaymod.gui.viewer.delete.failed2") - //#else - //$$ I18n.format("replaymod.gui.viewer.delete.failed1"), - //$$ I18n.format("replaymod.gui.viewer.delete.failed2") - //#endif - )); - return; - } - list.load(); - } - } - - @Override - public void onFailure(Throwable t) { - t.printStackTrace(); + popup.onAccept(() -> { + // Sanitize their input + String newName = nameField.getText().trim(); + // This file is what they want + File targetFile = new File(file.getParentFile(), Utils.replayNameToFileName(newName)); + try { + // Finally, try to move it + FileUtils.moveFile(file, targetFile); + } catch (IOException e) { + // We failed (might also be their OS) + e.printStackTrace(); + getMinecraft().openScreen(new NoticeScreen( + //#if MC>=11400 + GuiReplayViewer.this::display, + new TranslatableText("replaymod.gui.viewer.delete.failed1"), + new TranslatableText("replaymod.gui.viewer.delete.failed2") + //#else + //$$ I18n.format("replaymod.gui.viewer.delete.failed1"), + //$$ I18n.format("replaymod.gui.viewer.delete.failed2") + //#endif + )); + return; } + list.load(); }); } }).setSize(73, 20).setI18nLabel("replaymod.gui.rename").setDisabled(); public final GuiButton deleteButton = new GuiButton().onClick(() -> { for (GuiReplayEntry entry : list.getSelected()) { String name = entry.name.getText(); - GuiYesNoPopup popup = GuiYesNoPopup.open(GuiReplayViewer.this, + GuiYesNoPopup.open(GuiReplayViewer.this, new GuiLabel().setI18nText("replaymod.gui.viewer.delete.linea").setColor(Colors.BLACK), new GuiLabel().setI18nText("replaymod.gui.viewer.delete.lineb", name + Formatting.RESET).setColor(Colors.BLACK) - ).setYesI18nLabel("replaymod.gui.delete").setNoI18nLabel("replaymod.gui.cancel"); - Futures.addCallback(popup.getFuture(), new FutureCallback() { - @Override - public void onSuccess(Boolean delete) { - if (delete) { - try { - FileUtils.forceDelete(entry.file); - } catch (IOException e) { - e.printStackTrace(); - } - list.load(); - } - } - - @Override - public void onFailure(Throwable t) { - t.printStackTrace(); + ).setYesI18nLabel("replaymod.gui.delete").setNoI18nLabel("replaymod.gui.cancel").onAccept(() -> { + try { + FileUtils.forceDelete(entry.file); + } catch (IOException e) { + e.printStackTrace(); } + list.load(); }); } }).setSize(73, 20).setI18nLabel("replaymod.gui.delete").setDisabled(); @@ -283,7 +260,7 @@ public class GuiReplayViewer extends GuiScreen { loadButton.setTooltip(new GuiTooltip().setText(tooltipLines)); loadButton.setEnabled(!jobs.isEmpty()); - String[] compatError = VideoRenderer.checkCompat(); + String[] compatError = VideoRenderer.checkCompat(jobs.stream().map(RenderJob::getSettings)); if (compatError != null) { loadButton.setDisabled().setTooltip(new GuiTooltip().setText(compatError)); } diff --git a/src/main/java/com/replaymod/replay/handler/GuiHandler.java b/src/main/java/com/replaymod/replay/handler/GuiHandler.java index 88eea893..2bc2f79d 100644 --- a/src/main/java/com/replaymod/replay/handler/GuiHandler.java +++ b/src/main/java/com/replaymod/replay/handler/GuiHandler.java @@ -1,6 +1,13 @@ package com.replaymod.replay.handler; +import com.replaymod.core.gui.GuiReplayButton; +import com.replaymod.replay.Setting; +import de.johni0702.minecraft.gui.container.VanillaGuiScreen; +import de.johni0702.minecraft.gui.element.GuiTooltip; +import de.johni0702.minecraft.gui.layout.CustomLayout; import de.johni0702.minecraft.gui.utils.EventRegistrations; +import de.johni0702.minecraft.gui.utils.lwjgl.Point; +import de.johni0702.minecraft.gui.versions.callbacks.InitScreenCallback; import com.replaymod.replay.ReplayModReplay; import com.replaymod.replay.gui.screen.GuiReplayViewer; import net.minecraft.client.gui.screen.GameMenuScreen; @@ -16,9 +23,7 @@ import net.minecraft.text.TranslatableText; //$$ import net.minecraft.client.resource.language.I18n; //#endif -//#if FABRIC>=1 -import de.johni0702.minecraft.gui.versions.callbacks.InitScreenCallback; -//#else +//#if FABRIC<1 //$$ import net.minecraftforge.client.event.GuiScreenEvent; //$$ import net.minecraftforge.eventbus.api.SubscribeEvent; //#endif @@ -29,9 +34,11 @@ import net.minecraft.client.gui.widget.ButtonWidget; import java.io.IOException; import java.util.ArrayList; -import java.util.List; +import java.util.Collection; +import java.util.Comparator; import java.util.Optional; import java.util.function.Consumer; +import java.util.stream.Stream; import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.replay.ReplayModReplay.LOGGER; @@ -46,15 +53,8 @@ public class GuiHandler extends EventRegistrations { this.mod = mod; } - //#if FABRIC>=1 { on(InitScreenCallback.EVENT, this::injectIntoIngameMenu); } - private void injectIntoIngameMenu(Screen guiScreen, List buttonList) { - //#else - //$$ @SubscribeEvent - //$$ public void injectIntoIngameMenu(GuiScreenEvent.InitGuiEvent.Post event) { - //$$ Screen guiScreen = getGui(event); - //$$ List buttonList = getButtonList(event); - //#endif + private void injectIntoIngameMenu(Screen guiScreen, Collection buttonList) { if (!(guiScreen instanceof GameMenuScreen)) { return; } @@ -121,8 +121,8 @@ public class GuiHandler extends EventRegistrations { BUTTON_EXIT_REPLAY, b.x, b.y, - width(b), - height(b), + b.getWidth(), + b.getHeight(), "replaymod.gui.exit", this::onButton )); @@ -141,9 +141,9 @@ public class GuiHandler extends EventRegistrations { //#if MC>=11400 } else if (id.equals(BUTTON_OPTIONS)) { //#if MC>=11400 - width(b, 204); + b.setWidth(204); //#else - //$$ width(b, 200); + //$$ b.width = 200 //#endif //#endif } @@ -155,7 +155,7 @@ public class GuiHandler extends EventRegistrations { } if (achievements != null && stats != null) { moveAllButtonsInRect(buttonList, - achievements.x, stats.x + width(stats), + achievements.x, stats.x + stats.getWidth(), achievements.y, Integer.MAX_VALUE, -24); } @@ -182,9 +182,9 @@ public class GuiHandler extends EventRegistrations { */ private void moveAllButtonsInRect( //#if MC>=11400 - List buttons, + Collection buttons, //#else - //$$ List buttons, + //$$ Collection buttons, //#endif int xStart, int xEnd, @@ -193,19 +193,13 @@ public class GuiHandler extends EventRegistrations { int moveBy ) { buttons.stream() - .filter(button -> button.x <= xEnd && button.x + width(button) >= xStart) - .filter(button -> button.y <= yEnd && button.y + height(button) >= yStart) + .filter(button -> button.x <= xEnd && button.x + button.getWidth() >= xStart) + .filter(button -> button.y <= yEnd && button.y + button.getHeight() >= yStart) .forEach(button -> button.y += moveBy); } - //#if FABRIC>=1 - { on(InitScreenCallback.EVENT, this::ensureReplayStopped); } - private void ensureReplayStopped(Screen guiScreen, List buttonList) { - //#else - //$$ @SubscribeEvent - //$$ public void ensureReplayStopped(GuiScreenEvent.InitGuiEvent event) { - //$$ Screen guiScreen = getGui(event); - //#endif + { on(InitScreenCallback.EVENT, (screen, buttons) -> ensureReplayStopped(screen)); } + private void ensureReplayStopped(Screen guiScreen) { if (!(guiScreen instanceof TitleScreen || guiScreen instanceof MultiplayerScreen)) { return; } @@ -225,19 +219,40 @@ public class GuiHandler extends EventRegistrations { } } - //#if FABRIC>=1 { on(InitScreenCallback.EVENT, this::injectIntoMainMenu); } - private void injectIntoMainMenu(Screen guiScreen, List buttonList) { - //#else - //$$ @SubscribeEvent - //$$ public void injectIntoMainMenu(GuiScreenEvent.InitGuiEvent event) { - //$$ Screen guiScreen = getGui(event); - //$$ List buttonList = getButtonList(event); - //#endif + private void injectIntoMainMenu(Screen guiScreen, Collection buttonList) { if (!(guiScreen instanceof TitleScreen)) { return; } + boolean isCustomMainMenuMod = guiScreen.getClass().getName().endsWith("custommainmenu.gui.GuiFakeMain"); + + MainMenuButtonPosition buttonPosition = MainMenuButtonPosition.valueOf(mod.getCore().getSettingsRegistry().get(Setting.MAIN_MENU_BUTTON)); + if (buttonPosition != MainMenuButtonPosition.BIG && !isCustomMainMenuMod) { + VanillaGuiScreen vanillaGui = VanillaGuiScreen.wrap(guiScreen); + + GuiReplayButton replayButton = new GuiReplayButton(); + replayButton + .onClick(() -> new GuiReplayViewer(mod).display()) + .setTooltip(new GuiTooltip().setI18nText("replaymod.gui.replayviewer")); + + vanillaGui.setLayout(new CustomLayout(vanillaGui.getLayout()) { + private Point pos; + + @Override + protected void layout(de.johni0702.minecraft.gui.container.GuiScreen container, int width, int height) { + if (pos == null) { + // Delaying computation so we can take into account buttons + // added after our callback. + pos = determineButtonPos(buttonPosition, guiScreen, buttonList); + } + size(replayButton, 20, 20); + pos(replayButton, pos.getX(), pos.getY()); + } + }).addElements(null, replayButton); + return; + } + int x = guiScreen.width / 2 - 100; // We want to position our button below the realms button int y = findButton(buttonList, "menu.online", 14) @@ -267,23 +282,87 @@ public class GuiHandler extends EventRegistrations { this::onButton ); //#if FABRIC<=0 - //$$ if (guiScreen.getClass().getName().endsWith("custommainmenu.gui.GuiFakeMain")) { + //$$ if (isCustomMainMenuMod) { //$$ // CustomMainMenu uses a different list in the event than in its Fake gui - //$$ addButton(event, button); + //$$ buttonList.add(button); //$$ return; //$$ } //#endif addButton(guiScreen, button); } + private Point determineButtonPos(MainMenuButtonPosition buttonPosition, Screen guiScreen, Collection buttonList) { + Point topRight = new Point(guiScreen.width - 20 - 5, 5); + + if (buttonPosition == MainMenuButtonPosition.TOP_LEFT) { + return new Point(5, 5); + } else if (buttonPosition == MainMenuButtonPosition.TOP_RIGHT) { + return topRight; + } else if (buttonPosition == MainMenuButtonPosition.DEFAULT) { + return Stream.of( + findButton(buttonList, "menu.singleplayer", 1), + findButton(buttonList, "menu.multiplayer", 2), + findButton(buttonList, "menu.online", 14), + findButton(buttonList, "modmenu.title", 6) + ) + // skip buttons which do not exist + .flatMap(it -> it.map(Stream::of).orElseGet(Stream::empty)) + // skip buttons which already have something next to them + .filter(it -> buttonList.stream().noneMatch(button -> + button.x <= it.x + it.getWidth() + 4 + 20 + && button.y <= it.y + it.getHeight() + && button.x + button.getWidth() >= it.x + it.getWidth() + 4 + && button.y + button.getHeight() >= it.y + )) + // then take the bottom-most and if there's two, the right-most + .max(Comparator.comparingInt(it -> it.y).thenComparingInt(it -> it.x)) + // and place ourselves next to it + .map(it -> new Point(it.x + it.getWidth() + 4, it.y)) + // if all fails, just go with TOP_RIGHT + .orElse(topRight); + } else { + return Optional.of(buttonList).flatMap(buttons -> { + switch (buttonPosition) { + case LEFT_OF_SINGLEPLAYER: + case RIGHT_OF_SINGLEPLAYER: + return findButton(buttons, "menu.singleplayer", 1); + case LEFT_OF_MULTIPLAYER: + case RIGHT_OF_MULTIPLAYER: + return findButton(buttons, "menu.multiplayer", 2); + case LEFT_OF_REALMS: + case RIGHT_OF_REALMS: + return findButton(buttons, "menu.online", 14); + case LEFT_OF_MODS: + case RIGHT_OF_MODS: + return findButton(buttons, "modmenu.title", 6); + } + throw new RuntimeException(); + }).map(button -> { + switch (buttonPosition) { + case LEFT_OF_SINGLEPLAYER: + case LEFT_OF_MULTIPLAYER: + case LEFT_OF_REALMS: + case LEFT_OF_MODS: + return new Point(button.x - 4 - 20, button.y); + case RIGHT_OF_MODS: + case RIGHT_OF_SINGLEPLAYER: + case RIGHT_OF_MULTIPLAYER: + case RIGHT_OF_REALMS: + return new Point(button.x + button.getWidth() + 4, button.y); + } + throw new RuntimeException(); + }).orElse(topRight); + } + } + //#if MC>=11400 private void onButton(InjectedButton button) { Screen guiScreen = button.guiScreen; //#else //$$ @SubscribeEvent //$$ public void onButton(GuiScreenEvent.ActionPerformedEvent.Pre event) { - //$$ GuiScreen guiScreen = getGui(event); - //$$ GuiButton button = getButton(event); + //$$ GuiScreen guiScreen = event.getGui(); + //$$ GuiButton button = event.getButton(); //#endif if(!button.active) return; @@ -355,4 +434,22 @@ public class GuiHandler extends EventRegistrations { //$$ } //#endif } + + public enum MainMenuButtonPosition { + // The old big button below Realms/Mods which pushes other buttons around. + BIG, + // Right of the bottom-most button in the main block of buttons (so not the quit button). + // That will generally be either RIGHT_OF_REALMS or RIGHT_OF_MODS depending on version and installed mods. + DEFAULT, + TOP_LEFT, + TOP_RIGHT, + LEFT_OF_SINGLEPLAYER, + RIGHT_OF_SINGLEPLAYER, + LEFT_OF_MULTIPLAYER, + RIGHT_OF_MULTIPLAYER, + LEFT_OF_REALMS, + RIGHT_OF_REALMS, + LEFT_OF_MODS, + RIGHT_OF_MODS, + } } diff --git a/src/main/java/com/replaymod/replay/mixin/MixinCamera.java b/src/main/java/com/replaymod/replay/mixin/MixinCamera.java index 75c5f79c..7c1166d6 100644 --- a/src/main/java/com/replaymod/replay/mixin/MixinCamera.java +++ b/src/main/java/com/replaymod/replay/mixin/MixinCamera.java @@ -1,31 +1,20 @@ -//#if MC>=11400 package com.replaymod.replay.mixin; import com.replaymod.replay.camera.CameraEntity; +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.util.math.MatrixStack; +import net.minecraft.client.util.math.Vector3f; import net.minecraft.entity.Entity; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -//#if MC>=11500 -import net.minecraft.client.MinecraftClient; -import net.minecraft.client.util.math.MatrixStack; -import net.minecraft.client.util.math.Vector3f; -import org.spongepowered.asm.mixin.Final; -import org.spongepowered.asm.mixin.Shadow; -//#else -//$$ import com.mojang.blaze3d.platform.GlStateManager; -//$$ import net.minecraft.world.BlockView; -//#endif - -//#if MC>=11500 -@Mixin(net.minecraft.client.render.GameRenderer.class) -//#else -//$$ @Mixin(net.minecraft.client.render.Camera.class) -//#endif +@Mixin(GameRenderer.class) public class MixinCamera { - //#if MC>=11500 @Shadow @Final private MinecraftClient client; @Inject( method = "renderWorld", @@ -40,20 +29,4 @@ public class MixinCamera { matrixStack.multiply(Vector3f.POSITIVE_Z.getDegreesQuaternion(((CameraEntity) entity).roll)); } } - //#else - //$$ @Inject( - //$$ method = "update", - //$$ at = @At( - //$$ value = "INVOKE", - //$$ target = "Lcom/mojang/blaze3d/platform/GlStateManager;rotatef(FFFF)V", - //$$ ordinal = 0 - //$$ ) - //$$ ) - //$$ private void applyRoll(BlockView blockView, Entity view, boolean boolean_1, boolean boolean_2, float float_1, CallbackInfo ci) { - //$$ if (view instanceof CameraEntity) { - //$$ GlStateManager.rotatef(((CameraEntity) view).roll, 0.0F, 0.0F, 1.0F); - //$$ } - //$$ } - //#endif } -//#endif diff --git a/src/main/java/com/replaymod/replay/mixin/MixinEntityRenderer.java b/src/main/java/com/replaymod/replay/mixin/MixinEntityRenderer.java index fbf6d1ae..743815c4 100644 --- a/src/main/java/com/replaymod/replay/mixin/MixinEntityRenderer.java +++ b/src/main/java/com/replaymod/replay/mixin/MixinEntityRenderer.java @@ -1,28 +1 @@ -//#if MC<10800 -//$$ package com.replaymod.replay.mixin; -//$$ -//$$ import com.replaymod.replay.camera.CameraEntity; -//$$ import net.minecraft.client.Minecraft; -//$$ import net.minecraft.client.renderer.EntityRenderer; -//$$ import net.minecraft.entity.EntityLivingBase; -//$$ 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; -//$$ -//$$ @Mixin(EntityRenderer.class) -//$$ public class MixinEntityRenderer { -//$$ @Shadow private float prevCamRoll; -//$$ @Shadow private float camRoll; -//$$ @Shadow private Minecraft mc; -//$$ -//$$ @Inject(method = "orientCamera", at = @At("HEAD")) -//$$ private void applyRoll(float partialTicks, CallbackInfo ci) { -//$$ EntityLivingBase view = mc.renderViewEntity; -//$$ if (view instanceof CameraEntity) { -//$$ prevCamRoll = camRoll = ((CameraEntity) view).roll; -//$$ } -//$$ } -//$$ } -//#endif +// 1.7.10 and below diff --git a/src/main/java/com/replaymod/replay/mixin/MixinFMLClientHandler.java b/src/main/java/com/replaymod/replay/mixin/MixinFMLClientHandler.java index d239e2e9..743815c4 100644 --- a/src/main/java/com/replaymod/replay/mixin/MixinFMLClientHandler.java +++ b/src/main/java/com/replaymod/replay/mixin/MixinFMLClientHandler.java @@ -1,29 +1 @@ -//#if MC<10800 -//$$ package com.replaymod.replay.mixin; -//$$ -//$$ import com.replaymod.replay.gui.screen.GuiOpeningReplay; -//$$ import cpw.mods.fml.client.FMLClientHandler; -//$$ import net.minecraft.client.Minecraft; -//$$ import net.minecraft.client.gui.GuiScreen; -//$$ import org.spongepowered.asm.mixin.Mixin; -//$$ import org.spongepowered.asm.mixin.injection.At; -//$$ import org.spongepowered.asm.mixin.injection.Inject; -//$$ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -//$$ -//$$ @Mixin(value = FMLClientHandler.class, remap = false) -//$$ public class MixinFMLClientHandler { -//$$ // Usually this method is called async and can just block and wait for MC to tick the network manager -//$$ // Since we sometimes call it sync though, we need to take care of ticking ourselves. -//$$ @Inject(method = "waitForPlayClient", at = @At("HEAD"), remap = false) -//$$ private void tickNetworkManager(CallbackInfo ci) { -//$$ Minecraft mc = Minecraft.getMinecraft(); -//$$ if (!mc.isCallingFromMinecraftThread()) { -//$$ return; -//$$ } -//$$ GuiScreen gui = mc.currentScreen; -//$$ if (gui instanceof GuiOpeningReplay) { -//$$ gui.handleInput(); -//$$ } -//$$ } -//$$ } -//#endif +// 1.7.10 and below diff --git a/src/main/java/com/replaymod/replay/mixin/MixinInGameHud.java b/src/main/java/com/replaymod/replay/mixin/MixinInGameHud.java index 82f720a8..88431748 100644 --- a/src/main/java/com/replaymod/replay/mixin/MixinInGameHud.java +++ b/src/main/java/com/replaymod/replay/mixin/MixinInGameHud.java @@ -1,11 +1,13 @@ //#if MC>=11400 package com.replaymod.replay.mixin; +import com.replaymod.replay.events.RenderHotbarCallback; import com.replaymod.replay.events.RenderSpectatorCrosshairCallback; import net.minecraft.client.gui.hud.InGameHud; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; @Mixin(InGameHud.class) @@ -17,5 +19,13 @@ public class MixinInGameHud { ci.setReturnValue(state); } } + + @Inject(method = "renderHotbar", at = @At("HEAD"), cancellable = true) + private void shouldRenderHotbar(CallbackInfo ci) { + Boolean state = RenderHotbarCallback.EVENT.invoker().shouldRenderHotbar(); + if (state == Boolean.FALSE) { + ci.cancel(); + } + } } //#endif diff --git a/src/main/java/com/replaymod/replay/mixin/MixinParticleManager.java b/src/main/java/com/replaymod/replay/mixin/MixinParticleManager.java index 4ce7a6e5..6e9b8649 100644 --- a/src/main/java/com/replaymod/replay/mixin/MixinParticleManager.java +++ b/src/main/java/com/replaymod/replay/mixin/MixinParticleManager.java @@ -3,6 +3,7 @@ package com.replaymod.replay.mixin; //#if MC>=10904 import net.minecraft.client.particle.Particle; import net.minecraft.client.particle.ParticleManager; +import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.injection.At; @@ -19,12 +20,8 @@ import java.util.Queue; @Mixin(ParticleManager.class) public abstract class MixinParticleManager { - @Shadow - //#if MC>=11400 + @Final @Shadow private Queue newParticles; - //#else - //$$ private Queue queueEntityFX; - //#endif /** * This method additionally clears the queue of particles to be added when the world is changed. @@ -42,11 +39,7 @@ public abstract class MixinParticleManager { //$$ World world, //#endif CallbackInfo ci) { - //#if MC>=11400 this.newParticles.clear(); - //#else - //$$ this.queueEntityFX.clear(); - //#endif } } //#endif diff --git a/src/main/java/com/replaymod/replay/mixin/MixinRenderArrow.java b/src/main/java/com/replaymod/replay/mixin/MixinRenderArrow.java index bc20c96e..7b067f21 100644 --- a/src/main/java/com/replaymod/replay/mixin/MixinRenderArrow.java +++ b/src/main/java/com/replaymod/replay/mixin/MixinRenderArrow.java @@ -4,10 +4,15 @@ package com.replaymod.replay.mixin; import com.replaymod.replay.ReplayModReplay; import net.minecraft.client.render.entity.EntityRenderer; import net.minecraft.client.render.entity.ArrowEntityRenderer; -import net.minecraft.client.render.entity.EntityRenderDispatcher; import net.minecraft.entity.Entity; import org.spongepowered.asm.mixin.Mixin; +//#if MC>=11700 +//$$ import net.minecraft.client.render.entity.EntityRendererFactory.Context; +//#else +import net.minecraft.client.render.entity.EntityRenderDispatcher; +//#endif + //#if MC>=11500 import net.minecraft.client.render.Frustum; //#else @@ -16,9 +21,15 @@ import net.minecraft.client.render.Frustum; @Mixin(ArrowEntityRenderer.class) public abstract class MixinRenderArrow extends EntityRenderer { + //#if MC>=11700 + //$$ protected MixinRenderArrow(Context context) { + //$$ super(context); + //$$ } + //#else protected MixinRenderArrow(EntityRenderDispatcher renderManager) { super(renderManager); } + //#endif @SuppressWarnings("unchecked") @Override diff --git a/src/main/java/com/replaymod/replay/mixin/MixinRenderManager.java b/src/main/java/com/replaymod/replay/mixin/MixinRenderManager.java index 5050349c..2350c64e 100644 --- a/src/main/java/com/replaymod/replay/mixin/MixinRenderManager.java +++ b/src/main/java/com/replaymod/replay/mixin/MixinRenderManager.java @@ -1,30 +1,2 @@ // Appears to no longer be needed in 1.14+ (maybe not even 1.13?) -//#if MC>=10800 && MC<11400 -//$$ package com.replaymod.replay.mixin; -//$$ -//$$ import net.minecraft.client.settings.GameSettings; -//$$ import net.minecraft.client.gui.FontRenderer; -//$$ import net.minecraft.client.renderer.entity.RenderManager; -//$$ import net.minecraft.entity.Entity; -//$$ import net.minecraft.entity.passive.EntityAnimal; -//$$ import net.minecraft.world.World; -//$$ 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; -//$$ -//$$ @Mixin(RenderManager.class) -//$$ public class MixinRenderManager { -//$$ @Shadow -//$$ private float playerViewY; -//$$ -//$$ @Inject(method = "cacheActiveRenderInfo", at = @At("RETURN")) -//$$ public void fixHeadRotationForAnimals(World world, FontRenderer font, Entity view, Entity target, GameSettings settings, float partialRenderTick, CallbackInfo ci) { -//$$ if (view instanceof EntityAnimal && !((EntityAnimal) view).isPlayerSleeping()) { -//$$ EntityAnimal e = (EntityAnimal) view; -//$$ this.playerViewY = e.prevRotationYawHead + (e.rotationYawHead - e.prevRotationYawHead) * partialRenderTick; -//$$ } -//$$ } -//$$ } -//#endif +// 1.8 - 1.12.2 diff --git a/src/main/java/com/replaymod/replay/mixin/MixinTileEntityEndPortalRenderer.java b/src/main/java/com/replaymod/replay/mixin/MixinTileEntityEndPortalRenderer.java index 9139c516..20d75a52 100644 --- a/src/main/java/com/replaymod/replay/mixin/MixinTileEntityEndPortalRenderer.java +++ b/src/main/java/com/replaymod/replay/mixin/MixinTileEntityEndPortalRenderer.java @@ -40,7 +40,7 @@ public class MixinTileEntityEndPortalRenderer { //#endif //#endif //#endif - private long replayModReplay_getEnchantmentTime() { + private long replayModReplay_getPortalTime() { ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler(); if (replayHandler != null) { return replayHandler.getReplaySender().currentTimeStamp(); diff --git a/src/main/java/com/replaymod/replay/mixin/MixinWorldClient.java b/src/main/java/com/replaymod/replay/mixin/MixinWorldClient.java index 64e6e252..c5d79e4d 100644 --- a/src/main/java/com/replaymod/replay/mixin/MixinWorldClient.java +++ b/src/main/java/com/replaymod/replay/mixin/MixinWorldClient.java @@ -1,34 +1,10 @@ package com.replaymod.replay.mixin; import net.minecraft.client.world.ClientWorld; -import net.minecraft.entity.Entity; import org.spongepowered.asm.mixin.Mixin; -import org.spongepowered.asm.mixin.injection.At; -import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @Mixin(ClientWorld.class) public abstract class MixinWorldClient { - // Looks like this has finally been fixed in 1.14 (or been moved somewhere else entirely, guess we'll find out) - //#if MC<11400 - //$$ /** - //$$ * Fixes a bug in vanilla Minecraft that leaves entities remaining in the entityList even after respawn. - //$$ * The entityList in WorldClient is a Set that assumes that the entity ID of its entities does not change. - //$$ * However in {@link WorldClient#addEntityToWorld(int, Entity)}, the entity passed in is first added to the - //$$ * set and subsequently its id is changed, leaving it stuck in he Set. That is the buggy method. - //$$ * This wouldn't be too much of a problem if the entity had the correct id to begin with, however the handler for - //$$ * the spawn player packet creates an EntityOtherPlayerMP which takes its id from a counter and then passes it to - //$$ * this method with the wrong id. - //$$ * This mixin fixes the id of the entity before it is added to the set instead of right after. - //$$ * The original id change right after is not changed, however it should not have any effect. - //$$ * @param entityId The id to be set for the entity - //$$ * @param entity The entity to be added - //$$ * @param ci Callback info - //$$ */ - //$$ @Inject(method = "addEntityToWorld", at=@At("HEAD")) - //$$ public void replayModReplay_fix_addEntityToWorld(int entityId, Entity entity, CallbackInfo ci) { - //$$ entity.setEntityId(entityId); - //$$ } - //#endif + // 1.12.2 and below } diff --git a/src/main/java/com/replaymod/replay/mixin/Mixin_FixNPCSkinCaching.java b/src/main/java/com/replaymod/replay/mixin/Mixin_FixNPCSkinCaching.java new file mode 100644 index 00000000..925c2b0e --- /dev/null +++ b/src/main/java/com/replaymod/replay/mixin/Mixin_FixNPCSkinCaching.java @@ -0,0 +1,31 @@ +package com.replaymod.replay.mixin; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.network.AbstractClientPlayerEntity; +import net.minecraft.client.network.PlayerListEntry; +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 javax.annotation.Nullable; + +@Mixin(AbstractClientPlayerEntity.class) +public abstract class Mixin_FixNPCSkinCaching { + @Shadow @Nullable protected abstract PlayerListEntry getPlayerListEntry(); + + @Inject(method = "", at = @At("RETURN")) + private void forceCachePlayerListEntry(CallbackInfo ci) { + // Server-side NPCs (without a client mod) rely on the player list entry being cached in this class to function + // because the remove the entry shortly after spawning the player. However this method is only called when the + // player is rendered, and if we are currently jumping around in a replay then it most likely will not be + // rendered while the entry is still there. The result is that most NPCs get Steve/Alex skins instead of the + // intended ones. To fix that, we make this caching-glitch which servers have come to rely on an actual feature + // by just fetching the cache in the constructor which arguably is what MC should have done to begin with, + // especially because the spawn packet handling code already requires the entry to be present). + if (MinecraftClient.getInstance().getNetworkHandler() != null) { // will be null if this is the client player + this.getPlayerListEntry(); + } + } +} diff --git a/src/main/java/com/replaymod/replay/mixin/Mixin_MoveRealmsButton.java b/src/main/java/com/replaymod/replay/mixin/Mixin_MoveRealmsButton.java index 12f19084..3066899f 100644 --- a/src/main/java/com/replaymod/replay/mixin/Mixin_MoveRealmsButton.java +++ b/src/main/java/com/replaymod/replay/mixin/Mixin_MoveRealmsButton.java @@ -1,6 +1,9 @@ //#if MC>=11600 package com.replaymod.replay.mixin; +import com.replaymod.core.ReplayMod; +import com.replaymod.replay.Setting; +import com.replaymod.replay.handler.GuiHandler.MainMenuButtonPosition; import net.minecraft.client.gui.screen.TitleScreen; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; @@ -14,7 +17,11 @@ public abstract class Mixin_MoveRealmsButton { index = 2 ) private int adjustRealmsButton(int height) { - return height - 24 * 4; + String setting = ReplayMod.instance.getSettingsRegistry().get(Setting.MAIN_MENU_BUTTON); + if (MainMenuButtonPosition.valueOf(setting) == MainMenuButtonPosition.BIG) { + height -= 24 * 4; + } + return height; } } //#endif diff --git a/src/main/java/com/replaymod/restrictions/ReplayModRestrictions.java b/src/main/java/com/replaymod/restrictions/ReplayModRestrictions.java deleted file mode 100644 index d05741ac..00000000 --- a/src/main/java/com/replaymod/restrictions/ReplayModRestrictions.java +++ /dev/null @@ -1,124 +0,0 @@ -// I don't think this sees sufficient use to justify updating. -// Especially because it's non-trivial with forge's network changes. -// PRs welcome. Ping me if you actually used this. - -//#if MC<11400 -//$$ package com.replaymod.restrictions; -//$$ -//$$ import com.replaymod.core.SettingsRegistry; -//$$ import com.replaymod.core.utils.Restrictions; -//$$ import io.netty.buffer.Unpooled; -//$$ import io.netty.channel.ChannelDuplexHandler; -//$$ import io.netty.channel.ChannelFutureListener; -//$$ import io.netty.channel.ChannelHandler; -//$$ import net.minecraft.network.PacketBuffer; -//$$ import net.minecraftforge.common.config.Configuration; -//$$ import org.apache.logging.log4j.Logger; -//$$ -//#if MC>=10800 -//$$ import net.minecraftforge.fml.common.Mod; -//$$ import net.minecraftforge.fml.common.event.FMLInitializationEvent; -//$$ import net.minecraftforge.fml.common.event.FMLPostInitializationEvent; -//$$ import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; -//$$ import net.minecraftforge.fml.common.eventhandler.EventBus; -//$$ import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; -//$$ import net.minecraftforge.fml.common.gameevent.PlayerEvent; -//$$ import net.minecraftforge.fml.common.network.FMLEmbeddedChannel; -//$$ import net.minecraftforge.fml.common.network.FMLOutboundHandler; -//$$ import net.minecraftforge.fml.common.network.NetworkRegistry; -//$$ import net.minecraftforge.fml.common.network.internal.FMLProxyPacket; -//$$ import net.minecraftforge.fml.relauncher.Side; -//#else -//$$ import cpw.mods.fml.common.FMLCommonHandler; -//$$ import cpw.mods.fml.common.Mod; -//$$ import cpw.mods.fml.common.event.FMLInitializationEvent; -//$$ import cpw.mods.fml.common.event.FMLPostInitializationEvent; -//$$ import cpw.mods.fml.common.event.FMLPreInitializationEvent; -//$$ import cpw.mods.fml.common.eventhandler.EventBus; -//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; -//$$ import cpw.mods.fml.common.gameevent.PlayerEvent; -//$$ import cpw.mods.fml.common.network.FMLEmbeddedChannel; -//$$ import cpw.mods.fml.common.network.FMLOutboundHandler; -//$$ import cpw.mods.fml.common.network.NetworkRegistry; -//$$ import cpw.mods.fml.common.network.internal.FMLProxyPacket; -//$$ import cpw.mods.fml.relauncher.Side; -//#endif -//$$ -//$$ import java.io.IOException; -//$$ import java.util.HashMap; -//$$ import java.util.Map; -//$$ -//$$ import static com.replaymod.core.versions.MCVer.*; -//$$ -//$$ @Mod(modid = ReplayModRestrictions.MOD_ID, -//$$ version = "@MOD_VERSION@", -//$$ acceptedMinecraftVersions = "@MC_VERSION@", -//$$ acceptableRemoteVersions = "*", - //#if MC>=10800 - //$$ serverSideOnly = true, - //#endif -//$$ useMetadata = true) -//$$ public class ReplayModRestrictions { -//$$ public static final String MOD_ID = "replaymod-restrictions"; -//$$ -//$$ public static Logger LOGGER; -//$$ -//$$ private final SettingsRegistry settingsRegistry = new SettingsRegistry(); -//$$ private FMLEmbeddedChannel channel; -//$$ -//$$ @Mod.EventHandler -//$$ public void preInit(FMLPreInitializationEvent event) { -//$$ LOGGER = event.getModLog(); -//$$ -//$$ Configuration config = new Configuration(event.getSuggestedConfigurationFile()); -//$$ config.load(); -//$$ settingsRegistry.setConfiguration(config); -//$$ -//$$ settingsRegistry.register(Setting.class); -//$$ } -//$$ -//$$ @Mod.EventHandler -//$$ public void init(FMLInitializationEvent event) { - //#if MC<=10710 - //$$ if (FMLCommonHandler.instance().getSide() == Side.CLIENT) return; - //#endif -//$$ EventBus bus = FML_BUS; -//$$ bus.register(this); -//$$ -//$$ channel = NetworkRegistry.INSTANCE.newChannel(Restrictions.PLUGIN_CHANNEL, new RestrictionsChannelHandler()).get(Side.SERVER); -//$$ } -//$$ -//$$ @Mod.EventHandler -//$$ public void postInit(FMLPostInitializationEvent event) throws IOException { -//$$ settingsRegistry.save(); // Save default values to disk -//$$ } -//$$ -//$$ @ChannelHandler.Sharable -//$$ private static class RestrictionsChannelHandler extends ChannelDuplexHandler {} -//$$ -//$$ @SubscribeEvent -//$$ public void onUserJoin(PlayerEvent.PlayerLoggedInEvent event) { -//$$ Map restrictions = new HashMap<>(); -//$$ if (settingsRegistry.get(Setting.NO_XRAY)) restrictions.put("no_xray", new byte[0]); -//$$ if (settingsRegistry.get(Setting.NO_NOCLIP)) restrictions.put("no_noclip", new byte[0]); -//$$ if (settingsRegistry.get(Setting.ONLY_FIRST_PERSON)) restrictions.put("only_first_person", new byte[0]); -//$$ if (settingsRegistry.get(Setting.ONLY_RECORDING_PLAYER)) restrictions.put("only_recording_player", new byte[0]); -//$$ if (settingsRegistry.get(Setting.HIDE_COORDINATES)) restrictions.put("hide_coordinates", new byte[0]); -//$$ -//$$ PacketBuffer buf = new PacketBuffer(Unpooled.buffer()); -//$$ for (Map.Entry e : restrictions.entrySet()) { -//$$ byte[] bytes = e.getKey().getBytes(); -//$$ buf.writeByte(bytes.length); -//$$ buf.writeBytes(bytes); -//$$ buf.writeByte(1); -//$$ buf.writeBytes(e.getValue()); -//$$ } -//$$ -//$$ FMLProxyPacket packet = new FMLProxyPacket(buf, Restrictions.PLUGIN_CHANNEL); -//$$ -//$$ channel.attr(FMLOutboundHandler.FML_MESSAGETARGET).set(FMLOutboundHandler.OutboundTarget.PLAYER); -//$$ channel.attr(FMLOutboundHandler.FML_MESSAGETARGETARGS).set(event.player); -//$$ channel.writeAndFlush(packet).addListener(ChannelFutureListener.FIRE_EXCEPTION_ON_FAILURE); -//$$ } -//$$ } -//#endif diff --git a/src/main/java/com/replaymod/restrictions/Setting.java b/src/main/java/com/replaymod/restrictions/Setting.java deleted file mode 100644 index 7a36e936..00000000 --- a/src/main/java/com/replaymod/restrictions/Setting.java +++ /dev/null @@ -1,19 +0,0 @@ -package com.replaymod.restrictions; - -import com.replaymod.core.SettingsRegistry; - -public final class Setting extends SettingsRegistry.SettingKeys { - public static final Setting NO_XRAY = make("no_xray", false); - public static final Setting NO_NOCLIP = make("no_noclip", false); - public static final Setting ONLY_FIRST_PERSON = make("only_first_person", false); - public static final Setting ONLY_RECORDING_PLAYER = make("only_recording_player", false); - public static final Setting HIDE_COORDINATES = make("hide_coordinates", false); - - private static Setting make(String key, T defaultValue) { - return new Setting<>("restrictions", key, defaultValue); - } - - public Setting(String category, String key, T defaultValue) { - super(category, key, null, defaultValue); - } -} diff --git a/src/main/java/com/replaymod/simplepathing/SPTimeline.java b/src/main/java/com/replaymod/simplepathing/SPTimeline.java index 6a035759..561122d4 100644 --- a/src/main/java/com/replaymod/simplepathing/SPTimeline.java +++ b/src/main/java/com/replaymod/simplepathing/SPTimeline.java @@ -49,7 +49,6 @@ import java.util.Map; import java.util.Optional; import java.util.Set; -import static com.replaymod.core.versions.MCVer.*; import static com.replaymod.replaystudio.pathing.change.RemoveKeyframe.create; import static com.replaymod.simplepathing.ReplayModSimplePathing.LOGGER; @@ -674,7 +673,7 @@ public class SPTimeline implements PathingRegistry { } catch (IOException e) { CrashReport crash = CrashReport.create(e, "Serializing interpolator"); CrashReportSection category = crash.addElement("Serializing interpolator"); - addDetail(category, "Interpolator", interpolator::toString); + category.add("Interpolator", interpolator::toString); throw new CrashException(crash); } @@ -689,7 +688,7 @@ public class SPTimeline implements PathingRegistry { } catch (IOException e) { CrashReport crash = CrashReport.create(e, "De-serializing interpolator"); CrashReportSection category = crash.addElement("De-serializing interpolator"); - addDetail(category, "Interpolator", json::toString); + category.add("Interpolator", json::toString); throw new CrashException(crash); } } diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java b/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java index 27fd2fd1..eb3561ec 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiEditKeyframe.java @@ -155,7 +155,7 @@ public abstract class GuiEditKeyframe> extends Abst link(timeMinField, timeSecField, timeMSecField); - popup.forEach(IGuiLabel.class).setColor(Colors.BLACK); + popup.invokeAll(IGuiLabel.class, e -> e.setColor(Colors.BLACK)); } @Override @@ -194,7 +194,7 @@ public abstract class GuiEditKeyframe> extends Abst link(timestampMinField, timestampSecField, timestampMSecField, timeMinField, timeSecField, timeMSecField); - popup.forEach(IGuiLabel.class).setColor(Colors.BLACK); + popup.invokeAll(IGuiLabel.class, e -> e.setColor(Colors.BLACK)); } @Override @@ -252,7 +252,7 @@ public abstract class GuiEditKeyframe> extends Abst link(xField, yField, zField, yawField, pitchField, rollField, timeMinField, timeSecField, timeMSecField); - popup.forEach(IGuiLabel.class).setColor(Colors.BLACK); + popup.invokeAll(IGuiLabel.class, e -> e.setColor(Colors.BLACK)); } @Override diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiKeyframeTimeline.java b/src/main/java/com/replaymod/simplepathing/gui/GuiKeyframeTimeline.java index ece93360..3576ffa0 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiKeyframeTimeline.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiKeyframeTimeline.java @@ -18,6 +18,10 @@ import com.replaymod.simplepathing.SPTimeline.SPPath; import de.johni0702.minecraft.gui.GuiRenderer; import de.johni0702.minecraft.gui.element.advanced.AbstractGuiTimeline; import de.johni0702.minecraft.gui.function.Draggable; +import de.johni0702.minecraft.gui.utils.lwjgl.vector.Vector2f; +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.render.VertexFormats; import org.apache.commons.lang3.tuple.Pair; import de.johni0702.minecraft.gui.utils.lwjgl.Point; import de.johni0702.minecraft.gui.utils.lwjgl.ReadableDimension; @@ -27,9 +31,15 @@ import org.lwjgl.opengl.GL11; import java.util.Comparator; import java.util.Optional; -import static com.replaymod.core.versions.MCVer.BufferBuilder_addPosCol; -import static com.replaymod.core.versions.MCVer.BufferBuilder_beginPosCol; -import static com.replaymod.core.versions.MCVer.Tessellator_getInstance; +import static com.replaymod.core.versions.MCVer.emitLine; +import static de.johni0702.minecraft.gui.versions.MCVer.popScissorState; +import static de.johni0702.minecraft.gui.versions.MCVer.pushScissorState; +import static de.johni0702.minecraft.gui.versions.MCVer.setScissorState; + +//#if MC>=11700 +//$$ import com.mojang.blaze3d.systems.RenderSystem; +//$$ import net.minecraft.client.render.GameRenderer; +//#endif public class GuiKeyframeTimeline extends AbstractGuiTimeline implements Draggable { protected static final int KEYFRAME_SIZE = 5; @@ -142,58 +152,38 @@ public class GuiKeyframeTimeline extends AbstractGuiTimeline> 24 & 0xff, - color >> 16 & 0xff, - color >> 8 & 0xff, - color & 0xff - ); + Vector2f p1 = new Vector2f(replayTimelineLeft + positionXReplayTimeline, replayTimelineTop + BORDER_TOP); // Draw vertically over the replay timeline, including its bottom border - BufferBuilder_addPosCol( - replayTimelineLeft + positionXReplayTimeline, - replayTimelineBottom, - 0, - color >> 24 & 0xff, - color >> 16 & 0xff, - color >> 8 & 0xff, - color & 0xff - ); + Vector2f p2 = new Vector2f(replayTimelineLeft + positionXReplayTimeline, replayTimelineBottom); // Now for the important part: connecting to the keyframe timeline - BufferBuilder_addPosCol( - keyframeTimelineLeft + positionXKeyframeTimeline, - keyframeTimelineTop, - 0, - color >> 24 & 0xff, - color >> 16 & 0xff, - color >> 8 & 0xff, - color & 0xff - ); + Vector2f p3 = new Vector2f(keyframeTimelineLeft + positionXKeyframeTimeline, keyframeTimelineTop); // And finally another vertical bit (the timeline is already crammed enough, so only the border) - BufferBuilder_addPosCol( - keyframeTimelineLeft + positionXKeyframeTimeline, - keyframeTimelineTop + BORDER_TOP, - 0, - color >> 24 & 0xff, - color >> 16 & 0xff, - color >> 8 & 0xff, - color & 0xff - ); + Vector2f p4 = new Vector2f(keyframeTimelineLeft + positionXKeyframeTimeline, keyframeTimelineTop + BORDER_TOP); + emitLine(buffer, p1, p2, color); + emitLine(buffer, p2, p3, color); + emitLine(buffer, p3, p4, color); + + //#if MC>=11700 + //$$ RenderSystem.setShader(GameRenderer::getRenderTypeLinesShader); + //#else GL11.glEnable(GL11.GL_LINE_SMOOTH); GL11.glDisable(GL11.GL_TEXTURE_2D); - GL11.glPushAttrib(GL11.GL_SCISSOR_BIT); - GL11.glDisable(GL11.GL_SCISSOR_TEST); - GL11.glLineWidth(1); - Tessellator_getInstance().draw(); - GL11.glPopAttrib(); + //#endif + pushScissorState(); + setScissorState(false); + GL11.glLineWidth(2); + tessellator.draw(); + popScissorState(); + //#if MC<11700 GL11.glEnable(GL11.GL_TEXTURE_2D); GL11.glDisable(GL11.GL_LINE_SMOOTH); + //#endif } } }); diff --git a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java index d38ddad1..8df8f8f2 100644 --- a/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java +++ b/src/main/java/com/replaymod/simplepathing/gui/GuiPathing.java @@ -6,6 +6,7 @@ import com.google.common.util.concurrent.Futures; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.SettableFuture; import com.replaymod.core.ReplayMod; +import com.replaymod.core.utils.Result; import com.replaymod.core.utils.Utils; import com.replaymod.pathing.gui.GuiKeyframeRepository; import com.replaymod.pathing.player.RealtimeTimelinePlayer; @@ -101,8 +102,6 @@ public class GuiPathing { public final GuiButton renderButton = new GuiButton().onClick(new Runnable() { @Override public void run() { - Timeline timeline = preparePathsForPlayback(false); - if (timeline == null) return; GuiScreen screen = GuiRenderSettings.createBaseScreen(); new GuiRenderQueue(screen, replayHandler, () -> preparePathsForPlayback(false)) { @Override @@ -263,7 +262,10 @@ public class GuiPathing { } else { boolean ignoreTimeKeyframes = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT); - Timeline timeline = preparePathsForPlayback(ignoreTimeKeyframes); + Timeline timeline = preparePathsForPlayback(ignoreTimeKeyframes).okOrElse(err -> { + GuiInfoPopup.open(overlay, err); + return null; + }); if (timeline == null) return; Path timePath = new SPTimeline(timeline).getTimePath(); @@ -402,23 +404,12 @@ public class GuiPathing { } public void clearKeyframesButtonPressed() { - GuiYesNoPopup popup = GuiYesNoPopup.open(replayHandler.getOverlay(), + GuiYesNoPopup.open(replayHandler.getOverlay(), new GuiLabel().setI18nText("replaymod.gui.clearcallback.title").setColor(Colors.BLACK) - ).setYesI18nLabel("gui.yes").setNoI18nLabel("gui.no"); - Futures.addCallback(popup.getFuture(), new FutureCallback() { - @Override - public void onSuccess(Boolean delete) { - if (delete) { - mod.clearCurrentTimeline(); - if (entityTracker != null) { - mod.getCurrentTimeline().setEntityTracker(entityTracker); - } - } - } - - @Override - public void onFailure(Throwable t) { - t.printStackTrace(); + ).setYesI18nLabel("gui.yes").setNoI18nLabel("gui.no").onAccept(() -> { + mod.clearCurrentTimeline(); + if (entityTracker != null) { + mod.getCurrentTimeline().setEntityTracker(entityTracker); } }); } @@ -508,11 +499,12 @@ public class GuiPathing { }).start(); } - private Timeline preparePathsForPlayback(boolean ignoreTimeKeyframes) { + private Result preparePathsForPlayback(boolean ignoreTimeKeyframes) { SPTimeline spTimeline = mod.getCurrentTimeline(); - if (!validatePathsForPlayback(spTimeline, ignoreTimeKeyframes)) { - return null; + String[] errors = validatePathsForPlayback(spTimeline, ignoreTimeKeyframes); + if (errors != null) { + return Result.err(errors); } try { @@ -520,24 +512,23 @@ public class GuiPathing { String serialized = serialization.serialize(Collections.singletonMap("", spTimeline.getTimeline())); Timeline timeline = serialization.deserialize(serialized).get(""); timeline.getPaths().forEach(Path::updateAll); - return timeline; + return Result.ok(timeline); } catch (Throwable t) { error(LOGGER, replayHandler.getOverlay(), CrashReport.create(t, "Cloning timeline"), () -> {}); - return null; + return Result.err(null); } } - private boolean validatePathsForPlayback(SPTimeline timeline, boolean ignoreTimeKeyframes) { + private String[] validatePathsForPlayback(SPTimeline timeline, boolean ignoreTimeKeyframes) { timeline.getTimeline().getPaths().forEach(Path::updateAll); // Make sure there are at least two position keyframes if (timeline.getPositionPath().getSegments().isEmpty()) { - GuiInfoPopup.open(replayHandler.getOverlay(), "replaymod.chat.morekeyframes"); - return false; + return new String[]{ "replaymod.chat.morekeyframes" }; } if (ignoreTimeKeyframes) { - return true; + return null; } // Make sure time keyframes's values are monotonically increasing @@ -546,22 +537,21 @@ public class GuiPathing { int time = keyframe.getValue(TimestampProperty.PROPERTY).orElseThrow(IllegalStateException::new); if (time < lastTime) { // We are going backwards in time - GuiInfoPopup.open(replayHandler.getOverlay(), + return new String[]{ "replaymod.error.negativetime1", "replaymod.error.negativetime2", - "replaymod.error.negativetime3"); - return false; + "replaymod.error.negativetime3" + }; } lastTime = time; } // Make sure there are at least two time keyframes if (timeline.getTimePath().getSegments().isEmpty()) { - GuiInfoPopup.open(replayHandler.getOverlay(), "replaymod.chat.morekeyframes"); - return false; + return new String[]{ "replaymod.chat.morekeyframes" }; } - return true; + return null; } public void zoomTimeline(double factor) { @@ -655,9 +645,9 @@ public class GuiPathing { CameraEntity camera = replayHandler.getCameraEntity(); int spectatedId = -1; if (!replayHandler.isCameraView() && !neverSpectator) { - spectatedId = getRenderViewEntity(replayHandler.getOverlay().getMinecraft()).getEntityId(); + spectatedId = replayHandler.getOverlay().getMinecraft().getCameraEntity().getEntityId(); } - timeline.addPositionKeyframe(time, Entity_getX(camera), Entity_getY(camera), Entity_getZ(camera), + timeline.addPositionKeyframe(time, camera.getX(), camera.getY(), camera.getZ(), camera.yaw, camera.pitch, camera.roll, spectatedId); mod.setSelected(path, time); } diff --git a/src/main/java/com/replaymod/simplepathing/preview/PathPreviewRenderer.java b/src/main/java/com/replaymod/simplepathing/preview/PathPreviewRenderer.java index d4ff56cd..50782934 100644 --- a/src/main/java/com/replaymod/simplepathing/preview/PathPreviewRenderer.java +++ b/src/main/java/com/replaymod/simplepathing/preview/PathPreviewRenderer.java @@ -1,6 +1,8 @@ package com.replaymod.simplepathing.preview; +import com.mojang.blaze3d.platform.GlStateManager; import com.replaymod.core.ReplayMod; +import com.replaymod.core.events.PostRenderWorldCallback; import com.replaymod.core.versions.MCVer; import com.replaymod.pathing.properties.CameraProperties; import com.replaymod.pathing.properties.SpectatorProperty; @@ -16,31 +18,34 @@ import com.replaymod.simplepathing.ReplayModSimplePathing; import com.replaymod.simplepathing.SPTimeline; import com.replaymod.simplepathing.gui.GuiPathing; import de.johni0702.minecraft.gui.utils.EventRegistrations; +import de.johni0702.minecraft.gui.utils.lwjgl.vector.Vector3f; import net.minecraft.client.MinecraftClient; -import com.mojang.blaze3d.platform.GlStateManager; +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.render.VertexFormats; +import net.minecraft.client.util.math.MatrixStack; import net.minecraft.entity.Entity; import net.minecraft.util.Identifier; import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Triple; import org.lwjgl.opengl.GL11; -//#if MC>=11500 -import com.mojang.blaze3d.systems.RenderSystem; -import net.minecraft.client.util.math.MatrixStack; +//#if MC>=11700 +//$$ import net.minecraft.client.render.GameRenderer; //#endif -//#if FABRIC>=1 -import com.replaymod.core.events.PostRenderWorldCallback; -//#else -//$$ import net.minecraftforge.client.event.RenderWorldLastEvent; -//$$ import net.minecraftforge.eventbus.api.SubscribeEvent; +//#if MC>=11500 +import com.mojang.blaze3d.systems.RenderSystem; //#endif import java.util.Comparator; import java.util.Optional; import static com.replaymod.core.ReplayMod.TEXTURE; -import static com.replaymod.core.versions.MCVer.*; +import static com.replaymod.core.versions.MCVer.bindTexture; +import static com.replaymod.core.versions.MCVer.emitLine; +import static com.replaymod.core.versions.MCVer.popMatrix; +import static com.replaymod.core.versions.MCVer.pushMatrix; public class PathPreviewRenderer extends EventRegistrations { private static final Identifier CAMERA_HEAD = new Identifier("replaymod", "camera_head.png"); @@ -58,20 +63,11 @@ public class PathPreviewRenderer extends EventRegistrations { this.replayHandler = replayHandler; } - //#if FABRIC>=1 { on(PostRenderWorldCallback.EVENT, this::renderCameraPath); } - //#if MC>=11500 private void renderCameraPath(MatrixStack matrixStack) { - //#else - //$$ private void renderCameraPath() { - //#endif - //#else - //$$ @SubscribeEvent - //$$ public void renderCameraPath(RenderWorldLastEvent event) { - //#endif if (!replayHandler.getReplaySender().isAsyncMode() || mc.options.hudHidden) return; - Entity view = getRenderViewEntity(mc); + Entity view = mc.getCameraEntity(); if (view == null) return; GuiPathing guiPathing = mod.getGuiPathing(); @@ -89,24 +85,32 @@ public class PathPreviewRenderer extends EventRegistrations { int renderDistance = mc.options.viewDistance * 16; int renderDistanceSquared = renderDistance * renderDistance; - Triple viewPos = Triple.of( - Entity_getX(view), + Vector3f viewPos = new Vector3f( + (float) view.getX(), + (float) view.getY() //#if MC>=10800 && MC<11500 //$$ // Eye height is subtracted to make path appear higher (at eye height) than it actually is (at foot height) - //$$ Entity_getY(view) - view.getStandingEyeHeight(), + //$$ - view.getStandingEyeHeight(), //#else - Entity_getY(view), + , //#endif - Entity_getZ(view) + (float) view.getZ() ); + //#if MC<11700 GL11.glPushAttrib(GL11.GL_ALL_ATTRIB_BITS); - GL11.glPushMatrix(); + //#endif + pushMatrix(); try { + //#if MC<11700 GL11.glDisable(GL11.GL_LIGHTING); GL11.glDisable(GL11.GL_TEXTURE_2D); + //#endif - //#if MC>=11500 + //#if MC>=11700 + //$$ RenderSystem.getModelViewStack().method_34425(matrixStack.peek().getModel()); + //$$ RenderSystem.applyModelViewMatrix(); + //#elseif MC>=11500 RenderSystem.multMatrix(matrixStack.peek().getModel()); //#endif @@ -122,7 +126,7 @@ public class PathPreviewRenderer extends EventRegistrations { } // Spectator segments have 20 lines per second (at least 10) whereas normal segments have a fixed 100 long steps = spectator ? Math.max(diff / 50, 10) : 100; - Triple prevPos = null; + Vector3f prevPos = null; for (int i = 0; i <= steps; i++) { long time = start.getTime() + diff * i / steps; if (spectator) { @@ -131,24 +135,24 @@ public class PathPreviewRenderer extends EventRegistrations { if (entityId.isPresent() && replayTime.isPresent()) { Location loc = entityTracker.getEntityPositionAtTimestamp(entityId.get(), replayTime.get()); if (loc != null) { - Triple pos = Triple.of(loc.getX(), loc.getY(), loc.getZ()); + Vector3f pos = loc2Vec(loc); if (prevPos != null) { - drawConnection(viewPos, prevPos, pos, 0x0000ff, renderDistanceSquared); + drawConnection(viewPos, prevPos, pos, 0x0000ffff, renderDistanceSquared); } prevPos = pos; continue; } } } else { - Optional> optPos = path.getValue(CameraProperties.POSITION, time); + Optional optPos = path.getValue(CameraProperties.POSITION, time).map(this::tripleD2Vec); if (optPos.isPresent()) { - Triple pos = optPos.get(); + Vector3f pos = optPos.get(); if (prevPos != null) { double distance = Math.sqrt(distanceSquared(prevPos, pos)); double speed = Math.min(distance / (diff / steps), FASTEST_PATH_SPEED); double speedFraction = speed / FASTEST_PATH_SPEED; int color = interpolateColor(SLOW_PATH_COLOR, FAST_PATH_COLOR, speedFraction); - drawConnection(viewPos, prevPos, pos, color, renderDistanceSquared); + drawConnection(viewPos, prevPos, pos, (color << 8) | 0xff, renderDistanceSquared); } prevPos = pos; continue; @@ -159,12 +163,14 @@ public class PathPreviewRenderer extends EventRegistrations { } GL11.glEnable(GL11.GL_BLEND); - GL11.glEnable(GL11.GL_TEXTURE_2D); GL11.glBlendFunc(GL11.GL_DST_COLOR, GL11.GL_SRC_COLOR); GL11.glDisable(GL11.GL_DEPTH_TEST); + //#if MC<11700 + GL11.glEnable(GL11.GL_TEXTURE_2D); + //#endif path.getKeyframes().stream() - .map(k -> Pair.of(k, k.getValue(CameraProperties.POSITION))) + .map(k -> Pair.of(k, k.getValue(CameraProperties.POSITION).map(this::tripleD2Vec))) .filter(p -> p.getRight().isPresent()) .map(p -> Pair.of(p.getLeft(), p.getRight().get())) .filter(p -> distanceSquared(p.getRight(), viewPos) < renderDistanceSquared) @@ -183,26 +189,40 @@ public class PathPreviewRenderer extends EventRegistrations { if (replayTime.isPresent()) { Location loc = entityTracker.getEntityPositionAtTimestamp(entityId.get(), replayTime.get()); if (loc != null) { - drawCamera(viewPos, - Triple.of(loc.getX(), loc.getY(), loc.getZ()), - Triple.of(loc.getYaw(), loc.getPitch(), 0f)); + drawCamera(viewPos, loc2Vec(loc), new Vector3f(loc.getYaw(), loc.getPitch(), 0f)); } } } } else { // Normal camera path - Optional> cameraPos = path.getValue(CameraProperties.POSITION, time); - Optional> cameraRot = path.getValue(CameraProperties.ROTATION, time); + Optional cameraPos = path.getValue(CameraProperties.POSITION, time).map(this::tripleD2Vec); + Optional cameraRot = path.getValue(CameraProperties.ROTATION, time).map(this::tripleF2Vec); if (cameraPos.isPresent() && cameraRot.isPresent()) { drawCamera(viewPos, cameraPos.get(), cameraRot.get()); } } } finally { - GL11.glPopMatrix(); - GlStateManager.popAttributes(); + popMatrix(); + //#if MC>=11700 + //$$ GL11.glDisable(GL11.GL_BLEND); + //#else + GL11.glPopAttrib(); + //#endif } } + private Vector3f loc2Vec(Location loc) { + return new Vector3f((float) loc.getX(), (float) loc.getY(), (float) loc.getZ()); + } + + private Vector3f tripleD2Vec(Triple loc) { + return new Vector3f(loc.getLeft().floatValue(), loc.getMiddle().floatValue(), loc.getRight().floatValue()); + } + + private Vector3f tripleF2Vec(Triple loc) { + return new Vector3f(loc.getLeft(), loc.getMiddle(), loc.getRight()); + } + private static int interpolateColor(int c1, int c2, double weight) { return (interpolateColorComponent((c1 >> 16) & 0xff, (c2 >> 16) & 0xff, weight) << 16) | (interpolateColorComponent((c1 >> 8) & 0xff, (c2 >> 8) & 0xff, weight) << 8) @@ -213,50 +233,34 @@ public class PathPreviewRenderer extends EventRegistrations { return (int) (c1 + (1 - Math.pow(Math.E, -4 * weight)) * (c2 - c1)) & 0xff; } - private static double distanceSquared(Triple p1, Triple p2) { - double dx = p1.getLeft() - p2.getLeft(); - double dy = p1.getMiddle() - p2.getMiddle(); - double dz = p1.getRight() - p2.getRight(); - return dx * dx + dy * dy + dz * dz; + private static double distanceSquared(Vector3f p1, Vector3f p2) { + return Vector3f.sub(p1, p2, null).lengthSquared(); } - private void drawConnection(Triple view, - Triple pos1, - Triple pos2, - int color, int renderDistanceSquared) { + private void drawConnection(Vector3f view, Vector3f pos1, Vector3f pos2, int color, int renderDistanceSquared) { if (distanceSquared(view, pos1) > renderDistanceSquared) return; if (distanceSquared(view, pos2) > renderDistanceSquared) return; - BufferBuilder_beginPosCol(GL11.GL_LINES); + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder buffer = tessellator.getBuffer(); + buffer.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR); - BufferBuilder_addPosCol( - pos1.getLeft() - view.getLeft(), - pos1.getMiddle() - view.getMiddle(), - pos1.getRight() - view.getRight(), - color >> 16 & 0xff, - color >> 8 & 0xff, - color & 0xff, - 255 - ); - BufferBuilder_addPosCol( - pos2.getLeft() - view.getLeft(), - pos2.getMiddle() - view.getMiddle(), - pos2.getRight() - view.getRight(), - color >> 16 & 0xff, - color >> 8 & 0xff, - color & 0xff, - 255 - ); + emitLine(buffer, Vector3f.sub(pos1, view, null), Vector3f.sub(pos2, view, null), color); + //#if MC>=11700 + //$$ RenderSystem.setShader(GameRenderer::getRenderTypeLinesShader); + //$$ RenderSystem.disableCull(); + //#endif GL11.glLineWidth(3); - Tessellator_getInstance().draw(); + tessellator.draw(); + //#if MC>=11700 + //$$ RenderSystem.enableCull(); + //#endif } - private void drawPoint(Triple view, - Triple pos, - Keyframe keyframe) { + private void drawPoint(Vector3f view, Vector3f pos, Keyframe keyframe) { - MCVer.bindTexture(TEXTURE); + bindTexture(TEXTURE); float posX = 80f / ReplayMod.TEXTURE_SIZE; float posY = 0f; @@ -275,121 +279,125 @@ public class PathPreviewRenderer extends EventRegistrations { float maxX = 0.5f; float maxY = 0.5f; - BufferBuilder_beginPosTex(GL11.GL_QUADS); + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder buffer = tessellator.getBuffer(); + buffer.begin(GL11.GL_QUADS, VertexFormats.POSITION_TEXTURE); - BufferBuilder_addPosTex(minX, minY, 0, posX + size, posY + size); - BufferBuilder_addPosTex(minX, maxY, 0, posX + size, posY); - BufferBuilder_addPosTex(maxX, maxY, 0, posX, posY); - BufferBuilder_addPosTex(maxX, minY, 0, posX, posY + size); + buffer.vertex(minX, minY, 0).texture(posX + size, posY + size).next(); + buffer.vertex(minX, maxY, 0).texture(posX + size, posY).next(); + buffer.vertex(maxX, maxY, 0).texture(posX, posY).next(); + buffer.vertex(maxX, minY, 0).texture(posX, posY + size).next(); - GL11.glPushMatrix(); + pushMatrix(); - GL11.glTranslated( - pos.getLeft() - view.getLeft(), - pos.getMiddle() - view.getMiddle(), - pos.getRight() - view.getRight() - ); - GL11.glNormal3f(0, 1, 0); - //#if MC>=11500 - GL11.glRotatef(-getRenderManager().camera.getYaw(), 0, 1, 0); - GL11.glRotatef(getRenderManager().camera.getPitch(), 1, 0, 0); - //#else - //$$ GL11.glRotatef(-getRenderManager().cameraYaw, 0, 1, 0); - //$$ GL11.glRotatef(getRenderManager().cameraPitch, 1, 0, 0); + Vector3f t = Vector3f.sub(pos, view, null); + GL11.glTranslatef(t.x, t.y, t.z); + GL11.glRotatef(-mc.getEntityRenderDispatcher().camera.getYaw(), 0, 1, 0); + GL11.glRotatef(mc.getEntityRenderDispatcher().camera.getPitch(), 1, 0, 0); + + //#if MC>=11700 + //$$ RenderSystem.applyModelViewMatrix(); + //$$ RenderSystem.setShader(GameRenderer::getPositionTexShader); //#endif + tessellator.draw(); - Tessellator_getInstance().draw(); - - GL11.glPopMatrix(); + popMatrix(); } - private void drawCamera(Triple view, - Triple pos, - Triple rot) { + private void drawCamera(Vector3f view, Vector3f pos, Vector3f rot) { - MCVer.bindTexture(CAMERA_HEAD); + bindTexture(CAMERA_HEAD); - GL11.glPushMatrix(); + pushMatrix(); - GL11.glTranslated( - pos.getLeft() - view.getLeft(), - pos.getMiddle() - view.getMiddle(), - pos.getRight() - view.getRight() - ); - GL11.glRotated(-rot.getLeft(), 0, 1, 0); // Yaw - GL11.glRotated(rot.getMiddle(), 1, 0, 0); // Pitch - GL11.glRotated(rot.getRight(), 0, 0, 1); // Roll - GL11.glNormal3f(0, 1, 0); + Vector3f t = Vector3f.sub(pos, view, null); + GL11.glTranslatef(t.x, t.y, t.z); + GL11.glRotatef(-rot.x, 0, 1, 0); // Yaw + GL11.glRotatef(rot.y, 1, 0, 0); // Pitch + GL11.glRotatef(rot.z, 0, 0, 1); // Roll //draw the position line + Tessellator tessellator = Tessellator.getInstance(); + BufferBuilder buffer = tessellator.getBuffer(); + buffer.begin(GL11.GL_LINES, VertexFormats.POSITION_COLOR); + + emitLine(buffer, new Vector3f(0, 0, 0), new Vector3f(0, 0, 2), 0x00ff00aa); + + //#if MC>=11700 + //$$ RenderSystem.applyModelViewMatrix(); + //$$ RenderSystem.setShader(GameRenderer::getRenderTypeLinesShader); + //#else GL11.glDisable(GL11.GL_TEXTURE_2D); - BufferBuilder_beginPosCol(GL11.GL_LINES); + //#endif - BufferBuilder_addPosCol(0, 0, 0, 0, 255, 0, 170); - BufferBuilder_addPosCol(0, 0, 2, 0, 255, 0, 170); + tessellator.draw(); - Tessellator_getInstance().draw(); + //#if MC<11700 + GL11.glEnable(GL11.GL_TEXTURE_2D); + //#endif // draw camera cube - GL11.glEnable(GL11.GL_TEXTURE_2D); float cubeSize = 0.5f; double r = -cubeSize/2; - BufferBuilder_beginPosTexCol(GL11.GL_QUADS); + buffer.begin(GL11.GL_QUADS, VertexFormats.POSITION_TEXTURE_COLOR); //back - BufferBuilder_addPosTexCol(r, r + cubeSize, r, 3 * 8 / 64f, 8 / 64f, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r + cubeSize, r + cubeSize, r, 4*8/64f, 8/64f, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r + cubeSize, r, r, 4*8/64f, 2*8/64f, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r, r, r, 3*8/64f, 2*8/64f, 255, 255, 255, 200); + buffer.vertex(r, r + cubeSize, r).texture(3 * 8 / 64f, 8 / 64f).color(255, 255, 255, 200).next(); + buffer.vertex(r + cubeSize, r + cubeSize, r).texture(4*8/64f, 8/64f).color(255, 255, 255, 200).next(); + buffer.vertex(r + cubeSize, r, r).texture(4*8/64f, 2*8/64f).color(255, 255, 255, 200).next(); + buffer.vertex(r, r, r).texture(3*8/64f, 2*8/64f).color(255, 255, 255, 200).next(); //front - BufferBuilder_addPosTexCol(r + cubeSize, r, r + cubeSize, 2 * 8 / 64f, 2*8/64f, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r + cubeSize, r + cubeSize, r + cubeSize, 2 * 8 / 64f, 8/64f, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r, r + cubeSize, r + cubeSize, 8 / 64f, 8 / 64f, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r, r, r + cubeSize, 8 / 64f, 2*8/64f, 255, 255, 255, 200); + buffer.vertex(r + cubeSize, r, r + cubeSize).texture(2 * 8 / 64f, 2*8/64f).color(255, 255, 255, 200).next(); + buffer.vertex(r + cubeSize, r + cubeSize, r + cubeSize).texture(2 * 8 / 64f, 8/64f).color(255, 255, 255, 200).next(); + buffer.vertex(r, r + cubeSize, r + cubeSize).texture(8 / 64f, 8 / 64f).color(255, 255, 255, 200).next(); + buffer.vertex(r, r, r + cubeSize).texture(8 / 64f, 2*8/64f).color(255, 255, 255, 200).next(); //left - BufferBuilder_addPosTexCol(r + cubeSize, r + cubeSize, r, 0, 8/64f, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r + cubeSize, r + cubeSize, r + cubeSize, 8/64f, 8/64f, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r + cubeSize, r, r + cubeSize, 8/64f, 2*8/64f, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r+cubeSize, r, r, 0, 2*8/64f, 255, 255, 255, 200); + buffer.vertex(r + cubeSize, r + cubeSize, r).texture(0, 8/64f).color(255, 255, 255, 200).next(); + buffer.vertex(r + cubeSize, r + cubeSize, r + cubeSize).texture(8/64f, 8/64f).color(255, 255, 255, 200).next(); + buffer.vertex(r + cubeSize, r, r + cubeSize).texture(8/64f, 2*8/64f).color(255, 255, 255, 200).next(); + buffer.vertex(r+cubeSize, r, r).texture(0, 2*8/64f).color(255, 255, 255, 200).next(); //right - BufferBuilder_addPosTexCol(r, r + cubeSize, r + cubeSize, 2*8/64f, 8/64f, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r, r + cubeSize, r, 3*8/64f, 8/64f, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r, r, r, 3*8/64f, 2*8/64f, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r, r, r + cubeSize, 2 * 8 / 64f, 2 * 8 / 64f, 255, 255, 255, 200); + buffer.vertex(r, r + cubeSize, r + cubeSize).texture(2*8/64f, 8/64f).color(255, 255, 255, 200).next(); + buffer.vertex(r, r + cubeSize, r).texture(3*8/64f, 8/64f).color(255, 255, 255, 200).next(); + buffer.vertex(r, r, r).texture(3*8/64f, 2*8/64f).color(255, 255, 255, 200).next(); + buffer.vertex(r, r, r + cubeSize).texture(2 * 8 / 64f, 2 * 8 / 64f).color(255, 255, 255, 200).next(); //bottom - BufferBuilder_addPosTexCol(r + cubeSize, r, r, 3*8/64f, 0, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r + cubeSize, r, r + cubeSize, 3*8/64f, 8/64f, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r, r, r + cubeSize, 2*8/64f, 8/64f, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r, r, r, 2 * 8 / 64f, 0, 255, 255, 255, 200); + buffer.vertex(r + cubeSize, r, r).texture(3*8/64f, 0).color(255, 255, 255, 200).next(); + buffer.vertex(r + cubeSize, r, r + cubeSize).texture(3*8/64f, 8/64f).color(255, 255, 255, 200).next(); + buffer.vertex(r, r, r + cubeSize).texture(2*8/64f, 8/64f).color(255, 255, 255, 200).next(); + buffer.vertex(r, r, r).texture(2 * 8 / 64f, 0).color(255, 255, 255, 200).next(); //top - BufferBuilder_addPosTexCol(r, r + cubeSize, r, 8/64f, 0, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r, r + cubeSize, r + cubeSize, 8/64f, 8/64f, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r + cubeSize, r + cubeSize, r + cubeSize, 2*8/64f, 8/64f, 255, 255, 255, 200); - BufferBuilder_addPosTexCol(r + cubeSize, r + cubeSize, r, 2 * 8 / 64f, 0, 255, 255, 255, 200); + buffer.vertex(r, r + cubeSize, r).texture(8/64f, 0).color(255, 255, 255, 200).next(); + buffer.vertex(r, r + cubeSize, r + cubeSize).texture(8/64f, 8/64f).color(255, 255, 255, 200).next(); + buffer.vertex(r + cubeSize, r + cubeSize, r + cubeSize).texture(2*8/64f, 8/64f).color(255, 255, 255, 200).next(); + buffer.vertex(r + cubeSize, r + cubeSize, r).texture(2 * 8 / 64f, 0).color(255, 255, 255, 200).next(); - Tessellator_getInstance().draw(); + //#if MC>=11700 + //$$ RenderSystem.applyModelViewMatrix(); + //$$ RenderSystem.setShader(GameRenderer::getPositionTexColorShader); + //#endif + tessellator.draw(); - GL11.glPopMatrix(); + popMatrix(); } - private class KeyframeComparator implements Comparator>> { - private final Triple viewPos; + private class KeyframeComparator implements Comparator> { + private final Vector3f viewPos; - public KeyframeComparator(Triple viewPos) { + public KeyframeComparator(Vector3f viewPos) { this.viewPos = viewPos; } @Override - public int compare(Pair> o1, - Pair> o2) { + public int compare(Pair o1, Pair o2) { return -Double.compare(distanceSquared(o1.getRight(), viewPos), distanceSquared(o2.getRight(), viewPos)); } } diff --git a/src/main/resources/assets/replaymod/iris/ods/shaders/gbuffers_textured_lit.fsh b/src/main/resources/assets/replaymod/iris/ods/shaders/gbuffers_textured_lit.fsh new file mode 100644 index 00000000..76b37f95 --- /dev/null +++ b/src/main/resources/assets/replaymod/iris/ods/shaders/gbuffers_textured_lit.fsh @@ -0,0 +1,15 @@ +#version 120 + +uniform sampler2D texture; +uniform sampler2D lightmap; + +varying vec4 color; +varying vec2 texcoord; +varying vec2 lmcoord; + +void main() { + vec4 lightmap = texture2D(lightmap, lmcoord); + vec4 color = texture2D(texture, texcoord) * color; + color *= lightmap; + gl_FragColor = color; +} \ No newline at end of file diff --git a/src/main/resources/assets/replaymod/iris/ods/shaders/gbuffers_textured_lit.vsh b/src/main/resources/assets/replaymod/iris/ods/shaders/gbuffers_textured_lit.vsh new file mode 100644 index 00000000..3b53c589 --- /dev/null +++ b/src/main/resources/assets/replaymod/iris/ods/shaders/gbuffers_textured_lit.vsh @@ -0,0 +1,69 @@ +#version 120 + +varying vec4 color; +varying vec2 texcoord; +varying vec2 lmcoord; + +uniform int leftEye; +uniform int direction; + +const float eyeDistance = 0.14; + +void main() { + // Transform to view space + vec4 position = gl_ModelViewMatrix * gl_Vertex; + + // Distort for ODS + // O := The origin + // P := The current vertex/point + // T := Point of tangency with the tangent going through P + // Distance between P and O + float distPO = sqrt(position.x * position.x + position.z * position.z); + float distTO = eyeDistance * 0.5; + // Angle between PO and PT + float angP = acos(distTO / distPO); + // Angle between PO and TO (angle at the origin) + float angO = 90.0 - angP; + if (leftEye == 0) { + angO = -angO; + } + // The angel of OP within the circle, that is between OP and O(0,1) + float angOP = atan(position.x, position.z); + // The angle of OT within the circle, that is between OT and O(0,1) + float angOT = angO + angOP; + // Calculate the vector between O and T and finally move the vertex by that vector + position -= vec4(distTO * sin(angOT), 0, distTO * cos(angOT), 0); + + // Rotate for different cubic views + float z; + if (direction == 0) { // LEFT + z = position.z; + position.z = position.x; + position.x = -z; + } else if (direction == 1) { // RIGHT + z = position.z; + position.z = -position.x; + position.x = z; + } else if (direction == 2) { // FRONT + // No changes required + } else if (direction == 3) { // BACK + position.x = -position.x; + position.z = -position.z; + } else if (direction == 4) { // TOP + z = position.z; + position.z = -position.y; + position.y = z; + } else if (direction == 5) { // BOTTOM + z = position.z; + position.z = position.y; + position.y = -z; + } + + // Transform to screen space + gl_Position = gl_ProjectionMatrix * position; + + // Misc. + color = gl_Color; + texcoord = (gl_TextureMatrix[0] * gl_MultiTexCoord0).xy; + lmcoord = (gl_TextureMatrix[1] * gl_MultiTexCoord1).xy; +} \ No newline at end of file diff --git a/src/main/resources/assets/replaymod/lang b/src/main/resources/assets/replaymod/lang index 63b7a8be..54ab62bd 160000 --- a/src/main/resources/assets/replaymod/lang +++ b/src/main/resources/assets/replaymod/lang @@ -1 +1 @@ -Subproject commit 63b7a8bea1ab9a9f005c9ae0e1c54b4432cbc9e6 +Subproject commit 54ab62bdbb646c6b4f4b6d61a26183258e5a017d diff --git a/src/main/resources/assets/replaymod/logo_button.png b/src/main/resources/assets/replaymod/logo_button.png new file mode 100644 index 00000000..6e4d88f1 Binary files /dev/null and b/src/main/resources/assets/replaymod/logo_button.png differ diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index 6bc530eb..6eb336ce 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -20,11 +20,14 @@ "environment": "client", "entrypoints": { "client": [ - "com.replaymod.core.ReplayMod" + "com.replaymod.core.ReplayModBackend" ], "modmenu": [ "com.replaymod.core.gui.ModMenuApiImpl" ], + "frex_flawless_frames": [ + "com.replaymod.render.utils.FlawlessFrames::registerConsumer" + ], "preLaunch": [ "com.replaymod.core.DummyChainLoadEntryPoint" ], diff --git a/src/main/resources/mcmod.info b/src/main/resources/mcmod.info index 3432d978..4c7f1a50 100644 --- a/src/main/resources/mcmod.info +++ b/src/main/resources/mcmod.info @@ -117,22 +117,6 @@ "screenshots": [], "dependencies": [] }, - { - "modid": "replaymod-restrictions", - "name": "Replay Mod - Restrictions", - "description": "Server-side Restrictions Module of the ReplayMod - Restrict what clients can do in replays", - "version": "${version}", - "mcversion": "${mcversion}", - "url": "https://replaymod.com", - "updateUrl": "https://replaymod.com/download", - "authorList": [ - "CrushedPixel", - "johni0702" - ], - "logoFile": "replaymod_logo.png", - "screenshots": [], - "dependencies": [] - }, { "modid": "replaymod-compat", "name": "Replay Mod - Compatibility", diff --git a/src/main/resources/mixins.core.replaymod.json b/src/main/resources/mixins.core.replaymod.json index c002fc17..c0b0a787 100644 --- a/src/main/resources/mixins.core.replaymod.json +++ b/src/main/resources/mixins.core.replaymod.json @@ -4,9 +4,9 @@ "mixins": [], "server": [], "client": [ + "Mixin_ContextualKeyBindings", //#if MC>=11400 "AbstractButtonWidgetAccessor", - "Mixin_ContextualKeyBindings", "MixinGameRenderer", "Mixin_HideDynamicResourcePacks", "Mixin_RegisterDynamicResourcePacks", diff --git a/src/main/resources/mixins.render.replaymod.json b/src/main/resources/mixins.render.replaymod.json index 976fbf9b..7bddb362 100644 --- a/src/main/resources/mixins.render.replaymod.json +++ b/src/main/resources/mixins.render.replaymod.json @@ -5,9 +5,25 @@ "server": [], "client": [ "Mixin_ChromaKeyColorSky", + "Mixin_ChromaKeyDisableFog", "Mixin_ChromaKeyForceSky", - "Mixin_CubicRotation", + "Mixin_ForceChunkLoading", + "Mixin_HideNameTags", + "Mixin_HideNameTags_LivingEntity", + "Mixin_Omnidirectional_Camera", + "Mixin_Omnidirectional_DisableFrustumCulling", + "Mixin_Omnidirectional_Rotation", + "Mixin_Omnidirectional_SkipHand", + "Mixin_PreserveDepthDuringGuiRendering", "Mixin_SkipBlockOutlinesDuringRender", + "Mixin_SkipHudDuringRender", + "Mixin_StabilizeCamera", + "Mixin_Stereoscopic_Camera", + "Mixin_Stereoscopic_HandRenderPass", + //#if MC>=11600 + "Mixin_AddIrisOdsShaderUniforms", + "Mixin_LoadIrisOdsShaderPack", + //#endif //#if MC>=10800 //#if MC>=11500 "Mixin_BlockOnChunkRebuilds", @@ -15,30 +31,22 @@ //$$ "ChunkRenderDispatcherAccessor", //$$ "MixinChunkRenderWorker", //#endif - "Mixin_ForceChunkLoading", //#endif //#if MC>=11400 "Mixin_PreserveDepthDuringHandRendering", "Mixin_WindowsWorkaroundForTinyEXRNatives", - "MainWindowAccessor", //#endif + "MainWindowAccessor", "WorldRendererAccessor", //#if MC>=10904 "MixinParticleManager", //#else //$$ "MixinEffectRenderer", - //$$ "MixinRendererLivingEntity", //#endif - "MixinCamera", "MixinEntityRenderer", - //#if MC>=11400 - "MixinFogRenderer", - //#endif - //#if MC>=10800 + //#if MC>=10800 && MC<11700 "MixinGlStateManager", //#endif - "MixinFrustum", - "MixinRender", //#if MC>=10800 //#else //$$ "MixinOpenGlHelper", diff --git a/src/main/resources/mixins.replay.replaymod.json b/src/main/resources/mixins.replay.replaymod.json index ff2737f1..feb30fa7 100644 --- a/src/main/resources/mixins.replay.replaymod.json +++ b/src/main/resources/mixins.replay.replaymod.json @@ -5,6 +5,7 @@ "mixins": [], "server": [], "client": [ + "Mixin_FixNPCSkinCaching", //#if MC>=11600 "Mixin_MoveRealmsButton", //#endif diff --git a/version.txt b/version.txt index f225a78a..e70b4523 100644 --- a/version.txt +++ b/version.txt @@ -1 +1 @@ -2.5.2 +2.6.0 diff --git a/versions/1.11.2/src/main/java/com/replaymod/core/utils/ModInfoGetter.java b/versions/1.11.2/src/main/java/com/replaymod/core/utils/ModInfoGetter.java new file mode 100644 index 00000000..0d9db53a --- /dev/null +++ b/versions/1.11.2/src/main/java/com/replaymod/core/utils/ModInfoGetter.java @@ -0,0 +1,29 @@ +package com.replaymod.core.utils; + +import com.replaymod.replaystudio.data.ModInfo; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.ModContainer; +import net.minecraftforge.fml.common.registry.GameData; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +class ModInfoGetter { + static Collection getInstalledNetworkMods() { + Map ignoreCaseMap = Loader.instance().getModList().stream() + .collect(Collectors.toMap(m -> m.getModId().toLowerCase(), Function.identity())); + return Stream.concat( + ((Set) GameData.getBlockRegistry().getKeys()).stream(), + ((Set) GameData.getItemRegistry().getKeys()).stream() + ).map(ResourceLocation::getResourceDomain).filter(s -> !s.equals("minecraft")).distinct() + .map(String::toLowerCase).map(ignoreCaseMap::get).filter(Objects::nonNull) + .map(mod -> new ModInfo(mod.getModId(), mod.getName(), mod.getVersion())) + .collect(Collectors.toList()); + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/compat/bettersprinting/DisableBetterSprinting.java b/versions/1.12.2/src/main/java/com/replaymod/compat/bettersprinting/DisableBetterSprinting.java new file mode 100644 index 00000000..127450d5 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/compat/bettersprinting/DisableBetterSprinting.java @@ -0,0 +1,183 @@ +package com.replaymod.compat.bettersprinting; + +import com.replaymod.replay.ReplayModReplay; +import com.replaymod.replay.events.ReplayChatMessageEvent; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.PlayerControllerMP; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraftforge.client.event.GuiOpenEvent; +import net.minecraftforge.common.MinecraftForge; + +//#if MC>=10904 +import net.minecraft.block.state.IBlockState; +import net.minecraft.util.SoundCategory; +import net.minecraft.util.SoundEvent; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.IWorldEventListener; +import net.minecraft.world.World; + +import javax.annotation.Nullable; +//#else +//#if MC>= 10800 +//$$ import net.minecraft.util.BlockPos; +//#endif +//$$ import net.minecraft.world.IWorldAccess; +//#endif + +//#if MC>=10800 +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.ModContainer; +import net.minecraftforge.fml.common.eventhandler.EventPriority; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.versioning.DefaultArtifactVersion; +import net.minecraftforge.fml.common.versioning.Restriction; +import net.minecraftforge.fml.common.versioning.VersionRange; +//#else +//$$ import cpw.mods.fml.common.Loader; +//$$ import cpw.mods.fml.common.ModContainer; +//$$ import cpw.mods.fml.common.eventhandler.EventPriority; +//$$ import cpw.mods.fml.common.eventhandler.SubscribeEvent; +//$$ import cpw.mods.fml.common.versioning.DefaultArtifactVersion; +//$$ import cpw.mods.fml.common.versioning.Restriction; +//$$ import cpw.mods.fml.common.versioning.VersionRange; +//#endif + +import java.util.Collections; + +import static com.replaymod.compat.ReplayModCompat.LOGGER; +import static com.replaymod.core.versions.MCVer.*; + +/** + * Old Better Sprinting versions replace the vanilla player with their own, overridden instance (replacing the camera entity). + * + * See: https://github.com/chylex/Better-Sprinting/blob/1.8/src/main/java/chylex/bettersprinting/client/player/impl/LogicImplOverride.java + */ +public class DisableBetterSprinting { + private static final VersionRange OLD_VERSION = VersionRange.newRange(null, + Collections.singletonList(new Restriction(null, false, new DefaultArtifactVersion("2.0.0"), false))); + private static final String LOGIC_CLASS_NAME = "chylex.bettersprinting.client.player.impl.LogicImplOverride"; + private static final String CONTROLLER_OVERRIDE_CLASS_NAME = LOGIC_CLASS_NAME + "$PlayerControllerMPOverride"; + + public static void register() { + Loader.instance().getModList().stream() + .filter(mod -> mod.getModId().equalsIgnoreCase("bettersprinting")) + .findFirst() + .map(ModContainer::getProcessedVersion).filter(OLD_VERSION::containsVersion) + .ifPresent($_ -> MinecraftForge.EVENT_BUS.register(new DisableBetterSprinting())); + } + + private DisableBetterSprinting() { + LOGGER.info("BetterSprinting workaround enabled"); + } + + private final Minecraft mc = Minecraft.getMinecraft(); + private PlayerControllerMP originalController; + private BetterSprintingWorldAccess worldAccessHook = new BetterSprintingWorldAccess(); + + @SubscribeEvent(priority = EventPriority.HIGH) + public void beforeGuiOpenEvent(GuiOpenEvent event) { + if (ReplayModReplay.instance.getReplayHandler() != null && mc.world != null) { + // During replay, get ready to revert BetterSprinting's overwritten playerController + originalController = mc.playerController; + //#if MC>=10904 + mc.world.addEventListener(worldAccessHook); + //#else + //$$ mc.theWorld.addWorldAccess(worldAccessHook); + //#endif + } + } + + @SubscribeEvent(priority = EventPriority.LOW) + public void afterGuiOpenEvent(GuiOpenEvent event) { + if (ReplayModReplay.instance.getReplayHandler() != null && mc.world != null) { + //#if MC>=10904 + mc.world.addEventListener(worldAccessHook); + //#else + //$$ mc.theWorld.addWorldAccess(worldAccessHook); + //#endif + } + } + + @SubscribeEvent + public void onReplayChatMessage(ReplayChatMessageEvent event) { + // Suppress this message if it's the Better Sprinting warning message + for (StackTraceElement elem : Thread.currentThread().getStackTrace()) { + if (LOGIC_CLASS_NAME.equals(elem.getClassName())) { + LOGGER.info("BetterSprinting warning message suppressed."); + event.setCanceled(true); + return; + } + } + } + + private class BetterSprintingWorldAccess + //#if MC>=10904 + implements IWorldEventListener + //#else + //$$ implements IWorldAccess + //#endif + { + @Override + //#if MC>=10800 + public void onEntityRemoved(Entity entityIn) { + //#else + //$$ public void onEntityDestroy(Entity entityIn) { + //#endif + if (mc.playerController != null && mc.playerController.getClass().getName().equals(CONTROLLER_OVERRIDE_CLASS_NAME)) { + // Someone has secretly swapped out the player controller and is about to substitute their own player entity. + // This is the right time to destroy their plan. + LOGGER.info("Preventing player controller {} from being replaced by BetterSprinting with {}.", originalController, mc.playerController); + mc.playerController = originalController; + } + } + + @Override public void markBlockRangeForRenderUpdate(int x1, int y1, int z1, int x2, int y2, int z2) {} + //#if MC>=10800 + @Override public void notifyLightSet(BlockPos pos) {} + @Override public void spawnParticle(int p_180442_1_, boolean p_180442_2_, double p_180442_3_, double p_180442_5_, double p_180442_7_, double p_180442_9_, double p_180442_11_, double p_180442_13_, int... p_180442_15_) {} + @Override public void onEntityAdded(Entity entityIn) {} + @Override public void broadcastSound(int p_180440_1_, BlockPos p_180440_2_, int p_180440_3_) {} + @Override public void sendBlockBreakProgress(int breakerId, BlockPos pos, int progress) {} + //#else + //$$ @Override public void markBlockForRenderUpdate(int x, int y, int z) {} + //$$ @Override public void spawnParticle(String p_72708_1_, double p_72708_2_, double p_72708_4_, double p_72708_6_, double p_72708_8_, double p_72708_10_, double p_72708_12_) {} + //$$ @Override public void onEntityCreate(Entity entityIn) {} + //$$ @Override public void broadcastSound(int p_180440_1_, int x, int y, int z, int p_180440_3_) {} + //$$ @Override public void destroyBlockPartially(int breakerId, int x, int y, int z, int progress) {} + //$$ @Override public void onStaticEntitiesChanged() {} + //#endif + //#if MC>=10904 + @Override public void notifyBlockUpdate(World worldIn, BlockPos pos, IBlockState oldState, IBlockState newState, int flags) {} + @Override public void playSoundToAllNearExcept(@Nullable EntityPlayer player, SoundEvent soundIn, SoundCategory category, double x, double y, double z, float volume, float pitch) {} + @Override public void playRecord(SoundEvent soundIn, BlockPos pos) {} + @Override public void playEvent(EntityPlayer player, int type, BlockPos blockPosIn, int data) {} + //#if MC>=11100 + //#if MC>=11102 + @Override public void spawnParticle(int p_190570_1_, boolean p_190570_2_, boolean p_190570_3_, double p_190570_4_, double p_190570_6_, double p_190570_8_, double p_190570_10_, double p_190570_12_, double p_190570_14_, int... p_190570_16_) {} + //#else + //$$ @Override public void func_190570_a(int p_190570_1_, boolean p_190570_2_, boolean p_190570_3_, double p_190570_4_, double p_190570_6_, double p_190570_8_, double p_190570_10_, double p_190570_12_, double p_190570_14_, int... p_190570_16_) {} + //#endif + //#endif + //#else + //#if MC>=10800 + //$$ @Override public void markBlockForUpdate(BlockPos pos) {} + //$$ @Override public void playRecord(String recordName, BlockPos blockPosIn) {} + //#else + //$$ @Override public void markBlockForUpdate(int x, int y, int z) {} + //$$ @Override public void playRecord(String recordName, int x, int y, int z) {} + //#endif + //$$ @Override public void playSound(String soundName, double x, double y, double z, float volume, float pitch) {} + //$$ @Override public void playSoundToNearExcept(EntityPlayer except, String soundName, double x, double y, double z, float volume, float pitch) {} + //#if MC>=10809 + //$$ @Override public void playAuxSFX(EntityPlayer p_180439_1_, int p_180439_2_, BlockPos blockPosIn, int p_180439_4_) {} + //#else + //#if MC>=10800 + //$$ @Override public void playAusSFX(EntityPlayer p_180439_1_, int p_180439_2_, BlockPos blockPosIn, int p_180439_4_) {} + //#else + //$$ @Override public void playAuxSFX(EntityPlayer p_72706_1_, int p_72706_2_, int p_72706_3_, int p_72706_4_, int p_72706_5_, int p_72706_6_) {} + //#endif + //#endif + //#endif + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/compat/mapwriter/mixin/MixinMinecraft.java b/versions/1.12.2/src/main/java/com/replaymod/compat/mapwriter/mixin/MixinMinecraft.java new file mode 100644 index 00000000..7a4a792c --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/compat/mapwriter/mixin/MixinMinecraft.java @@ -0,0 +1,44 @@ +package com.replaymod.compat.mapwriter.mixin; + +import com.replaymod.replay.ReplayModReplay; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ServerData; +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.CallbackInfoReturnable; + +//#if MC>=10800 +import net.minecraftforge.fml.common.Loader; +//#else +//$$ import cpw.mods.fml.common.Loader; +//#endif + +/** + * Approximately this for <1.12: https://github.com/Vectron/mapwriter/commit/68234520c7a3a0ae8201a085d7e66369900586ac + */ +@Mixin(Minecraft.class) +public abstract class MixinMinecraft { + + @Shadow + private ServerData currentServerData; + + @Inject(method = "getCurrentServerData", cancellable = true, at = @At("HEAD")) + private void replayModCompat_fixBug96(CallbackInfoReturnable ci) { + if (currentServerData == null + && (Loader.isModLoaded("mapwriter") || Loader.isModLoaded("MapWriter")) + && ReplayModReplay.instance.getReplayHandler() != null) { + for (StackTraceElement elem : Thread.currentThread().getStackTrace()) { + if ("mapwriter.util.Utils".equals(elem.getClassName()) && "getWorldName".equals(elem.getMethodName())) { + //#if MC>=10809 + ci.setReturnValue(new ServerData(null, "replay", false)); + //#else + //$$ ci.setReturnValue(new ServerData(null, "replay")); + //#endif + return; + } + } + } + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/compat/oranges17animations/HideInvisibleEntities.java b/versions/1.12.2/src/main/java/com/replaymod/compat/oranges17animations/HideInvisibleEntities.java new file mode 100644 index 00000000..01aec589 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/compat/oranges17animations/HideInvisibleEntities.java @@ -0,0 +1,29 @@ +package com.replaymod.compat.oranges17animations; + +import com.replaymod.core.ReplayMod; +import com.replaymod.replay.camera.CameraEntity; +import de.johni0702.minecraft.gui.utils.EventRegistrations; +import net.minecraft.client.Minecraft; +import net.minecraftforge.client.event.RenderLivingEvent; +import net.minecraftforge.fml.common.eventhandler.EventPriority; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; + +/** + * Orange seems to have copied vast parts of the RendererLivingEntity into their ArmorAnimation class which cancels the RenderLivingEvent.Pre and calls its own code instead. + * This breaks our mixin which assures that, even though the camera is in spectator mode, it cannot see invisible entities. + * + * To fix this issue, we simply cancel the RenderLivingEvent.Pre before it gets to ArmorAnimation if the entity is invisible. + */ +public class HideInvisibleEntities extends EventRegistrations { + private final Minecraft mc = Minecraft.getMinecraft(); + private final boolean modLoaded = ReplayMod.instance.isModLoaded("animations"); + + @SubscribeEvent(priority = EventPriority.HIGH) + public void preRenderLiving(RenderLivingEvent.Pre event) { + if (modLoaded) { + if (mc.player instanceof CameraEntity && event.getEntity().isInvisible()) { + event.setCanceled(true); + } + } + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/core/LoadingPlugin.java b/versions/1.12.2/src/main/java/com/replaymod/core/LoadingPlugin.java new file mode 100644 index 00000000..cf6d0389 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/core/LoadingPlugin.java @@ -0,0 +1,102 @@ +package com.replaymod.core; + +import net.minecraft.launchwrapper.Launch; +import org.apache.logging.log4j.LogManager; +import org.spongepowered.asm.launch.MixinBootstrap; +import org.spongepowered.asm.mixin.Mixins; + +import net.minecraftforge.fml.relauncher.CoreModManager; +import net.minecraftforge.fml.relauncher.IFMLLoadingPlugin; +//#if MC<10800 +//$$ import com.replaymod.core.asm.GLErrorTransformer; +//$$ import com.replaymod.core.asm.GLStateTrackerTransformer; +//$$ +//$$ import java.util.ArrayList; +//$$ import java.util.List; +//#endif + +import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; +import java.security.CodeSource; +import java.util.Map; + +@IFMLLoadingPlugin.TransformerExclusions("com.replaymod.core.asm.") +public class LoadingPlugin implements IFMLLoadingPlugin { + + public LoadingPlugin() { + if (Launch.blackboard.get("fml.deobfuscatedEnvironment") != Boolean.FALSE) { + // Outside of the dev env, this is the job of the tweaker + MixinBootstrap.init(); + } + + Mixins.addConfiguration("mixins.core.replaymod.json"); + Mixins.addConfiguration("mixins.recording.replaymod.json"); + Mixins.addConfiguration("mixins.render.replaymod.json"); + Mixins.addConfiguration("mixins.render.blend.replaymod.json"); + Mixins.addConfiguration("mixins.replay.replaymod.json"); + Mixins.addConfiguration("mixins.compat.mapwriter.replaymod.json"); + Mixins.addConfiguration("mixins.compat.shaders.replaymod.json"); + Mixins.addConfiguration("mixins.extras.playeroverview.replaymod.json"); + + CodeSource codeSource = getClass().getProtectionDomain().getCodeSource(); + if (codeSource != null) { + URL location = codeSource.getLocation(); + try { + File file = new File(location.toURI()); + if (file.isFile()) { + // This forces forge to reexamine the jar file for FML mods + // Should eventually be handled by Mixin itself, maybe? + //#if MC>=10809 + CoreModManager.getIgnoredMods().remove(file.getName()); + //#else + //$$ CoreModManager.getLoadedCoremods().remove(file.getName()); + //#if MC<=10710 + //$$ CoreModManager.getReparseableCoremods().add(file.getName()); + //#endif + //#endif + } + } catch (URISyntaxException e) { + e.printStackTrace(); + } + } else { + LogManager.getLogger().warn("No CodeSource, if this is not a development environment we might run into problems!"); + LogManager.getLogger().warn(getClass().getProtectionDomain()); + } + } + + @Override + public String[] getASMTransformerClass() { + //#if MC>=10800 + return new String[]{ + }; + //#else + //$$ List transformers = new ArrayList<>(); + //$$ if ("true".equals(System.getProperty("replaymod.glerrors", "false"))) { + //$$ transformers.add(GLErrorTransformer.class.getName()); + //$$ } + //$$ transformers.add(GLStateTrackerTransformer.class.getName()); + //$$ return transformers.stream().toArray(String[]::new); + //#endif + } + + @Override + public String getModContainerClass() { + return null; + } + + @Override + public String getSetupClass() { + return null; + } + + @Override + public void injectData(Map data) { + } + + @Override + public String getAccessTransformerClass() { + return null; + } + +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/core/ReplayModBackend.java b/versions/1.12.2/src/main/java/com/replaymod/core/ReplayModBackend.java new file mode 100644 index 00000000..6da12741 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/core/ReplayModBackend.java @@ -0,0 +1,96 @@ +package com.replaymod.core; + +import com.replaymod.core.mixin.MinecraftAccessor; +import com.replaymod.core.versions.forge.EventsAdapter; +import net.minecraft.client.resources.IResourcePack; +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.Mod; +import net.minecraftforge.fml.common.Mod.EventHandler; +import net.minecraftforge.fml.common.event.FMLInitializationEvent; +import net.minecraftforge.fml.common.event.FMLPreInitializationEvent; + +import java.util.List; + +import static com.replaymod.core.ReplayMod.MOD_ID; +import static com.replaymod.core.ReplayMod.jGuiResourcePack; +import static com.replaymod.core.versions.MCVer.getMinecraft; + +//#if MC<=10710 +//$$ import net.minecraft.client.resources.FolderResourcePack; +//$$ import java.io.ByteArrayInputStream; +//$$ import java.io.File; +//$$ import java.io.IOException; +//$$ import java.io.InputStream; +//$$ import java.nio.charset.StandardCharsets; +//#endif + +@Mod(modid = ReplayMod.MOD_ID, + useMetadata = true, + version = "@MOD_VERSION@", + acceptedMinecraftVersions = "@MC_VERSION@", + acceptableRemoteVersions = "*", + //#if MC>=10800 + clientSideOnly = true, + updateJSON = "https://raw.githubusercontent.com/ReplayMod/ReplayMod/master/versions.json", + //#endif + guiFactory = "com.replaymod.core.gui.GuiFactory") +public class ReplayModBackend { + private final ReplayMod mod = new ReplayMod(this); + private final EventsAdapter eventsAdapter = new EventsAdapter(); + + @Deprecated + public static Configuration config; + + @EventHandler + public void init(FMLPreInitializationEvent event) { + config = new Configuration(event.getSuggestedConfigurationFile()); + config.load(); + SettingsRegistry settingsRegistry = mod.getSettingsRegistry(); + settingsRegistry.backend.setConfiguration(config); + settingsRegistry.save(); // Save default values to disk + } + + @EventHandler + public void init(FMLInitializationEvent event) { + mod.initModules(); + eventsAdapter.register(); + } + + public String getVersion() { + return Loader.instance().getIndexedModList().get(MOD_ID).getVersion(); + } + + public String getMinecraftVersion() { + return Loader.MC_VERSION; + } + + public boolean isModLoaded(String id) { + return Loader.isModLoaded(id); + } + + static { // Note: even preInit is too late and we'd have to issue another resource reload + List defaultResourcePacks = ((MinecraftAccessor) getMinecraft()).getDefaultResourcePacks(); + + if (jGuiResourcePack != null) { + defaultResourcePacks.add(jGuiResourcePack); + } + + //#if MC<=10710 + //$$ FolderResourcePack mainResourcePack = new FolderResourcePack(new File("../src/main/resources")) { + //$$ @Override + //$$ protected InputStream getInputStreamByName(String resourceName) throws IOException { + //$$ try { + //$$ return super.getInputStreamByName(resourceName); + //$$ } catch (IOException e) { + //$$ if ("pack.mcmeta".equals(resourceName)) { + //$$ return new ByteArrayInputStream(("{\"pack\": {\"description\": \"dummy pack for mod resources in dev-env\", \"pack_format\": 1}}").getBytes(StandardCharsets.UTF_8)); + //$$ } + //$$ throw e; + //$$ } + //$$ } + //$$ }; + //$$ defaultResourcePacks.add(mainResourcePack); + //#endif + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/core/SettingsRegistryBackend.java b/versions/1.12.2/src/main/java/com/replaymod/core/SettingsRegistryBackend.java new file mode 100644 index 00000000..41fa3211 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/core/SettingsRegistryBackend.java @@ -0,0 +1,75 @@ +package com.replaymod.core; + +import net.minecraftforge.common.config.Configuration; +import net.minecraftforge.common.config.Property; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +class SettingsRegistryBackend { + private final Map, Object> settings; + + private static final Object NULL_OBJECT = new Object(); + private Configuration configuration; + + SettingsRegistryBackend(Map, Object> settings) { + this.settings = settings; + } + + public void register() { + } + + public void setConfiguration(Configuration configuration) { + this.configuration = configuration; + + for (SettingsRegistry.SettingKey key : new ArrayList<>(settings.keySet())) { + register(key); + } + } + + public void register(SettingsRegistry.SettingKey key) { + Object value; + if (configuration != null) { + if (key.getDefault() instanceof Boolean) { + value = configuration.get(key.getCategory(), key.getKey(), (Boolean) key.getDefault()).getBoolean(); + } else if (key.getDefault() instanceof Integer) { + value = configuration.get(key.getCategory(), key.getKey(), (Integer) key.getDefault()).getInt(); + } else if (key.getDefault() instanceof Double) { + value = configuration.get(key.getCategory(), key.getKey(), (Double) key.getDefault()).getDouble(); + } else if (key.getDefault() instanceof String) { + Property property = configuration.get(key.getCategory(), key.getKey(), (String) key.getDefault()); + value = property.getString(); + if (key instanceof SettingsRegistry.MultipleChoiceSettingKey) { + @SuppressWarnings("unchecked") + List choices = ((SettingsRegistry.MultipleChoiceSettingKey) key).getChoices(); + property.setValidValues(choices.toArray(new String[0])); + property.setComment("Valid values: " + String.join(", ", choices)); + } + } else { + throw new IllegalArgumentException("Default type " + key.getDefault().getClass() + " not supported."); + } + } else { + value = NULL_OBJECT; + } + settings.put(key, value); + } + + public void update(SettingsRegistry.SettingKey key, T value) { + if (key.getDefault() instanceof Boolean) { + configuration.get(key.getCategory(), key.getKey(), (Boolean) key.getDefault()).set((Boolean) value); + } else if (key.getDefault() instanceof Integer) { + configuration.get(key.getCategory(), key.getKey(), (Integer) key.getDefault()).set((Integer) value); + } else if (key.getDefault() instanceof Double) { + configuration.get(key.getCategory(), key.getKey(), (Double) key.getDefault()).set((Double) value); + } else if (key.getDefault() instanceof String) { + configuration.get(key.getCategory(), key.getKey(), (String) key.getDefault()).set((String) value); + } else { + throw new IllegalArgumentException("Default type " + key.getDefault().getClass() + " not supported."); + } + } + + public void save() { + configuration.save(); + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/core/gui/GuiFactory.java b/versions/1.12.2/src/main/java/com/replaymod/core/gui/GuiFactory.java new file mode 100644 index 00000000..e2ac1ed4 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/core/gui/GuiFactory.java @@ -0,0 +1,61 @@ +package com.replaymod.core.gui; + +import com.replaymod.core.ReplayMod; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; + +//#if MC>=10800 +import net.minecraftforge.fml.client.IModGuiFactory; +//#else +//$$ import cpw.mods.fml.client.IModGuiFactory; +//#endif + +import java.util.Set; + +@SuppressWarnings("unused") +public class GuiFactory implements IModGuiFactory { + @Override + public void initialize(Minecraft minecraftInstance) { + + } + + //#if MC>=11200 + @Override + public boolean hasConfigGui() { + return true; + } + + @Override + public GuiScreen createConfigGui(GuiScreen parentScreen) { + return new GuiReplaySettings(parentScreen, ReplayMod.instance.getSettingsRegistry()).toMinecraft(); + } + //#else + //$$ @Override + //$$ public Class mainConfigGuiClass() { + //$$ return ConfigGuiWrapper.class; + //$$ } + //$$ + //$$ @Override + //$$ public RuntimeOptionGuiHandler getHandlerFor(RuntimeOptionCategoryElement element) { + //$$ return null; + //$$ } + //$$ + //$$ public static class ConfigGuiWrapper extends GuiScreen { + //$$ private final GuiScreen parent; + //$$ + //$$ public ConfigGuiWrapper(GuiScreen parent) { + //$$ this.parent = parent; + //$$ } + //$$ + //$$ @Override + //$$ public void initGui() { + //$$ new GuiReplaySettings(parent, ReplayMod.instance.getSettingsRegistry()).display(); + //$$ } + //$$ } + //#endif + + @Override + public Set runtimeGuiCategories() { + return null; + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/core/mixin/MixinKeyboardListener.java b/versions/1.12.2/src/main/java/com/replaymod/core/mixin/MixinKeyboardListener.java new file mode 100644 index 00000000..2e29e2ed --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/core/mixin/MixinKeyboardListener.java @@ -0,0 +1,27 @@ +package com.replaymod.core.mixin; + +import com.replaymod.core.events.KeyBindingEventCallback; +import com.replaymod.core.events.KeyEventCallback; +import net.minecraft.client.settings.KeyBinding; +import org.lwjgl.input.Keyboard; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(KeyBinding.class) +public class MixinKeyboardListener { + @Inject(method = "onTick", at = @At("HEAD"), cancellable = true) + private static void beforeKeyBindingTick(CallbackInfo ci) { + int keyCode = Keyboard.getEventKey() == 0 ? Keyboard.getEventCharacter() + 256 : Keyboard.getEventKey(); + int action = Keyboard.getEventKeyState() ? KeyEventCallback.ACTION_PRESS : KeyEventCallback.ACTION_RELEASE; + if (KeyEventCallback.EVENT.invoker().onKeyEvent(keyCode, 0, action, 0)) { + ci.cancel(); + } + } + + @Inject(method = "onTick", at = @At("RETURN")) + private static void afterKeyBindingTick(CallbackInfo ci) { + KeyBindingEventCallback.EVENT.invoker().onKeybindingEvent(); + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/core/tweaker/ReplayModTweaker.java b/versions/1.12.2/src/main/java/com/replaymod/core/tweaker/ReplayModTweaker.java new file mode 100644 index 00000000..dc20dd20 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/core/tweaker/ReplayModTweaker.java @@ -0,0 +1,87 @@ +package com.replaymod.core.tweaker; + +import net.minecraft.launchwrapper.ITweaker; +import net.minecraft.launchwrapper.Launch; +import net.minecraft.launchwrapper.LaunchClassLoader; +import org.spongepowered.asm.launch.GlobalProperties; +import org.spongepowered.asm.launch.MixinBootstrap; +import org.spongepowered.asm.launch.platform.MixinPlatformManager; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.List; + +//#if MC>=11200 +import org.spongepowered.asm.launch.platform.container.ContainerHandleURI; +//#endif + +// Fabric equivalent is in ReplayModMMLauncher +public class ReplayModTweaker implements ITweaker { + + private static final String MIXIN_TWEAKER = "org.spongepowered.asm.launch.MixinTweaker"; + + public ReplayModTweaker() { + injectMixinTweaker(); + } + + private void injectMixinTweaker() { + @SuppressWarnings("unchecked") + List tweakClasses = (List) Launch.blackboard.get("TweakClasses"); + + // If the MixinTweaker is already queued (because of another mod), then there's nothing we need to to + if (tweakClasses.contains(MIXIN_TWEAKER)) { + return; + } + + // If it is already booted, we're also good to go + if (GlobalProperties.get(GlobalProperties.Keys.INIT) != null) { + return; + } + + System.out.println("Injecting MixinTweaker from ReplayModTweaker"); + + // Otherwise, we need to take things into our own hands because the normal way to chainload a tweaker + // (by adding it to the TweakClasses list during injectIntoClassLoader) is too late for Mixin. + // Instead we instantiate the MixinTweaker on our own and add it to the current Tweaks list immediately. + Launch.classLoader.addClassLoaderExclusion(MIXIN_TWEAKER.substring(0, MIXIN_TWEAKER.lastIndexOf('.'))); + @SuppressWarnings("unchecked") + List tweaks = (List) Launch.blackboard.get("Tweaks"); + try { + tweaks.add((ITweaker) Class.forName(MIXIN_TWEAKER, true, Launch.classLoader).newInstance()); + } catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + @Override + public void acceptOptions(List args, File gameDir, File assetsDir, String profile) { + } + + @Override + public void injectIntoClassLoader(LaunchClassLoader classLoader) { + // Add our jar as a Mixin container (cause normally Mixin detects those via the TweakClass manifest entry) + URI uri; + try { + uri = this.getClass().getProtectionDomain().getCodeSource().getLocation().toURI(); + } catch (URISyntaxException e) { + throw new RuntimeException(e); + } + MixinPlatformManager platform = MixinBootstrap.getPlatform(); + //#if MC>=11200 + platform.addContainer(new ContainerHandleURI(uri)); + //#else + //$$ platform.addContainer(uri); + //#endif + } + + @Override + public String getLaunchTarget() { + return null; + } + + @Override + public String[] getLaunchArguments() { + return new String[0]; + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/core/utils/ModInfoGetter.java b/versions/1.12.2/src/main/java/com/replaymod/core/utils/ModInfoGetter.java new file mode 100644 index 00000000..1ec326e3 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/core/utils/ModInfoGetter.java @@ -0,0 +1,29 @@ +package com.replaymod.core.utils; + +import com.replaymod.replaystudio.data.ModInfo; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.fml.common.Loader; +import net.minecraftforge.fml.common.ModContainer; +import net.minecraftforge.registries.ForgeRegistry; +import net.minecraftforge.registries.RegistryManager; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; + +class ModInfoGetter { + static Collection getInstalledNetworkMods() { + Map ignoreCaseMap = Loader.instance().getModList().stream() + .collect(Collectors.toMap(m -> m.getModId().toLowerCase(), Function.identity())); + return RegistryManager.ACTIVE.takeSnapshot(false).keySet().stream() + .map(RegistryManager.ACTIVE::getRegistry) + .map(ForgeRegistry::getKeys).flatMap(Set::stream) + .map(ResourceLocation::getResourceDomain).filter(s -> !s.equals("minecraft")).distinct() + .map(String::toLowerCase).map(ignoreCaseMap::get).filter(Objects::nonNull) + .map(mod -> new ModInfo(mod.getModId(), mod.getName(), mod.getVersion())) + .collect(Collectors.toList()); + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/core/versions/GLFW.java b/versions/1.12.2/src/main/java/com/replaymod/core/versions/GLFW.java new file mode 100644 index 00000000..0c30d254 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/core/versions/GLFW.java @@ -0,0 +1,9 @@ +package com.replaymod.core.versions; + +import org.lwjgl.opengl.Display; + +public class GLFW { + public static boolean glfwWindowShouldClose(@SuppressWarnings("unused") long handle) { + return Display.isCloseRequested(); + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/core/versions/Window.java b/versions/1.12.2/src/main/java/com/replaymod/core/versions/Window.java new file mode 100644 index 00000000..fdcadef3 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/core/versions/Window.java @@ -0,0 +1,69 @@ +package com.replaymod.core.versions; + +import com.replaymod.render.mixin.MainWindowAccessor; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ScaledResolution; +import org.lwjgl.opengl.Display; + +public class Window implements MainWindowAccessor { + + private final Minecraft mc; + private ScaledResolution scaledResolution; + + public Window(Minecraft mc) { + this.mc = mc; + } + + @Override + public int getFramebufferWidth() { + return mc.displayWidth; + } + + @Override + public void setFramebufferWidth(int value) { + mc.displayWidth = value; + } + + @Override + public int getFramebufferHeight() { + return mc.displayHeight; + } + + @Override + public void setFramebufferHeight(int value) { + mc.displayHeight = value; + } + + public long getHandle() { + return 0; + } + + public int getWidth() { + return Display.getWidth(); + } + + public int getHeight() { + return Display.getHeight(); + } + + private ScaledResolution scaledResolution() { + ScaledResolution scaledResolution = this.scaledResolution; + if (scaledResolution == null) { + //#if MC>=10809 + scaledResolution = new ScaledResolution(mc); + //#else + //$$ scaledResolution = new ScaledResolution(mc, mc.displayWidth, mc.displayHeight); + //#endif + this.scaledResolution = scaledResolution; + } + return scaledResolution; + } + + public int getScaledWidth() { + return scaledResolution().getScaledWidth(); + } + + public int getScaledHeight() { + return scaledResolution().getScaledHeight(); + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/core/versions/forge/EventsAdapter.java b/versions/1.12.2/src/main/java/com/replaymod/core/versions/forge/EventsAdapter.java new file mode 100644 index 00000000..4a567d0c --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/core/versions/forge/EventsAdapter.java @@ -0,0 +1,70 @@ +package com.replaymod.core.versions.forge; + +import com.replaymod.core.events.KeyBindingEventCallback; +import com.replaymod.core.events.PostRenderCallback; +import com.replaymod.core.events.PostRenderWorldCallback; +import com.replaymod.core.events.PreRenderCallback; +import com.replaymod.core.events.PreRenderHandCallback; +import com.replaymod.core.versions.MCVer; +import com.replaymod.replay.events.RenderHotbarCallback; +import com.replaymod.replay.events.RenderSpectatorCrosshairCallback; +import de.johni0702.minecraft.gui.utils.EventRegistrations; +import de.johni0702.minecraft.gui.versions.MatrixStack; +import net.minecraftforge.client.event.RenderGameOverlayEvent; +import net.minecraftforge.client.event.RenderHandEvent; +import net.minecraftforge.client.event.RenderWorldLastEvent; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.InputEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; + +public class EventsAdapter extends EventRegistrations { + @SubscribeEvent + public void onKeyEvent(InputEvent.KeyInputEvent event) { + KeyBindingEventCallback.EVENT.invoker().onKeybindingEvent(); + } + + @SubscribeEvent + public void onMouseInput(InputEvent.MouseInputEvent event) { + KeyBindingEventCallback.EVENT.invoker().onKeybindingEvent(); + } + + @SubscribeEvent + public void preRender(TickEvent.RenderTickEvent event) { + if (event.phase != TickEvent.Phase.START) return; + PreRenderCallback.EVENT.invoker().preRender(); + } + + @SubscribeEvent + public void postRender(TickEvent.RenderTickEvent event) { + if (event.phase != TickEvent.Phase.END) return; + PostRenderCallback.EVENT.invoker().postRender(); + } + + @SubscribeEvent + public void renderCameraPath(RenderWorldLastEvent event) { + PostRenderWorldCallback.EVENT.invoker().postRenderWorld(new MatrixStack()); + } + + @SubscribeEvent + public void oRenderHand(RenderHandEvent event) { + if (PreRenderHandCallback.EVENT.invoker().preRenderHand()) { + event.setCanceled(true); + } + } + + @SubscribeEvent + public void preRenderGameOverlay(RenderGameOverlayEvent.Pre event) { + Boolean result = null; + switch (event.getType()) { + case CROSSHAIRS: + result = RenderSpectatorCrosshairCallback.EVENT.invoker().shouldRenderSpectatorCrosshair(); + break; + case HOTBAR: + result = RenderHotbarCallback.EVENT.invoker().shouldRenderHotbar(); + break; + } + if (result == Boolean.FALSE) { + event.setCanceled(true); + } + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/core/versions/forge/Patterns.java b/versions/1.12.2/src/main/java/com/replaymod/core/versions/forge/Patterns.java new file mode 100644 index 00000000..0f8a6ff3 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/core/versions/forge/Patterns.java @@ -0,0 +1,57 @@ +package com.replaymod.core.versions.forge; + +import com.replaymod.gradle.remap.Pattern; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.entity.EntityLivingBase; +import net.minecraftforge.client.event.GuiScreenEvent; +import net.minecraftforge.client.event.RenderGameOverlayEvent; +import net.minecraftforge.client.event.RenderLivingEvent; +import net.minecraftforge.common.config.Property; + +class Patterns { + @Pattern + private static GuiButton getButton(GuiScreenEvent.ActionPerformedEvent event) { + //#if MC>=10904 + return event.getButton(); + //#else + //$$ return event.button; + //#endif + } + + @Pattern + private static GuiScreen getGui(GuiScreenEvent event) { + //#if MC>=10904 + return event.getGui(); + //#else + //$$ return event.gui; + //#endif + } + + @Pattern + private static EntityLivingBase getEntity(RenderLivingEvent event) { + //#if MC>=10904 + return event.getEntity(); + //#else + //$$ return event.entity; + //#endif + } + + @Pattern + private static RenderGameOverlayEvent.ElementType getType(RenderGameOverlayEvent event) { + //#if MC>=10904 + return event.getType(); + //#else + //$$ return event.type; + //#endif + } + + @Pattern + private static void setComment(Property property, String comment) { + //#if MC>=10900 + property.setComment(comment); + //#else + //$$ property.comment = comment; + //#endif + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/core/versions/scheduler/SchedulerImpl.java b/versions/1.12.2/src/main/java/com/replaymod/core/versions/scheduler/SchedulerImpl.java new file mode 100644 index 00000000..65444afc --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/core/versions/scheduler/SchedulerImpl.java @@ -0,0 +1,142 @@ +package com.replaymod.core.versions.scheduler; + +import com.google.common.util.concurrent.ListenableFutureTask; +import com.replaymod.core.mixin.MinecraftAccessor; +import net.minecraft.client.Minecraft; +import net.minecraft.util.ReportedException; +import net.minecraftforge.fml.common.eventhandler.EventBus; +import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; +import net.minecraftforge.fml.common.gameevent.TickEvent; + +import java.util.Queue; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +//#if MC>=10809 +import net.minecraftforge.common.MinecraftForge; +//#else +//$$ import net.minecraftforge.fml.common.FMLCommonHandler; +//#endif + +//#if MC<10800 +//$$ import java.util.ArrayDeque; +//#endif + +public class SchedulerImpl implements Scheduler { + private static final Minecraft mc = Minecraft.getMinecraft(); + + //#if MC>=10809 + private static final EventBus FML_BUS = MinecraftForge.EVENT_BUS; + //#else + //$$ private static final EventBus FML_BUS = FMLCommonHandler.instance().bus(); + //#endif + + @Override + public void runSync(Runnable runnable) throws InterruptedException, ExecutionException, TimeoutException { + if (mc.isCallingFromMinecraftThread()) { + runnable.run(); + } else { + FutureTask future = new FutureTask<>(runnable, null); + runLater(future); + future.get(30, TimeUnit.SECONDS); + } + } + + @Override + public void runPostStartup(Runnable runnable) { + runLater(runnable); + } + + /** + * Set when the currently running code has been scheduled by runLater. + * If this is the case, subsequent calls to runLater have to be delayed until all scheduled tasks have been + * processed, otherwise a livelock may occur. + */ + private boolean inRunLater = false; + + @Override + public void runLaterWithoutLock(Runnable runnable) { + runLater(() -> runLaterWithoutLock(runnable), runnable); + } + + public void runLater(Runnable runnable) { + runLater(runnable, () -> runLater(runnable)); + } + + private void runLater(Runnable runnable, Runnable defer) { + if (mc.isCallingFromMinecraftThread() && inRunLater) { + //#if MC>=10800 + FML_BUS.register(new Object() { + @SubscribeEvent + public void onRenderTick(TickEvent.RenderTickEvent event) { + if (event.phase == TickEvent.Phase.START) { + FML_BUS.unregister(this); + defer.run(); + } + } + }); + //#else + //$$ FML_BUS.register(new RunLaterHelper(defer)); + //#endif + return; + } + //#if MC>=10800 + Queue> tasks = ((MinecraftAccessor) mc).getScheduledTasks(); + //noinspection SynchronizationOnLocalVariableOrMethodParameter + synchronized (tasks) { + //#else + //$$ Queue> tasks = scheduledTasks; + //$$ synchronized (scheduledTasks) { + //#endif + tasks.add(ListenableFutureTask.create(() -> { + inRunLater = true; + try { + runnable.run(); + } catch (ReportedException e) { + e.printStackTrace(); + System.err.println(e.getCrashReport().getCompleteReport()); + mc.crashed(e.getCrashReport()); + } finally { + inRunLater = false; + } + }, null)); + } + } + + //#if MC>=10800 + @Override + public void runTasks() { + } + //#else + //$$ // 1.7.10: Cannot use MC's because it is processed only during ticks (so not at all when replay is paused) + //$$ private final Queue> scheduledTasks = new ArrayDeque<>(); + //$$ + //$$ // in 1.7.10 apparently events can't be delivered to anonymous classes + //$$ public class RunLaterHelper { + //$$ private final Runnable defer; + //$$ + //$$ private RunLaterHelper(Runnable defer) { + //$$ this.defer = defer; + //$$ } + //$$ + //$$ @SubscribeEvent + //$$ public void onRenderTick(TickEvent.RenderTickEvent event) { + //$$ if (event.phase == TickEvent.Phase.START) { + //$$ FML_BUS.unregister(this); + //$$ defer.run(); + //$$ } + //$$ } + //$$ } + //$$ + //$$ @Override + //$$ public void runTasks() { + //$$ synchronized (scheduledTasks) { + //$$ while (!scheduledTasks.isEmpty()) { + //$$ scheduledTasks.poll().run(); + //$$ } + //$$ } + //$$ } + //#endif +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/recording/handler/FMLHandshakeFilter.java b/versions/1.12.2/src/main/java/com/replaymod/recording/handler/FMLHandshakeFilter.java new file mode 100644 index 00000000..f53c5533 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/recording/handler/FMLHandshakeFilter.java @@ -0,0 +1,29 @@ +package com.replaymod.recording.handler; + +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.SimpleChannelInboundHandler; +import net.minecraftforge.fml.common.network.handshake.FMLHandshakeMessage; + +/** + * Filters out all handshake packets that were sent for recording but must + * not actually be handled. + * This handler is only present when connected to the integrated server as + * otherwise all packets must be handled. + * + * When in single player, the game state packets must never be handled + * otherwise wired bugs related to semi-singletons can occur. + * See https://bugs.replaymod.com/show_bug.cgi?id=44 + */ +public class FMLHandshakeFilter extends SimpleChannelInboundHandler { + @Override + protected void channelRead0(ChannelHandlerContext ctx, FMLHandshakeMessage msg) throws Exception { + //#if MC>=10800 + if (!(msg instanceof FMLHandshakeMessage.RegistryData)) { + //#else + //$$ if (!(msg instanceof FMLHandshakeMessage.ModIdData)) { + //#endif + // Pass on everything but RegistryData messages + ctx.fireChannelRead(msg); + } + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerLoginClient.java b/versions/1.12.2/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerLoginClient.java new file mode 100644 index 00000000..10c052e8 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/recording/mixin/MixinNetHandlerLoginClient.java @@ -0,0 +1,37 @@ +package com.replaymod.recording.mixin; + +import com.replaymod.recording.ReplayModRecording; +import net.minecraft.client.network.NetHandlerLoginClient; +import net.minecraft.network.NetworkManager; +import net.minecraftforge.fml.common.network.FMLNetworkEvent; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(NetHandlerLoginClient.class) +public abstract class MixinNetHandlerLoginClient { + + @Final @Shadow + private NetworkManager networkManager; + + /** + * Starts the recording right before switching into PLAY state. + * We cannot use the {@link FMLNetworkEvent.ClientConnectedToServerEvent} + * as it only fires after the forge handshake. + */ + @Inject(method = "handleLoginSuccess", at=@At("HEAD")) + public void replayModRecording_initiateRecording(CallbackInfo cb) { + ReplayModRecording.instance.initiateRecording(this.networkManager); + } + + // Race condition in Forge's networking (not sure if still present in 1.13) + //#if MC>=11200 && MC<11400 + @Inject(method = "handleLoginSuccess", at=@At("RETURN")) + public void replayModRecording_raceConditionWorkAround(CallbackInfo cb) { + ((NetworkManagerAccessor) this.networkManager).getChannel().config().setAutoRead(true); + } + //#endif +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/recording/mixin/MixinNetworkDispatcher.java b/versions/1.12.2/src/main/java/com/replaymod/recording/mixin/MixinNetworkDispatcher.java new file mode 100644 index 00000000..120dd947 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/recording/mixin/MixinNetworkDispatcher.java @@ -0,0 +1,69 @@ +package com.replaymod.recording.mixin; + +import com.replaymod.recording.handler.FMLHandshakeFilter; +import io.netty.channel.ChannelPipeline; +import io.netty.channel.embedded.EmbeddedChannel; +import net.minecraftforge.fml.common.network.handshake.FMLHandshakeCodec; +import net.minecraftforge.fml.common.network.handshake.NetworkDispatcher; +import net.minecraftforge.fml.relauncher.Side; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +//#if MC>=11200 +import io.netty.channel.ChannelConfig; +import net.minecraft.network.EnumConnectionState; +import net.minecraft.network.NetworkManager; +import org.spongepowered.asm.mixin.injection.Redirect; +//#endif + +@Mixin(value = NetworkDispatcher.class, remap = false) +public abstract class MixinNetworkDispatcher { + + @Final @Shadow + private Side side; + + @Final @Shadow + private EmbeddedChannel handshakeChannel; + + /** + * Always sets fml:isLocal to false on the server side. + * This effectively removes the difference in the FML handshake between SP and MP + * and forces the block/item ids, etc. to always be send. + * Injects a {@link FMLHandshakeFilter} on the client side to filter out + * those extra, unexpected packets. + */ + @Inject(method = "insertIntoChannel", at=@At("HEAD")) + public void replayModRecording_setupForLocalRecording(CallbackInfo cb) { + // If we're in multiplayer, everything is fine as is + if (!handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).get()) return; + + if (side == Side.SERVER) { + // On the server side, force all packets to be sent + handshakeChannel.attr(NetworkDispatcher.IS_LOCAL).set(false); + } else { + // On the client side, discard additional packets + ChannelPipeline pipeline = handshakeChannel.pipeline(); + pipeline.addAfter(pipeline.context(FMLHandshakeCodec.class).name(), + "replaymod_filter", new FMLHandshakeFilter()); + } + } + + //#if MC>=11200 + @Redirect(method = "clientListenForServerHandshake", at = @At(value = "INVOKE", remap = true, target = + "Lnet/minecraft/network/NetworkManager;setConnectionState(Lnet/minecraft/network/EnumConnectionState;)V")) + public void replayModRecording_raceConditionWorkAround1(NetworkManager self, EnumConnectionState ignored) { } + + @Redirect(method = "insertIntoChannel", at = @At(value = "INVOKE", target = + "Lio/netty/channel/ChannelConfig;setAutoRead(Z)Lio/netty/channel/ChannelConfig;")) + public ChannelConfig replayModRecording_raceConditionWorkAround2(ChannelConfig self, boolean autoRead) { + if (side == Side.CLIENT) { + autoRead = false; + } + return self.setAutoRead(autoRead); + } + //#endif +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/recording/mixin/MixinPlayerControllerMP.java b/versions/1.12.2/src/main/java/com/replaymod/recording/mixin/MixinPlayerControllerMP.java new file mode 100644 index 00000000..11e08b99 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/recording/mixin/MixinPlayerControllerMP.java @@ -0,0 +1,33 @@ +package com.replaymod.recording.mixin; + +//#if MC>=10904 && MC<11400 +import com.replaymod.recording.handler.RecordingEventHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.PlayerControllerMP; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +import static com.replaymod.core.versions.MCVer.*; + +@Mixin(PlayerControllerMP.class) +public abstract class MixinPlayerControllerMP implements RecordingEventHandler.RecordingEventSender { + + @Final @Shadow + private Minecraft mc; + + // Redirects the call to playEvent without the initial player argument to the method with that argument + // The new method will then play it and (if applicable) record it. (See MixinWorldClient) + // This is necessary for the block break event (particles and sound) to be recorded. Otherwise it looks like the + // event was emitted because of a packet (player will be null) and not as it actually was (by the player). + @Redirect(method = "onPlayerDestroyBlock", at = @At(value = "INVOKE", + target = "Lnet/minecraft/world/World;playEvent(ILnet/minecraft/util/math/BlockPos;I)V")) + public void replayModRecording_playEvent_fixed(World world, int type, BlockPos pos, int data) { + world.playEvent(mc.player, type, pos, data); + } +} +//#endif diff --git a/versions/1.12.2/src/main/java/com/replaymod/render/mixin/MainWindowAccessor.java b/versions/1.12.2/src/main/java/com/replaymod/render/mixin/MainWindowAccessor.java new file mode 100644 index 00000000..2d91cb58 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/render/mixin/MainWindowAccessor.java @@ -0,0 +1,17 @@ +package com.replaymod.render.mixin; + +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +@Mixin(Minecraft.class) +public interface MainWindowAccessor { + @Accessor("displayWidth") + int getFramebufferWidth(); + @Accessor("displayWidth") + void setFramebufferWidth(int value); + @Accessor("displayHeight") + int getFramebufferHeight(); + @Accessor("displayHeight") + void setFramebufferHeight(int value); +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/render/mixin/Mixin_ChromaKeyDisableFog.java b/versions/1.12.2/src/main/java/com/replaymod/render/mixin/Mixin_ChromaKeyDisableFog.java new file mode 100644 index 00000000..e3c1e00c --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/render/mixin/Mixin_ChromaKeyDisableFog.java @@ -0,0 +1,19 @@ +package com.replaymod.render.mixin; + +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.renderer.EntityRenderer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(EntityRenderer.class) +public abstract class Mixin_ChromaKeyDisableFog implements EntityRendererHandler.IEntityRenderer { + @Inject(method = "setupFog", at = @At("HEAD"), cancellable = true) + private void replayModRender_onSetupFog(int fogDistanceFlag, float partialTicks, CallbackInfo ci) { + EntityRendererHandler handler = replayModRender_getHandler(); + if (handler != null && handler.getSettings().getChromaKeyingColor() != null) { + ci.cancel(); + } + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_Camera.java b/versions/1.12.2/src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_Camera.java new file mode 100644 index 00000000..9f7d56de --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_Camera.java @@ -0,0 +1,40 @@ +package com.replaymod.render.mixin; + +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.renderer.EntityRenderer; +import org.lwjgl.util.glu.Project; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(EntityRenderer.class) +public abstract class Mixin_Omnidirectional_Camera implements EntityRendererHandler.IEntityRenderer { + @Redirect(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false)) + private void replayModRender_gluPerspective$0(float fovY, float aspect, float zNear, float zFar) { + replayModRender_gluPerspective(fovY, aspect, zNear, zFar); + } + + //#if MC>=10800 + @Redirect(method = "renderWorldPass", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false)) + //#else + //$$ @Redirect(method = "renderHand", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false)) + //#endif + private void replayModRender_gluPerspective$1(float fovY, float aspect, float zNear, float zFar) { + replayModRender_gluPerspective(fovY, aspect, zNear, zFar); + } + + //#if MC>=10800 + @Redirect(method = "renderCloudsCheck", at = @At(value = "INVOKE", target = "Lorg/lwjgl/util/glu/Project;gluPerspective(FFFF)V", remap = false)) + private void replayModRender_gluPerspective$2(float fovY, float aspect, float zNear, float zFar) { + replayModRender_gluPerspective(fovY, aspect, zNear, zFar); + } + //#endif + + private void replayModRender_gluPerspective(float fovY, float aspect, float zNear, float zFar) { + if (replayModRender_getHandler() != null && replayModRender_getHandler().omnidirectional) { + fovY = 90; + aspect = 1; + } + Project.gluPerspective(fovY, aspect, zNear, zFar); + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/render/mixin/Mixin_Stereoscopic_HandRenderPass.java b/versions/1.12.2/src/main/java/com/replaymod/render/mixin/Mixin_Stereoscopic_HandRenderPass.java new file mode 100644 index 00000000..5cf178a5 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/render/mixin/Mixin_Stereoscopic_HandRenderPass.java @@ -0,0 +1,29 @@ +package com.replaymod.render.mixin; + +import com.replaymod.render.capturer.StereoscopicOpenGlFrameCapturer; +import com.replaymod.render.hooks.EntityRendererHandler; +import com.replaymod.replay.camera.CameraEntity; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.EntityRenderer; +import net.minecraft.entity.Entity; +import net.minecraft.entity.player.EntityPlayer; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +@Mixin(EntityRenderer.class) +public abstract class Mixin_Stereoscopic_HandRenderPass implements EntityRendererHandler.IEntityRenderer { + @ModifyVariable(method = "renderHand", at = @At("HEAD"), argsOnly = true) + private int replayModRender_renderSpectatorHand(int renderPass) { + EntityRendererHandler handler = replayModRender_getHandler(); + if (handler != null) { + Entity currentEntity = Minecraft.getMinecraft().getRenderViewEntity(); + if (currentEntity instanceof EntityPlayer && !(currentEntity instanceof CameraEntity)) { + if (renderPass == 2) { // Need to update render pass + renderPass = handler.data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE ? 1 : 0; + } + } + } + return renderPass; + } +} diff --git a/versions/1.12.2/src/main/java/com/replaymod/replay/mixin/MixinRenderManager.java b/versions/1.12.2/src/main/java/com/replaymod/replay/mixin/MixinRenderManager.java new file mode 100644 index 00000000..38080714 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/replay/mixin/MixinRenderManager.java @@ -0,0 +1,30 @@ +// Appears to no longer be needed in 1.14+ (maybe not even 1.13?) +//#if MC>=10800 +package com.replaymod.replay.mixin; + +import net.minecraft.client.settings.GameSettings; +import net.minecraft.client.gui.FontRenderer; +import net.minecraft.client.renderer.entity.RenderManager; +import net.minecraft.entity.Entity; +import net.minecraft.entity.passive.EntityAnimal; +import net.minecraft.world.World; +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; + +@Mixin(RenderManager.class) +public class MixinRenderManager { + @Shadow + public float playerViewY; + + @Inject(method = "cacheActiveRenderInfo", at = @At("RETURN")) + public void fixHeadRotationForAnimals(World world, FontRenderer font, Entity view, Entity target, GameSettings settings, float partialRenderTick, CallbackInfo ci) { + if (view instanceof EntityAnimal && !((EntityAnimal) view).isPlayerSleeping()) { + EntityAnimal e = (EntityAnimal) view; + this.playerViewY = e.prevRotationYawHead + (e.rotationYawHead - e.prevRotationYawHead) * partialRenderTick; + } + } +} +//#endif diff --git a/versions/1.12.2/src/main/java/com/replaymod/replay/mixin/MixinWorldClient.java b/versions/1.12.2/src/main/java/com/replaymod/replay/mixin/MixinWorldClient.java new file mode 100644 index 00000000..643ac038 --- /dev/null +++ b/versions/1.12.2/src/main/java/com/replaymod/replay/mixin/MixinWorldClient.java @@ -0,0 +1,31 @@ +package com.replaymod.replay.mixin; + +import net.minecraft.client.multiplayer.WorldClient; +import net.minecraft.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(WorldClient.class) +public abstract class MixinWorldClient { + + /** + * Fixes a bug in vanilla Minecraft that leaves entities remaining in the entityList even after respawn. + * The entityList in WorldClient is a Set that assumes that the entity ID of its entities does not change. + * However in {@link WorldClient#addEntityToWorld(int, Entity)}, the entity passed in is first added to the + * set and subsequently its id is changed, leaving it stuck in he Set. That is the buggy method. + * This wouldn't be too much of a problem if the entity had the correct id to begin with, however the handler for + * the spawn player packet creates an EntityOtherPlayerMP which takes its id from a counter and then passes it to + * this method with the wrong id. + * This mixin fixes the id of the entity before it is added to the set instead of right after. + * The original id change right after is not changed, however it should not have any effect. + * @param entityId The id to be set for the entity + * @param entity The entity to be added + * @param ci Callback info + */ + @Inject(method = "addEntityToWorld", at=@At("HEAD")) + public void replayModReplay_fix_addEntityToWorld(int entityId, Entity entity, CallbackInfo ci) { + entity.setEntityId(entityId); + } +} diff --git a/versions/1.14.4-forge/mapping.txt b/versions/1.14.4-forge/mapping.txt index 029bf807..d57a46f3 100644 --- a/versions/1.14.4-forge/mapping.txt +++ b/versions/1.14.4-forge/mapping.txt @@ -4,6 +4,8 @@ net.minecraft.resources.AbstractResourcePack net.minecraft.client.resources.Abst net.minecraft.resources.FolderPack net.minecraft.client.resources.FolderResourcePack 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 +net.minecraft.client.settings.AbstractOption func_216728_a() setValueMax() net.minecraftforge.eventbus.api.SubscribeEvent net.minecraftforge.fml.common.eventhandler.SubscribeEvent net.minecraftforge.eventbus.api.EventPriority net.minecraftforge.fml.common.eventhandler.EventPriority net.minecraftforge.eventbus.api.Cancelable net.minecraftforge.fml.common.eventhandler.Cancelable @@ -12,6 +14,12 @@ net.minecraft.crash.ReportedException net.minecraft.util.ReportedException net.minecraftforge.eventbus.api.IEventBus net.minecraftforge.fml.common.eventhandler.EventBus net.minecraft.network.play.server.SPacketEntity.Move net.minecraft.network.play.server.SPacketEntity.S17PacketEntityLookMove net.minecraftforge.fml.network.NetworkRegistry net.minecraftforge.fml.common.network.NetworkRegistry +net.minecraft.client.particle.ParticleManager queue queueEntityFX +org.apache.maven.artifact.versioning.ComparableVersion net.minecraftforge.fml.common.versioning.ComparableVersion +org.lwjgl.glfw.GLFW com.replaymod.core.versions.GLFW +net.minecraft.client.MainWindow com.replaymod.core.versions.Window +net.minecraft.client.audio.SimpleSound net.minecraft.client.audio.PositionedSoundRecord +net.minecraft.client.gui.IngameGui net.minecraft.client.gui.GuiIngame net.minecraft.resources.FolderPack getInputStream() getInputStreamByName() net.minecraft.client.gui.GuiYesNoCallback confirmResult() confirmClicked() @@ -176,7 +184,6 @@ net.minecraft.client.gui.screen.Screen removed() onGuiClosed() net.minecraft.client.gui.screen.Screen renderBackground() drawDefaultBackground() net.minecraft.client.gui.screen.Screen renderDirtBackground() drawBackground() net.minecraft.client.gui.hud.ChatHud net.minecraft.client.gui.GuiNewChat -net.minecraft.client.gui.hud.InGameHud net.minecraft.client.gui.GuiIngame net.minecraft.client.GameSettings net.minecraft.client.settings.GameSettings net.minecraft.client.GameSettings debugEnabled showDebugInfo net.minecraft.client.GameSettings viewDistance renderDistanceChunks diff --git a/versions/1.14.4-forge/src/main/java/com/replaymod/core/ReplayModBackend.java b/versions/1.14.4-forge/src/main/java/com/replaymod/core/ReplayModBackend.java new file mode 100644 index 00000000..2ba2eab7 --- /dev/null +++ b/versions/1.14.4-forge/src/main/java/com/replaymod/core/ReplayModBackend.java @@ -0,0 +1,20 @@ +package com.replaymod.core; + +import net.minecraft.client.Minecraft; +import net.minecraftforge.fml.ModList; + +import static com.replaymod.core.ReplayMod.MOD_ID; + +public class ReplayModBackend { + public String getVersion() { + return ModList.get().getModContainerById(MOD_ID).get().getModInfo().getVersion().toString(); + } + + public String getMinecraftVersion() { + return Minecraft.getInstance().getMinecraftGame().getVersion().getName(); + } + + public boolean isModLoaded(String id) { + return ModList.get().isLoaded(id.toLowerCase()); + } +} diff --git a/versions/1.14.4-forge/src/main/java/com/replaymod/core/utils/ModInfoGetter.java b/versions/1.14.4-forge/src/main/java/com/replaymod/core/utils/ModInfoGetter.java new file mode 100644 index 00000000..7bb66d98 --- /dev/null +++ b/versions/1.14.4-forge/src/main/java/com/replaymod/core/utils/ModInfoGetter.java @@ -0,0 +1,11 @@ +package com.replaymod.core.utils; + +import com.replaymod.replaystudio.data.ModInfo; + +import java.util.Collection; + +class ModInfoGetter { + static Collection getInstalledNetworkMods() { + throw new UnsupportedOperationException(); + } +} diff --git a/versions/1.14.4/src/main/java/com/replaymod/render/hooks/ForceChunkLoadingHook.java b/versions/1.14.4/src/main/java/com/replaymod/render/hooks/ForceChunkLoadingHook.java new file mode 100644 index 00000000..181c7fc1 --- /dev/null +++ b/versions/1.14.4/src/main/java/com/replaymod/render/hooks/ForceChunkLoadingHook.java @@ -0,0 +1,131 @@ +package com.replaymod.render.hooks; + +import com.replaymod.render.mixin.ChunkRenderDispatcherAccessor; +import com.replaymod.render.mixin.WorldRendererAccessor; +import com.replaymod.render.utils.JailingQueue; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.client.render.chunk.BlockBufferBuilderStorage; +import net.minecraft.client.render.chunk.ChunkBuilder; +import net.minecraft.client.render.chunk.ChunkRenderTask; +import net.minecraft.client.render.chunk.ChunkRenderWorker; +import net.minecraft.client.render.chunk.ChunkRenderer; + +import java.util.Iterator; + +//#if MC>=10904 +import java.util.concurrent.PriorityBlockingQueue; +//#else +//$$ import java.util.concurrent.BlockingQueue; +//#endif + +import static com.replaymod.core.versions.MCVer.ChunkRenderWorkerAccessor; + +public class ForceChunkLoadingHook { + + private final WorldRenderer hooked; + + private ChunkBuilder renderDispatcher; + //#if MC>=11400 + private JailingQueue workerJailingQueue; + //#else + //$$ private JailingQueue workerJailingQueue; + //#endif + private ChunkRenderWorkerAccessor renderWorker; + private int frame; + + public ForceChunkLoadingHook(WorldRenderer renderGlobal) { + this.hooked = renderGlobal; + + setup(((WorldRendererAccessor) renderGlobal).getRenderDispatcher()); + IForceChunkLoading.from(renderGlobal).replayModRender_setHook(this); + } + + public void updateRenderDispatcher(ChunkBuilder renderDispatcher) { + if (this.renderDispatcher != null) { + workerJailingQueue.freeAll(); + this.renderDispatcher = null; + } + if (renderDispatcher != null) { + setup(renderDispatcher); + } + } + + private void setup(ChunkBuilder renderDispatcher) { + this.renderDispatcher = renderDispatcher; + this.renderWorker = (ChunkRenderWorkerAccessor) new ChunkRenderWorker(renderDispatcher, new BlockBufferBuilderStorage()); + ChunkRenderDispatcherAccessor renderDispatcherAcc = (ChunkRenderDispatcherAccessor) renderDispatcher; + + int workerThreads = renderDispatcherAcc.getListThreadedWorkers().size(); + //#if MC>=10904 + PriorityBlockingQueue queueChunkUpdates = renderDispatcherAcc.getQueueChunkUpdates(); + //#else + //$$ BlockingQueue queueChunkUpdates = renderDispatcherAcc.getQueueChunkUpdates(); + //#endif + workerJailingQueue = new JailingQueue<>(queueChunkUpdates); + renderDispatcherAcc.setQueueChunkUpdates(workerJailingQueue); + //#if MC>=10904 + ChunkRenderTask element = new ChunkRenderTask( + null, + null, + 0 + //#if MC>=11400 + , null + //#endif + ); + //#else + //$$ ChunkCompileTaskGenerator element = new ChunkCompileTaskGenerator(null, null); + //#endif + element.cancel(); + for (int i = 0; i < workerThreads; i++) { + queueChunkUpdates.add(element); + } + + // Temporary workaround for dead lock, will be replaced by a new (ShaderMod compatible) mechanism later + //noinspection StatementWithEmptyBody + while (renderDispatcher.runTasksSync(0)) {} + + workerJailingQueue.jail(workerThreads); + renderDispatcherAcc.setQueueChunkUpdates(queueChunkUpdates); + } + + public void updateChunks() { + while (renderDispatcher.runTasksSync(0)) { + ((WorldRendererAccessor) hooked).setDisplayListEntitiesDirty(true); + } + + //#if MC>=10904 + PriorityBlockingQueue queueChunkUpdates; + //#else + //$$ BlockingQueue queueChunkUpdates; + //#endif + queueChunkUpdates = ((ChunkRenderDispatcherAccessor) renderDispatcher).getQueueChunkUpdates(); + while (!queueChunkUpdates.isEmpty()) { + try { + renderWorker.doRunTask(queueChunkUpdates.poll()); + } catch (InterruptedException ignored) { } + } + + Iterator iterator = ((WorldRendererAccessor) hooked).getChunksToUpdate().iterator(); + while (iterator.hasNext()) { + ChunkRenderer renderchunk = iterator.next(); + + renderDispatcher.rebuildSync(renderchunk); + + //#if MC>=10904 + renderchunk.unscheduleRebuild(); + //#else + //$$ renderchunk.setNeedsUpdate(false); + //#endif + iterator.remove(); + } + } + + public int nextFrameId() { + return frame++; + } + + public void uninstall() { + workerJailingQueue.freeAll(); + IForceChunkLoading.from(hooked).replayModRender_setHook(null); + } +} diff --git a/versions/1.14.4/src/main/java/com/replaymod/render/mixin/ChunkRenderDispatcherAccessor.java b/versions/1.14.4/src/main/java/com/replaymod/render/mixin/ChunkRenderDispatcherAccessor.java new file mode 100644 index 00000000..2c62b138 --- /dev/null +++ b/versions/1.14.4/src/main/java/com/replaymod/render/mixin/ChunkRenderDispatcherAccessor.java @@ -0,0 +1,34 @@ +//#if MC>=10800 +package com.replaymod.render.mixin; + +import net.minecraft.client.render.chunk.ChunkBuilder; +import net.minecraft.client.render.chunk.ChunkRenderTask; +import net.minecraft.client.render.chunk.ChunkRenderWorker; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.List; +//#if MC>=10904 +import java.util.concurrent.PriorityBlockingQueue; +//#else +//$$ import java.util.concurrent.BlockingQueue; +//#endif + +@Mixin(ChunkBuilder.class) +public interface ChunkRenderDispatcherAccessor { + @Accessor("workers") + List getListThreadedWorkers(); + + //#if MC>=10904 + @Accessor("pendingChunks") + PriorityBlockingQueue getQueueChunkUpdates(); + @Accessor("pendingChunks") + void setQueueChunkUpdates(PriorityBlockingQueue value); + //#else + //$$ @Accessor + //$$ BlockingQueue getQueueChunkUpdates(); + //$$ @Accessor + //$$ void setQueueChunkUpdates(BlockingQueue value); + //#endif +} +//#endif diff --git a/versions/1.14.4/src/main/java/com/replaymod/render/mixin/MixinChunkRenderWorker.java b/versions/1.14.4/src/main/java/com/replaymod/render/mixin/MixinChunkRenderWorker.java new file mode 100644 index 00000000..6e4a2a00 --- /dev/null +++ b/versions/1.14.4/src/main/java/com/replaymod/render/mixin/MixinChunkRenderWorker.java @@ -0,0 +1,18 @@ +//#if MC>=10800 +package com.replaymod.render.mixin; + +import com.replaymod.core.versions.MCVer; +import net.minecraft.client.render.chunk.ChunkRenderTask; +import net.minecraft.client.render.chunk.ChunkRenderWorker; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(ChunkRenderWorker.class) +public abstract class MixinChunkRenderWorker implements MCVer.ChunkRenderWorkerAccessor { + @Shadow abstract void runTask(ChunkRenderTask task) throws InterruptedException; + + public void doRunTask(ChunkRenderTask task) throws InterruptedException { + runTask(task); + } +} +//#endif diff --git a/versions/1.14.4/src/main/java/com/replaymod/render/mixin/Mixin_ForceChunkLoading.java b/versions/1.14.4/src/main/java/com/replaymod/render/mixin/Mixin_ForceChunkLoading.java new file mode 100644 index 00000000..92120a32 --- /dev/null +++ b/versions/1.14.4/src/main/java/com/replaymod/render/mixin/Mixin_ForceChunkLoading.java @@ -0,0 +1,131 @@ +package com.replaymod.render.mixin; + +//#if MC>=10800 +import com.replaymod.compat.shaders.ShaderReflection; +import com.replaymod.render.hooks.ForceChunkLoadingHook; +import com.replaymod.render.hooks.IForceChunkLoading; +import net.minecraft.client.render.VisibleRegion; +import net.minecraft.client.render.WorldRenderer; +import net.minecraft.client.render.chunk.ChunkBuilder; +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; + +//#if MC>=11400 +import net.minecraft.client.render.Camera; +//#else +//$$ import net.minecraft.entity.Entity; +//#endif + +//#if MC<10904 +//$$ import net.minecraft.client.renderer.chunk.RenderChunk; +//$$ import net.minecraft.util.BlockPos; +//$$ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +//#endif + +@Mixin(WorldRenderer.class) +public abstract class Mixin_ForceChunkLoading implements IForceChunkLoading { + private ForceChunkLoadingHook replayModRender_hook; + + @Override + public void replayModRender_setHook(ForceChunkLoadingHook hook) { + this.replayModRender_hook = hook; + } + + private boolean replayModRender_passThroughSetupTerrain; + + @Shadow + private boolean needsTerrainUpdate; + + @Shadow + private ChunkBuilder chunkBuilder; + + @Shadow + public abstract void setUpTerrain( + //#if MC>=11400 + Camera viewEntity, + //#else + //$$ Entity viewEntity, + //$$ double partialTicks, + //#endif + VisibleRegion camera, + int frameCount, + boolean playerSpectator + ); + + @Inject(method = "setUpTerrain", at = @At("HEAD"), cancellable = true) + private void replayModRender_setupTerrain( + //#if MC>=11400 + Camera viewEntity, + //#else + //$$ Entity viewEntity, + //$$ double partialTicks, + //#endif + VisibleRegion camera, + int frameCount, + boolean playerSpectator, + CallbackInfo ci + ) throws IllegalAccessException { + if (ShaderReflection.shaders_isShadowPass != null && (boolean) ShaderReflection.shaders_isShadowPass.get(null)) { + return; + } + if (replayModRender_hook != null && !replayModRender_passThroughSetupTerrain) { + replayModRender_passThroughSetupTerrain = true; + + do { + setUpTerrain( + viewEntity, + //#if MC<11400 + //$$ partialTicks, + //#endif + camera, + replayModRender_hook.nextFrameId(), + playerSpectator + ); + replayModRender_hook.updateChunks(); + } while (this.needsTerrainUpdate); + + this.needsTerrainUpdate = true; + + replayModRender_passThroughSetupTerrain = false; + ci.cancel(); + } + } + + //#if MC<10904 + //$$ @Inject(method = "isPositionInRenderChunk", at = @At("HEAD"), cancellable = true) + //$$ public void replayModRender_isPositionInRenderChunk(BlockPos pos, RenderChunk chunk, CallbackInfoReturnable ci) { + //$$ if (replayModRender_hook != null) { + //$$ ci.setReturnValue(true); + //$$ } + //$$ } + //#endif + + @Inject(method = "updateChunks", at = @At("HEAD"), cancellable = true) + public void replayModRender_updateChunks(long finishTimeNano, CallbackInfo ci) { + if (replayModRender_hook != null) { + replayModRender_hook.updateChunks(); + ci.cancel(); + } + } + + // Prior to 1.9.4, MC always uses the same ChunkRenderDispatcher instance + //#if MC>=10904 + @Inject(method = "setWorld", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/render/chunk/ChunkBuilder;stop()V")) + private void stopWorkerThreadsAndChunkLoadingRenderGlobal(CallbackInfo ci) { + if (replayModRender_hook != null) { + replayModRender_hook.updateRenderDispatcher(null); + } + } + //#endif + + @Inject(method = "reload", at = @At(value = "RETURN")) + private void setupChunkLoadingRenderGlobal(CallbackInfo ci) { + if (replayModRender_hook != null) { + replayModRender_hook.updateRenderDispatcher(this.chunkBuilder); + } + } +} +//#endif diff --git a/versions/1.14.4/src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_Camera.java b/versions/1.14.4/src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_Camera.java new file mode 100644 index 00000000..6a0dd1e4 --- /dev/null +++ b/versions/1.14.4/src/main/java/com/replaymod/render/mixin/Mixin_Omnidirectional_Camera.java @@ -0,0 +1,34 @@ +package com.replaymod.render.mixin; + +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.render.GameRenderer; +import net.minecraft.client.util.math.Matrix4f; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Mixin(GameRenderer.class) +public abstract class Mixin_Omnidirectional_Camera implements EntityRendererHandler.IEntityRenderer { + @Redirect(method = "applyCameraTransformations", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/math/Matrix4f;method_4929(DFFF)Lnet/minecraft/client/util/math/Matrix4f;")) + private Matrix4f replayModRender_perspective$0(double fovY, float aspect, float zNear, float zFar) { + return replayModRender_perspective((float) fovY, aspect, zNear, zFar); + } + + @Redirect(method = "renderCenter", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/math/Matrix4f;method_4929(DFFF)Lnet/minecraft/client/util/math/Matrix4f;")) + private Matrix4f replayModRender_perspective$1(double fovY, float aspect, float zNear, float zFar) { + return replayModRender_perspective((float) fovY, aspect, zNear, zFar); + } + + @Redirect(method = "renderAboveClouds", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/util/math/Matrix4f;method_4929(DFFF)Lnet/minecraft/client/util/math/Matrix4f;")) + private Matrix4f replayModRender_perspective$2(double fovY, float aspect, float zNear, float zFar) { + return replayModRender_perspective((float) fovY, aspect, zNear, zFar); + } + + private Matrix4f replayModRender_perspective(float fovY, float aspect, float zNear, float zFar) { + if (replayModRender_getHandler() != null && replayModRender_getHandler().omnidirectional) { + fovY = 90; + aspect = 1; + } + return Matrix4f.method_4929(fovY, aspect, zNear, zFar); + } +} diff --git a/versions/1.14.4/src/main/java/com/replaymod/render/mixin/Mixin_Stereoscopic_Camera.java b/versions/1.14.4/src/main/java/com/replaymod/render/mixin/Mixin_Stereoscopic_Camera.java new file mode 100644 index 00000000..400e80e7 --- /dev/null +++ b/versions/1.14.4/src/main/java/com/replaymod/render/mixin/Mixin_Stereoscopic_Camera.java @@ -0,0 +1,43 @@ +package com.replaymod.render.mixin; + +import com.replaymod.render.capturer.StereoscopicOpenGlFrameCapturer; +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.render.GameRenderer; +import org.lwjgl.opengl.GL11; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(GameRenderer.class) +public abstract class Mixin_Stereoscopic_Camera implements EntityRendererHandler.IEntityRenderer { + //#if MC>=10800 + @Inject(method = "applyCameraTransformations", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/GlStateManager;loadIdentity()V", shift = At.Shift.AFTER, ordinal = 0)) + //#else + //$$ @Inject(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glLoadIdentity()V", shift = At.Shift.AFTER, ordinal = 0, remap = false)) + //#endif + private void replayModRender_setupStereoscopicProjection(CallbackInfo ci) { + if (replayModRender_getHandler() != null) { + if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE) { + GL11.glTranslatef(0.07f, 0, 0); + } else if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.RIGHT_EYE) { + GL11.glTranslatef(-0.07f, 0, 0); + } + } + } + + //#if MC>=10800 + @Inject(method = "applyCameraTransformations", at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/GlStateManager;loadIdentity()V", shift = At.Shift.AFTER, ordinal = 1)) + //#else + //$$ @Inject(method = "setupCameraTransform", at = @At(value = "INVOKE", target = "Lorg/lwjgl/opengl/GL11;glLoadIdentity()V", shift = At.Shift.AFTER, ordinal = 1, remap = false)) + //#endif + private void replayModRender_setupStereoscopicModelView(CallbackInfo ci) { + if (replayModRender_getHandler() != null) { + if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.LEFT_EYE) { + GL11.glTranslatef(0.1f, 0, 0); + } else if (replayModRender_getHandler().data == StereoscopicOpenGlFrameCapturer.Data.RIGHT_EYE) { + GL11.glTranslatef(-0.1f, 0, 0); + } + } + } +} diff --git a/versions/1.14.4/src/main/java/com/replaymod/replay/mixin/MixinCamera.java b/versions/1.14.4/src/main/java/com/replaymod/replay/mixin/MixinCamera.java new file mode 100644 index 00000000..a0c0d58a --- /dev/null +++ b/versions/1.14.4/src/main/java/com/replaymod/replay/mixin/MixinCamera.java @@ -0,0 +1,30 @@ +//#if MC>=11400 +package com.replaymod.replay.mixin; + +import com.mojang.blaze3d.platform.GlStateManager; +import com.replaymod.replay.camera.CameraEntity; +import net.minecraft.client.render.Camera; +import net.minecraft.entity.Entity; +import net.minecraft.world.BlockView; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(Camera.class) +public class MixinCamera { + @Inject( + method = "update", + at = @At( + value = "INVOKE", + target = "Lcom/mojang/blaze3d/platform/GlStateManager;rotatef(FFFF)V", + ordinal = 0 + ) + ) + private void applyRoll(BlockView blockView, Entity view, boolean boolean_1, boolean boolean_2, float float_1, CallbackInfo ci) { + if (view instanceof CameraEntity) { + GlStateManager.rotatef(((CameraEntity) view).roll, 0.0F, 0.0F, 1.0F); + } + } +} +//#endif diff --git a/versions/1.17.1/.gitkeep b/versions/1.17.1/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/versions/1.17/.gitkeep b/versions/1.17/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/versions/1.17/src/main/java/com/replaymod/render/mixin/MixinGlStateManager.java b/versions/1.17/src/main/java/com/replaymod/render/mixin/MixinGlStateManager.java new file mode 100644 index 00000000..135f5079 --- /dev/null +++ b/versions/1.17/src/main/java/com/replaymod/render/mixin/MixinGlStateManager.java @@ -0,0 +1 @@ +// 1.16.5 and below diff --git a/versions/1.17/src/main/java/com/replaymod/replay/mixin/MixinTileEntityEndPortalRenderer.java b/versions/1.17/src/main/java/com/replaymod/replay/mixin/MixinTileEntityEndPortalRenderer.java new file mode 100644 index 00000000..ded0c4de --- /dev/null +++ b/versions/1.17/src/main/java/com/replaymod/replay/mixin/MixinTileEntityEndPortalRenderer.java @@ -0,0 +1,9 @@ +package com.replaymod.replay.mixin; + +import com.mojang.blaze3d.systems.RenderSystem; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(RenderSystem.class) +public class MixinTileEntityEndPortalRenderer { + // All shaders now use the game time +} diff --git a/versions/1.7.10/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaders.java b/versions/1.7.10/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaders.java new file mode 100644 index 00000000..cec9d0a5 --- /dev/null +++ b/versions/1.7.10/src/main/java/com/replaymod/compat/shaders/mixin/MixinShaders.java @@ -0,0 +1,52 @@ +//#if MC<=10710 +package com.replaymod.compat.shaders.mixin; + +import com.replaymod.render.hooks.EntityRendererHandler; +import com.replaymod.replay.ReplayHandler; +import com.replaymod.replay.ReplayModReplay; +import net.minecraft.client.Minecraft; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Pseudo; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +@Pseudo +@Mixin(targets = "shadersmod/client/Shaders", remap = false) +public abstract class MixinShaders { + + @Shadow static Minecraft mc; + @Shadow static long systemTime; + @Shadow static long lastSystemTime; + @Shadow static long diffSystemTime; + @Shadow static int frameCounter; + @Shadow static float frameTime; + @Shadow static float frameTimeCounter; + + @Redirect(method = "beginRender", at = @At(value = "INVOKE", target = "Ljava/lang/System;currentTimeMillis()J")) + private static long replayModCompat_currentTimeMillis() { + ReplayHandler replayHandler = ReplayModReplay.instance.getReplayHandler(); + if (replayHandler != null) { + systemTime = replayHandler.getReplaySender().currentTimeStamp(); + + // We need to manipulate all the other previous-frame-time-based variables too + + lastSystemTime = 0; // Draw all frames as if they were the first ones + // diffSystemTime will be set to 0 by Shaders + // frameTime will be set to 0 by Shaders + frameTimeCounter = systemTime / 1000f; // will be %= 3600f by Shaders + + // Set frameCounter only if rendering is in progress + EntityRendererHandler entityRendererHandler = + ((EntityRendererHandler.IEntityRenderer) mc.entityRenderer).replayModRender_getHandler(); + if (entityRendererHandler != null) { + frameCounter = entityRendererHandler.getRenderInfo().getFramesDone(); + } + + return systemTime; + } + return System.currentTimeMillis(); + } + +} +//#endif diff --git a/versions/1.7.10/src/main/java/com/replaymod/core/asm/GLErrorTransformer.java b/versions/1.7.10/src/main/java/com/replaymod/core/asm/GLErrorTransformer.java new file mode 100644 index 00000000..06098167 --- /dev/null +++ b/versions/1.7.10/src/main/java/com/replaymod/core/asm/GLErrorTransformer.java @@ -0,0 +1,93 @@ +package com.replaymod.core.asm; + +import net.minecraft.launchwrapper.IClassTransformer; +import org.lwjgl.opengl.GL11; +import org.lwjgl.util.glu.GLU; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +/** + * Insert glGetError checks after all calls to any method in any GL*, ARB* and EXT* classes + */ +public class GLErrorTransformer implements IClassTransformer { + private static final String GL_CLASS = "org.lwjgl.opengl.GL"; + private static final String GL = GL_CLASS.replace('.', '/'); + private static final String ARB_CLASS = "org.lwjgl.opengl.ARB"; + private static final String ARB = ARB_CLASS.replace('.', '/'); + private static final String EXT_CLASS = "org.lwjgl.opengl.EXT"; + private static final String EXT = EXT_CLASS.replace('.', '/'); + private static final String GLErrorTransformer_CLASS = GLErrorTransformer.class.getName(); + private static final String GLErrorTransformer = GLErrorTransformer_CLASS.replace('.', '/'); + + @Override + public byte[] transform(String name, String transformedName, byte[] basicClass) { + if (basicClass == null) return null; + + ClassReader reader = new ClassReader(basicClass); + ClassWriter writer = new ClassWriter(reader, 0); + reader.accept(new ClassVisitor(Opcodes.ASM5, writer) { + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + return new MethodVisitor(Opcodes.ASM5, mv) { + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + super.visitMethodInsn(opcode, owner, name, desc, itf); + if (owner.startsWith(GL) || owner.startsWith(ARB) || owner.startsWith(EXT)) { + visitLdcInsn(owner.replace('/', '.')); + visitLdcInsn(name); + super.visitMethodInsn(Opcodes.INVOKESTATIC, GLErrorTransformer, "glErrorCheck", "(Ljava/lang/String;Ljava/lang/String;)V", false); + } + } + + @Override + public void visitMaxs(int maxStack, int maxLocals) { + super.visitMaxs(maxStack + 2, maxLocals); + } + }; + } + }, 0); + return writer.toByteArray(); + } + + private static boolean inGlBegin; + + @SuppressWarnings("unused") // Called via ASM + public static void glErrorCheck(String cls, String method) { + if (method.equals("glBegin") && cls.equals(GL_CLASS + "11")) { + if (inGlBegin) { + // glBegin within glBegin + throw new GLError(GL11.GL_INVALID_OPERATION, cls, method); + } + inGlBegin = true; + return; + } + if (inGlBegin && method.equals("glEnd") && cls.equals(GL_CLASS + "11")) { + inGlBegin = false; + } + if (inGlBegin) { + return; // Cannot call glGetError in between of glBegin and glEnd + } + int err = GL11.glGetError(); + if (err != 0) { + GLError e = new GLError(err, cls, method); + if ("true".equals(System.getProperty("replaymod.glerrors.throw", "true"))) { + throw e; + } else { + e.printStackTrace(); + } + } + } + + public static class GLError extends RuntimeException { + private GLError(int err, String cls, String method) { + super(GLU.gluErrorString(err) + " (" + err + ")"); + StackTraceElement[] stack = getStackTrace(); + stack[0] = new StackTraceElement(cls, method, stack[0].getFileName(), stack[0].getLineNumber()); + setStackTrace(stack); + } + } +} diff --git a/versions/1.7.10/src/main/java/com/replaymod/core/asm/GLStateTrackerTransformer.java b/versions/1.7.10/src/main/java/com/replaymod/core/asm/GLStateTrackerTransformer.java new file mode 100644 index 00000000..641cf104 --- /dev/null +++ b/versions/1.7.10/src/main/java/com/replaymod/core/asm/GLStateTrackerTransformer.java @@ -0,0 +1,104 @@ +package com.replaymod.core.asm; + +import net.minecraft.launchwrapper.IClassTransformer; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.ClassWriter; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; + +import java.util.HashSet; +import java.util.Set; + +/** + * Redirect all calls to GL11.glEnable and GL11.glDisable to the GLStateTracker (excluding that class itself). + */ +public class GLStateTrackerTransformer implements IClassTransformer { + private static final String GL11_CLASS = "org.lwjgl.opengl.GL11"; + private static final String GL11 = GL11_CLASS.replace('.', '/'); + private static final String glEnable = "glEnable"; + private static final String glDisable = "glDisable"; + private static final String GLStateTracker_CLASS = "com.replaymod.render.hooks.GLStateTracker"; + private static final String GLStateTracker = GLStateTracker_CLASS.replace('.', '/'); + private static final String hook_glEnable = "hook_glEnable"; + private static final String hook_glDisable = "hook_glDisable"; + + @Override + public byte[] transform(String name, String transformedName, byte[] basicClass) { + if (basicClass == null) return null; + // Ignore the state tracker itself + if (name.equals(GLStateTracker_CLASS)) return basicClass; + + ClassReader reader = new ClassReader(basicClass); + Set eligibleMethods = findEligibleMethods(reader); + if (eligibleMethods.isEmpty()) { + return basicClass; + } + + ClassWriter writer = new ClassWriter(reader, 0); + reader.accept(new ClassVisitor(Opcodes.ASM5, writer) { + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + MethodVisitor mv = super.visitMethod(access, name, desc, signature, exceptions); + if (!eligibleMethods.contains(new Method(name, desc))) { + return mv; + } + return new MethodVisitor(Opcodes.ASM5, mv) { + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + if (owner.equals(GL11)) { + if (name.equals(glEnable)) { + owner = GLStateTracker; + name = hook_glEnable; + } else if (name.equals(glDisable)) { + owner = GLStateTracker; + name = hook_glDisable; + } + } + super.visitMethodInsn(opcode, owner, name, desc, itf); + } + }; + } + }, 0); + return writer.toByteArray(); + } + + private Set findEligibleMethods(ClassReader reader) { + Set eligibleMethods = new HashSet<>(); + reader.accept(new ClassVisitor(Opcodes.ASM5) { + @Override + public MethodVisitor visitMethod(int access, String methodName, String methodDesc, String signature, String[] exceptions) { + return new MethodVisitor(Opcodes.ASM5) { + @Override + public void visitMethodInsn(int opcode, String owner, String name, String desc, boolean itf) { + if (owner.equals(GL11) && (name.equals(glEnable) || name.equals(glDisable))) { + eligibleMethods.add(new Method(methodName, methodDesc)); + } + } + }; + } + }, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES); + return eligibleMethods; + } + + private static class Method { + private final String name, desc; + + private Method(String name, String desc) { + this.name = name; + this.desc = desc; + } + + @Override + public boolean equals(Object o) { + if (o == null || getClass() != o.getClass()) return false; + Method method = (Method) o; + return name.equals(method.name) && desc.equals(method.desc); + } + + @Override + public int hashCode() { + return name.hashCode() ^ desc.hashCode(); + } + } +} diff --git a/versions/1.7.10/src/main/java/com/replaymod/core/mixin/ResourcePackRepositoryAccessor.java b/versions/1.7.10/src/main/java/com/replaymod/core/mixin/ResourcePackRepositoryAccessor.java new file mode 100644 index 00000000..d9c16a24 --- /dev/null +++ b/versions/1.7.10/src/main/java/com/replaymod/core/mixin/ResourcePackRepositoryAccessor.java @@ -0,0 +1,20 @@ +package com.replaymod.core.mixin; + +import net.minecraft.client.resources.IResourcePack; +import net.minecraft.client.resources.ResourcePackRepository; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.io.File; + +@Mixin(ResourcePackRepository.class) +public interface ResourcePackRepositoryAccessor { + @Accessor("field_148533_g") + boolean isActive(); + @Accessor("field_148533_g") + void setActive(boolean value); + @Accessor("field_148532_f") + void setPack(IResourcePack value); + @Accessor("field_148534_e") + File getCacheDir(); +} diff --git a/versions/1.7.10/src/main/java/com/replaymod/core/utils/ModInfoGetter.java b/versions/1.7.10/src/main/java/com/replaymod/core/utils/ModInfoGetter.java new file mode 100644 index 00000000..c8b8e842 --- /dev/null +++ b/versions/1.7.10/src/main/java/com/replaymod/core/utils/ModInfoGetter.java @@ -0,0 +1,32 @@ +package com.replaymod.core.utils; + +import com.replaymod.replaystudio.data.ModInfo; +import cpw.mods.fml.common.Loader; +import cpw.mods.fml.common.ModContainer; +import cpw.mods.fml.common.registry.GameData; + +import java.util.Collection; +import java.util.Map; +import java.util.Objects; +import java.util.Set; +import java.util.function.Function; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +class ModInfoGetter { + static Collection getInstalledNetworkMods() { + Map ignoreCaseMap = Loader.instance().getModList().stream() + .collect(Collectors.toMap(m -> m.getModId().toLowerCase(), Function.identity())); + return Stream.concat( + ((Set) GameData.getBlockRegistry().getKeys()).stream(), + ((Set) GameData.getItemRegistry().getKeys()).stream() + ).map(name -> { + if (!name.contains(":")) return null; // Still using old names without namespace, can't do anything, ignore + return name.split(":", 2)[0]; + }).filter(Objects::nonNull) + .filter(s -> !s.equals("minecraft")).distinct() + .map(String::toLowerCase).map(ignoreCaseMap::get).filter(Objects::nonNull) + .map(mod -> new ModInfo(mod.getModId(), mod.getName(), mod.getVersion())) + .collect(Collectors.toList()); + } +} diff --git a/versions/1.7.10/src/main/java/com/replaymod/core/versions/BufferBuilder.java b/versions/1.7.10/src/main/java/com/replaymod/core/versions/BufferBuilder.java new file mode 100644 index 00000000..73628626 --- /dev/null +++ b/versions/1.7.10/src/main/java/com/replaymod/core/versions/BufferBuilder.java @@ -0,0 +1,30 @@ +package com.replaymod.core.versions; + +import net.minecraft.client.renderer.Tessellator; + +public class BufferBuilder { + + private final Tessellator inner; + + public BufferBuilder(Tessellator inner) { + this.inner = inner; + } + + public void startDrawing(int glQuads) { + inner.startDrawing(glQuads); + } + + public void addVertexWithUV(double x, double y, double z, float u, float v) { + inner.addVertexWithUV(x, y, z, u, v); + } + + public void setColorRGBA(int r, int g, int b, int a) { + inner.setColorRGBA(r, g, b, a); + } + + public void addVertex(double x, double y, double z) { + inner.addVertex(x, y, z); + } + + public static class VertexFormats {} +} diff --git a/versions/1.7.10/src/main/java/com/replaymod/recording/mixin/MixinGuiScreen.java b/versions/1.7.10/src/main/java/com/replaymod/recording/mixin/MixinGuiScreen.java new file mode 100644 index 00000000..1f795d7c --- /dev/null +++ b/versions/1.7.10/src/main/java/com/replaymod/recording/mixin/MixinGuiScreen.java @@ -0,0 +1,42 @@ +package com.replaymod.recording.mixin; + +import de.johni0702.minecraft.gui.container.VanillaGuiScreen; +import net.minecraft.client.gui.GuiScreen; +import net.minecraftforge.common.MinecraftForge; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +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; + +@Mixin(GuiScreen.class) +public abstract class MixinGuiScreen { + + @Inject(method = "handleInput", at = @At("HEAD"), cancellable = true) + private void handleJGuiInput(CallbackInfo ci) { + ci.cancel(); + if (Mouse.isCreated()) { + while (Mouse.next()) { + if (MinecraftForge.EVENT_BUS.post(new VanillaGuiScreen.MouseInputEvent())) { + continue; + } + handleMouseInput(); + } + } + + if (Keyboard.isCreated()) { + while (Keyboard.next()) { + if (MinecraftForge.EVENT_BUS.post(new VanillaGuiScreen.KeyboardInputEvent())) { + continue; + } + handleKeyboardInput(); + } + } + } + + @Shadow public abstract void handleMouseInput(); + + @Shadow public abstract void handleKeyboardInput(); +} diff --git a/versions/1.7.10/src/main/java/com/replaymod/recording/mixin/MixinS26PacketMapChunkBulk.java b/versions/1.7.10/src/main/java/com/replaymod/recording/mixin/MixinS26PacketMapChunkBulk.java new file mode 100644 index 00000000..b7c64009 --- /dev/null +++ b/versions/1.7.10/src/main/java/com/replaymod/recording/mixin/MixinS26PacketMapChunkBulk.java @@ -0,0 +1,36 @@ +package com.replaymod.recording.mixin; + +import net.minecraft.network.PacketBuffer; +import net.minecraft.network.play.server.S21PacketChunkData; +import net.minecraft.network.play.server.S26PacketMapChunkBulk; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +import java.util.concurrent.Semaphore; + +@Mixin({ S21PacketChunkData.class, S26PacketMapChunkBulk.class }) +public abstract class MixinS26PacketMapChunkBulk { + + private byte[] rawData; + + @Shadow(remap = false) + private Semaphore deflateGate; + + @Inject(method = "readPacketData", at = @At("HEAD")) + private void replayModRecording_readRawPacketData(PacketBuffer data, CallbackInfo ci) { + int readerIndex = data.readerIndex(); + data.readBytes(rawData = new byte[data.readableBytes()]); + data.readerIndex(readerIndex); + } + + @Inject(method = "writePacketData", at = @At("HEAD"), cancellable = true) + private void replayModRecording_writePacketData(PacketBuffer data, CallbackInfo ci) { + if (rawData != null && deflateGate == null) { + data.writeBytes(rawData); + ci.cancel(); + } + } +} diff --git a/versions/1.7.10/src/main/java/com/replaymod/render/hooks/ForceChunkLoadingHook.java b/versions/1.7.10/src/main/java/com/replaymod/render/hooks/ForceChunkLoadingHook.java new file mode 100644 index 00000000..eb4134eb --- /dev/null +++ b/versions/1.7.10/src/main/java/com/replaymod/render/hooks/ForceChunkLoadingHook.java @@ -0,0 +1,17 @@ +package com.replaymod.render.hooks; + +import net.minecraft.client.renderer.RenderGlobal; + +public class ForceChunkLoadingHook { + private final RenderGlobal hooked; + + public ForceChunkLoadingHook(RenderGlobal renderGlobal) { + this.hooked = renderGlobal; + + IForceChunkLoading.from(renderGlobal).replayModRender_setHook(this); + } + + public void uninstall() { + IForceChunkLoading.from(hooked).replayModRender_setHook(null); + } +} diff --git a/versions/1.7.10/src/main/java/com/replaymod/render/hooks/GLStateTracker.java b/versions/1.7.10/src/main/java/com/replaymod/render/hooks/GLStateTracker.java new file mode 100644 index 00000000..54c06ce4 --- /dev/null +++ b/versions/1.7.10/src/main/java/com/replaymod/render/hooks/GLStateTracker.java @@ -0,0 +1,44 @@ +package com.replaymod.render.hooks; + +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL13; + +/** + * Tracks OpenGL state for use in the ODS shader. + * All methods should be called from the GL mixins only. + */ +public class GLStateTracker { + private static ThreadLocal stateTracker = ThreadLocal.withInitial(GLStateTracker::new); + public static GLStateTracker getInstance() { + return stateTracker.get(); + } + + public int activeTexture; + + public void updateActiveTexture(int magic) { + this.activeTexture = magic - GL13.GL_TEXTURE0; + } + + public void updateEnabledState(int magic, boolean enabled) { + switch (magic) { + case GL11.GL_FOG: FogStateCallback.EVENT.invoker().fogStateChanged(enabled); break; + case GL11.GL_TEXTURE_2D: Texture2DStateCallback.EVENT.invoker().texture2DStateChanged(activeTexture, enabled); break; + } + } + + // Called via ASM (see GLStateTrackerTransformer) + @Deprecated + @SuppressWarnings("unused") + public static void hook_glEnable(int magic) { + GL11.glEnable(magic); + getInstance().updateEnabledState(magic, true); + } + + // Called via ASM (see GLStateTrackerTransformer) + @Deprecated + @SuppressWarnings("unused") + public static void hook_glDisable(int magic) { + GL11.glDisable(magic); + getInstance().updateEnabledState(magic, false); + } +} diff --git a/versions/1.7.10/src/main/java/com/replaymod/render/mixin/MixinOpenGlHelper.java b/versions/1.7.10/src/main/java/com/replaymod/render/mixin/MixinOpenGlHelper.java new file mode 100644 index 00000000..8a20ca74 --- /dev/null +++ b/versions/1.7.10/src/main/java/com/replaymod/render/mixin/MixinOpenGlHelper.java @@ -0,0 +1,16 @@ +package com.replaymod.render.mixin; + +import com.replaymod.render.hooks.GLStateTracker; +import net.minecraft.client.renderer.OpenGlHelper; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(OpenGlHelper.class) +public abstract class MixinOpenGlHelper { + @Inject(method = "setActiveTexture", at = @At("RETURN")) + private static void replayModRender_trackActiveTexture(int magic, CallbackInfo ci) { + GLStateTracker.getInstance().updateActiveTexture(magic); + } +} diff --git a/versions/1.7.10/src/main/java/com/replaymod/render/mixin/Mixin_ForceChunkLoading.java b/versions/1.7.10/src/main/java/com/replaymod/render/mixin/Mixin_ForceChunkLoading.java new file mode 100644 index 00000000..3d319182 --- /dev/null +++ b/versions/1.7.10/src/main/java/com/replaymod/render/mixin/Mixin_ForceChunkLoading.java @@ -0,0 +1,27 @@ +package com.replaymod.render.mixin; + +import com.replaymod.render.hooks.ForceChunkLoadingHook; +import com.replaymod.render.hooks.IForceChunkLoading; +import net.minecraft.client.renderer.RenderGlobal; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyVariable; + +@Mixin(RenderGlobal.class) +public abstract class Mixin_ForceChunkLoading implements IForceChunkLoading { + private ForceChunkLoadingHook replayModRender_hook; + + @Override + public void replayModRender_setHook(ForceChunkLoadingHook hook) { + this.replayModRender_hook = hook; + } + + @ModifyVariable(method = "updateRenderers", at = @At("HEAD"), argsOnly = true) + private boolean replayModRender_updateAllChunks(boolean renderAllChunks) { + if (replayModRender_hook != null) { + renderAllChunks = true; + } + return renderAllChunks; + } +} + diff --git a/versions/1.7.10/src/main/java/com/replaymod/replay/mixin/MixinEntityRenderer.java b/versions/1.7.10/src/main/java/com/replaymod/replay/mixin/MixinEntityRenderer.java new file mode 100644 index 00000000..40c44332 --- /dev/null +++ b/versions/1.7.10/src/main/java/com/replaymod/replay/mixin/MixinEntityRenderer.java @@ -0,0 +1,26 @@ +package com.replaymod.replay.mixin; + +import com.replaymod.replay.camera.CameraEntity; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.EntityRenderer; +import net.minecraft.entity.EntityLivingBase; +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; + +@Mixin(EntityRenderer.class) +public class MixinEntityRenderer { + @Shadow private float prevCamRoll; + @Shadow private float camRoll; + @Shadow private Minecraft mc; + + @Inject(method = "orientCamera", at = @At("HEAD")) + private void applyRoll(float partialTicks, CallbackInfo ci) { + EntityLivingBase view = mc.renderViewEntity; + if (view instanceof CameraEntity) { + prevCamRoll = camRoll = ((CameraEntity) view).roll; + } + } +} diff --git a/versions/1.7.10/src/main/java/com/replaymod/replay/mixin/MixinFMLClientHandler.java b/versions/1.7.10/src/main/java/com/replaymod/replay/mixin/MixinFMLClientHandler.java new file mode 100644 index 00000000..b5e45632 --- /dev/null +++ b/versions/1.7.10/src/main/java/com/replaymod/replay/mixin/MixinFMLClientHandler.java @@ -0,0 +1,27 @@ +package com.replaymod.replay.mixin; + +import com.replaymod.replay.gui.screen.GuiOpeningReplay; +import cpw.mods.fml.client.FMLClientHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiScreen; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(value = FMLClientHandler.class, remap = false) +public class MixinFMLClientHandler { + // Usually this method is called async and can just block and wait for MC to tick the network manager + // Since we sometimes call it sync though, we need to take care of ticking ourselves. + @Inject(method = "waitForPlayClient", at = @At("HEAD"), remap = false) + private void tickNetworkManager(CallbackInfo ci) { + Minecraft mc = Minecraft.getMinecraft(); + if (!mc.isCallingFromMinecraftThread()) { + return; + } + GuiScreen gui = mc.currentScreen; + if (gui instanceof GuiOpeningReplay) { + gui.handleInput(); + } + } +} diff --git a/versions/1.8.9/src/main/java/com/replaymod/recording/mixin/MixinPlayerControllerMP.java b/versions/1.8.9/src/main/java/com/replaymod/recording/mixin/MixinPlayerControllerMP.java new file mode 100644 index 00000000..1adc3fa2 --- /dev/null +++ b/versions/1.8.9/src/main/java/com/replaymod/recording/mixin/MixinPlayerControllerMP.java @@ -0,0 +1 @@ +// 1.9.4 - 1.12.2 diff --git a/versions/1.8.9/src/main/java/com/replaymod/render/mixin/MixinEffectRenderer.java b/versions/1.8.9/src/main/java/com/replaymod/render/mixin/MixinEffectRenderer.java new file mode 100644 index 00000000..a7dd45eb --- /dev/null +++ b/versions/1.8.9/src/main/java/com/replaymod/render/mixin/MixinEffectRenderer.java @@ -0,0 +1,101 @@ +package com.replaymod.render.mixin; + +import com.replaymod.render.blend.BlendState; +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.client.particle.EffectRenderer; +import net.minecraft.client.particle.EntityFX; +import net.minecraft.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; + +//#if MC>=10800 +import com.replaymod.render.blend.exporters.ParticlesExporter; +import net.minecraft.client.renderer.WorldRenderer; +//#else +//$$ import net.minecraft.client.renderer.Tessellator; +//#endif + +@Mixin(EffectRenderer.class) +public abstract class MixinEffectRenderer { + //#if MC>=10800 + //#if MC>=10809 + @Redirect(method = "renderParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;renderParticle(Lnet/minecraft/client/renderer/WorldRenderer;Lnet/minecraft/entity/Entity;FFFFFF)V")) + //#else + //$$ @Redirect(method = "renderParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;func_180434_a(Lnet/minecraft/client/renderer/WorldRenderer;Lnet/minecraft/entity/Entity;FFFFFF)V")) + //#endif + private void renderNormalParticle(EntityFX fx, WorldRenderer worldRenderer, Entity view, float partialTicks, + float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) { + renderParticle(fx, worldRenderer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY); + } + //#else + //$$ @Redirect(method = "renderParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;renderParticle(Lnet/minecraft/client/renderer/Tessellator;FFFFFF)V")) + //$$ private void renderNormalParticle(EntityFX fx, Tessellator tesselator, float partialTicks, + //$$ float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) { + //$$ renderParticle(fx, tesselator, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY); + //$$ } + //#endif + + //#if MC>=10800 + //#if MC>=10809 + @Redirect(method = "renderLitParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;renderParticle(Lnet/minecraft/client/renderer/WorldRenderer;Lnet/minecraft/entity/Entity;FFFFFF)V")) + //#else + //$$ @Redirect(method = "renderLitParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;func_180434_a(Lnet/minecraft/client/renderer/WorldRenderer;Lnet/minecraft/entity/Entity;FFFFFF)V")) + //#endif + private void renderLitParticle(EntityFX fx, WorldRenderer worldRenderer, Entity view, float partialTicks, + float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) { + renderParticle(fx, worldRenderer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY); + } + //#else + //$$ @Redirect(method = "renderLitParticles", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/particle/EntityFX;renderParticle(Lnet/minecraft/client/renderer/Tessellator;FFFFFF)V")) + //$$ private void renderLitParticle(EntityFX fx, Tessellator tessellator, float partialTicks, + //$$ float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) { + //$$ renderParticle(fx, tessellator, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY); + //$$ } + //#endif + + private void renderParticle(EntityFX fx, + //#if MC>=10800 + WorldRenderer worldRenderer, Entity view, + //#else + //$$ Tessellator tessellator, + //#endif + float partialTicks, + float rotX, float rotXZ, float rotZ, float rotYZ, float rotXY) { + EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler(); + if (handler != null && handler.omnidirectional) { + //#if MC<=10710 + //$$ Entity view = Minecraft.getMinecraft().renderViewEntity; + //#endif + // Align all particles towards the camera + double dx = fx.prevPosX + (fx.posX - fx.prevPosX) * partialTicks - view.posX; + double dy = fx.prevPosY + (fx.posY - fx.prevPosY) * partialTicks - view.posY; + double dz = fx.prevPosZ + (fx.posZ - fx.prevPosZ) * partialTicks - view.posZ; + double pitch = -Math.atan2(dy, Math.sqrt(dx * dx + dz * dz)); + double yaw = -Math.atan2(dx, dz); + + rotX = (float) Math.cos(yaw); + rotZ = (float) Math.sin(yaw); + rotXZ = (float) Math.cos(pitch); + + rotYZ = (float) (-rotZ * Math.sin(pitch)); + rotXY = (float) (rotX * Math.sin(pitch)); + } + //#if MC>=10800 + BlendState blendState = BlendState.getState(); + if (blendState != null) { + blendState.get(ParticlesExporter.class).onRender(fx, partialTicks); + } + //#endif + //#if MC>=10809 + fx.renderParticle(worldRenderer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY); + //#else + //#if MC>=10800 + //$$ fx.func_180434_a(worldRenderer, view, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY); + //#else + //$$ fx.renderParticle(tessellator, partialTicks, rotX, rotXZ, rotZ, rotYZ, rotXY); + //#endif + //#endif + } +} diff --git a/versions/1.8.9/src/main/java/com/replaymod/render/mixin/Mixin_HideNameTags_LivingEntity.java b/versions/1.8.9/src/main/java/com/replaymod/render/mixin/Mixin_HideNameTags_LivingEntity.java new file mode 100644 index 00000000..edbd70ef --- /dev/null +++ b/versions/1.8.9/src/main/java/com/replaymod/render/mixin/Mixin_HideNameTags_LivingEntity.java @@ -0,0 +1,21 @@ +package com.replaymod.render.mixin; + +import com.replaymod.render.hooks.EntityRendererHandler; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.entity.RendererLivingEntity; +import net.minecraft.entity.EntityLivingBase; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; + +@Mixin(RendererLivingEntity.class) +public abstract class Mixin_HideNameTags_LivingEntity { + @Inject(method = "canRenderName", at = @At("HEAD"), cancellable = true) + private void replayModRender_areAllNamesHidden(EntityLivingBase entity, CallbackInfoReturnable ci) { + EntityRendererHandler handler = ((EntityRendererHandler.IEntityRenderer) Minecraft.getMinecraft().entityRenderer).replayModRender_getHandler(); + if (handler != null && !handler.getSettings().isRenderNameTags()) { + ci.setReturnValue(false); //this calls the cancel method + } + } +} diff --git a/versions/1.8/mapping.txt b/versions/1.8/mapping.txt index 8f452af4..3096beb7 100644 --- a/versions/1.8/mapping.txt +++ b/versions/1.8/mapping.txt @@ -31,6 +31,10 @@ net.minecraftforge.fml.common.network.handshake.FMLHandshakeMessage.RegistryData net.minecraftforge.fml.common.network.internal.FMLProxyPacket cpw.mods.fml.common.network.internal.FMLProxyPacket net.minecraftforge.fml.common.network.NetworkRegistry cpw.mods.fml.common.network.NetworkRegistry net.minecraftforge.fml.common.gameevent.InputEvent cpw.mods.fml.common.gameevent.InputEvent +net.minecraftforge.fml.common.versioning.ComparableVersion cpw.mods.fml.common.versioning.ComparableVersion net.minecraft.client.renderer.tileentity.TileEntityEndPortalRenderer net.minecraft.client.renderer.tileentity.RenderEndPortal net.minecraft.network.play.server.S3FPacketCustomPayload getChannelName() func_149169_c() net.minecraft.network.play.server.S01PacketJoinGame getEntityId() func_149197_c() +net.minecraft.client.network.NetHandlerLoginClient networkManager field_147393_d +net.minecraft.client.renderer.WorldRenderer com.replaymod.core.versions.BufferBuilder +net.minecraft.client.renderer.vertex.DefaultVertexFormats com.replaymod.core.versions.BufferBuilder.VertexFormats diff --git a/versions/1.9.4/mapping.txt b/versions/1.9.4/mapping.txt index d0fcbe20..9225b4c1 100644 --- a/versions/1.9.4/mapping.txt +++ b/versions/1.9.4/mapping.txt @@ -20,6 +20,7 @@ net.minecraft.network.play.server.SPacketSpawnMob dataManager field_149043_l net.minecraft.network.play.server.SPacketSpawnMob getDataManagerEntries() func_149027_c() net.minecraft.network.play.server.SPacketSpawnPlayer getDataManagerEntries() func_148944_c() net.minecraft.client.multiplayer.ServerList saveSingleServer() func_147414_b() +net.minecraft.util.SoundEvent com.replaymod.core.versions.MCVer.SoundEvent net.minecraft.network.play.server.SPacketJoinGame net.minecraft.network.play.server.S01PacketJoinGame net.minecraft.network.play.server.SPacketChat net.minecraft.network.play.server.S02PacketChat diff --git a/versions/mapping-fabric-1.17-1.16.4.txt b/versions/mapping-fabric-1.17-1.16.4.txt new file mode 100644 index 00000000..300a639a --- /dev/null +++ b/versions/mapping-fabric-1.17-1.16.4.txt @@ -0,0 +1,4 @@ +com.mojang.blaze3d.systems.RenderSystem com.mojang.blaze3d.platform.GlStateManager +net.minecraft.network.packet.s2c.play.PlayerRespawnS2CPacket getDimensionType() method_29445() +net.minecraft.entity.Entity getId() getEntityId() +net.minecraft.client.network.ClientPlayerEntity init() net.minecraft.entity.player.PlayerEntity afterSpawn()