diff options
author | Linnea Gräf <nea@nea.moe> | 2024-08-11 13:22:24 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-08-11 13:22:24 +0200 |
commit | 8903d9fa783455558d20eb32a2038c87ab5913be (patch) | |
tree | 2c925d0ecf625f8e8a5505960d0d51d2201e88e2 | |
parent | a8d4861b051298cc8a1db3c5210a32abab866b1e (diff) | |
download | skyhanni-8903d9fa783455558d20eb32a2038c87ab5913be.tar.gz skyhanni-8903d9fa783455558d20eb32a2038c87ab5913be.tar.bz2 skyhanni-8903d9fa783455558d20eb32a2038c87ab5913be.zip |
Add multi version preprocessor (#2283)
Co-authored-by: hannibal2 <24389977+hannibal00212@users.noreply.github.com>
34 files changed, 1027 insertions, 188 deletions
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 379f842d8..8275ac8b0 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,44 +1,63 @@ name: Build on: - push: - branches: - - "*" - paths-ignore: - - ".gitignore" - pull_request: - branches: - - "*" - paths-ignore: - - ".gitignore" - workflow_dispatch: + push: + branches: + - "*" + paths-ignore: + - ".gitignore" + pull_request: + branches: + - "*" + paths-ignore: + - ".gitignore" + workflow_dispatch: permissions: write-all jobs: - build: - runs-on: ubuntu-latest - name: "Build and test" - steps: - - uses: actions/checkout@v3 - - name: Set up JDK 17 - uses: actions/setup-java@v3 - with: - java-version: 17 - distribution: temurin - cache: gradle - - name: Setup gradle - uses: gradle/gradle-build-action@v2 - - name: Build with Gradle - run: ./gradlew build -x test --stacktrace - - uses: actions/upload-artifact@v3 - name: Upload development build - with: - name: "Development Build" - path: build/libs/*.jar - - name: Test with Gradle - run: ./gradlew test - - uses: actions/upload-artifact@v3 - name: "Upload test report" - if: ${{ !cancelled() }} - with: - name: "Test Results" - path: build/reports/tests/test/ + build: + runs-on: ubuntu-latest + name: "Build and test" + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 21 + uses: actions/setup-java@v3 + with: + java-version: 21 + distribution: temurin + cache: gradle + - name: Setup gradle + uses: gradle/gradle-build-action@v2 + - name: Build with Gradle + run: ./gradlew assemble -x test --stacktrace + - uses: actions/upload-artifact@v3 + name: Upload development build + with: + name: "Development Build" + path: versions/1.8.9/build/libs/*.jar + - name: Test with Gradle + run: ./gradlew test + - uses: actions/upload-artifact@v3 + name: "Upload test report" + if: ${{ !cancelled() }} + with: + name: "Test Results" + path: versions/1.8.9/build/reports/tests/test/ + preprocess: + runs-on: ubuntu-latest + name: "Build multi version" + steps: + - uses: actions/checkout@v3 + - name: Set up JDK 21 + uses: actions/setup-java@v3 + with: + java-version: 21 + distribution: temurin + cache: gradle + - name: Setup gradle + uses: gradle/gradle-build-action@v2 + - name: Enable preprocessor + run: | + mkdir -p .gradle + echo skyhanni.multi-version=preprocess-only > .gradle/private.properties + - name: Build with Gradle + run: ./gradlew build --stacktrace diff --git a/.github/workflows/generate-constants.yaml b/.github/workflows/generate-constants.yaml index 505a035b2..c589676d0 100644 --- a/.github/workflows/generate-constants.yaml +++ b/.github/workflows/generate-constants.yaml @@ -18,10 +18,10 @@ jobs: name: "Generate regexes" steps: - uses: actions/checkout@v3 - - name: Set up JDK 17 + - name: Set up JDK 21 uses: actions/setup-java@v3 with: - java-version: 17 + java-version: 21 distribution: temurin cache: gradle - name: Setup gradle @@ -33,7 +33,7 @@ jobs: name: Upload generated repo regexes with: name: Repo Regexes - path: build/regexes/constants.json + path: versions/1.8.9/build/regexes/constants.json publish-regexes: runs-on: ubuntu-latest needs: regexes diff --git a/.gitignore b/.gitignore index c303fd98c..d77f4bd8b 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ gradle.properties .devauth/* !.devauth/config.toml +.kotlin diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 62ffd979e..57f22f2e2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -180,3 +180,194 @@ Plugin [HotSwap Agent](https://plugins.jetbrains.com/plugin/9552-hotswapagent) t Follow [this](https://forums.Minecraftforge.net/topic/82228-1152-3110-intellij-and-gradlew-forge-hotswap-and-dcevm-tutorial/) tutorial. + +## 1.21 / Modern version development + +You might have noticed that while the SkyHanni source code is found in `src/`, the actual tasks for compiling, building and running the mod +are located in a subproject called `1.8.9`. This is because SkyHanni is preparing for the eventual fall of 1.8.9 (via the foraging update or +otherwise). + +To do so (while not disrupting regular development) we use [preprocessor](https://github.com/Deftu/RM-Preprocessor). Preprocessor +automatically transforms code based on mappings as well as comment directives to create multiple variants of your source code for +different Minecraft versions. + +Note also that the only targets we consider are 1.8.9 and 1.21 (or whatever the latest version we may target). The other versions are only there +to make mappings translate more easily (more on that later). + +### Goals + +It is the explicit goal of this operation to passively generate a 1.21 version of SH using preprocessor. To this end, contributors are +encouraged to add mappings and preprocessing directives to their features to make them compile on 1.21. *However*, this is considered a very +low priority. Due to the confusing nature (and the slower initial setup time due to decompiling four versions of Minecraft), this feature +is disabled by default. Similarly, it is up to each contributor to decide if they want to learn how to use preprocessor mappings and +directives. An explicit non-goal is to maintain two SH versions continuously; instead, we only want to make the eventual transition to 1.21 a task +that can be slowly worked on over a long span of time. + +### Set Up + +The modern version variants can be set using `skyhanni.multi-version` in `.gradle/private.properties` to three levels. + +`off` completely disables any preprocessor action or alternative versions. There will be only one project (although still at the `:1.8.9` +subproject path), and alternative version sources will not be generated (although old generated sources **will not be deleted**). To make +setting up a dev environment as fast and discernible as possible, this is the default option. + +`preprocess-only` adds the `preprocessCode` task as well as all the version subprojects. Compiling or running newer versions is not +possible, but the `preprocessCode` task can be run manually to inspect the generated source code. This mode is what should most often be +used when making alterations to the mappings or modifying preprocessor directives. Note that while this setting generally ignores any failed +renaming attempts, if something is so badly mangled that it cannot even guess a name for a function, it will still break the build. Those +situations should be rare, however. (In the entire SH codebase prior to me introducing this system I only found <10 such cases). You can +specifically compile 1.8.9 using `./gradlew :1.8.9:build`. This does not affect the regular execution of the client which will only compile +1.8.9. + +`compile` enables compilation for the `:1.21` subproject. This means that a `build` or `assemble` task will try (and fail) to compile a +1.21 (as well as 1.8.9) JAR. This mode may be useful for someone seeking out issues to fix, but is generally not useful in day to day +operations since the compile will never succeed and will block things like hotswap compilations (via <kbd>CTRL+F9</kbd>) from completing. + +### Improving mappings + +The different project versions are set up in such a way that each version depends on a slightly older version from which it is then adapted. +There are two main versions (1.8.9 and 1.21), but there are also a few bridge versions. These exist to make remapping easier since automatic +name mappings between 1.8.9 and 1.21 do not really exist. This is the current layout for our remaps: First, we remap to 1.12. This still +largely uses the old rendering system and has a lot of similar names to 1.8.9. As such, only very little needs to be adapted in terms of +behaviour, and at best, a couple of names need to be updated manually. The big jump is from 1.12 to 1.16. We use 1.16 since we don't want to +make too large of a jump (which could lead to a lot more missing names), but we still want to jump to a version with Fabric (and specifically +with Fabric intermediary mappings). We also can't really jump to an earlier version since 1.14 and 1.15 have a really poor Fabric API and +still have some of the old rendering code, meaning we would need to adapt to two slightly different rendering engines instead of just one +big rendering change. Despite the preprocessor's best efforts, this version will likely have the most manual mapping changes. Note that we +actually have two projects on 1.16. There is the Forge project, which is the one we remap to first. Then we remap to the corresponding +Fabric/Yarn mappings. This is because remapping between Searge and Yarn is very inconsistent unless it is done on one and the same version. +Finally, we remap from 1.16 to 1.21. This is a fairly small change, especially since Fabric intermediary mappings make different names +between versions very rare. The only real changes that need to be done in this jump are behavioural ones. + +The preprocessor does some built-in remapping (changing names), based on obfuscated names, but sometimes the automatic matching fails. If it +cannot find a new name (or the name it automatically determines was wrong), you can change the corresponding mapping. In order to make +this as smooth as possible, it is generally recommended to find the earliest spot at which the mappings deviate. So fixing a mapping on the +hop from 1.16 to 1.21 is generally not recommended. This is because, while we do not care about 1.12 or 1.16 compiling for its own merit, +we do care about the automatically inferred name changes from 1.12 to 1.16 and so on, which only work if those versions already have the +correct names available. + +#### A missing/incorrect name + +This is the easiest part. If a name for a function simply could not be automatically remapped, all you need to do is to add an entry in the +corresponding mapping.txt file. These can be found at `versions/mapping-<newVersion>-<oldVersion>.txt`. + +``` +# You can use # to comment lines + +# You can rename a class simply by writing the two names +# The first name is the name on the newer version (the first one in the file name); the second one is the name in the old version. +net.minecraft.util.math.MathHelper net.minecraft.util.MathHelper +# If you want to rename an inner class, remember to use $s +net.minecraft.world.scores.Team$Visibility net.minecraft.scoreboard.Team$EnumVisible + +# You can rename a field by writing the name of the containing class in the new version, and then the new and old name of the field. +# Again, the first field name is the one in the newer version (first in the file name). +net.minecraft.world.entity.Entity xOld prevPosX + +# Finally, you can also rename methods. To do so, you need to first specify the name of the containing class in the new version, then +# the name of the new and old method name. The first method name is the newer version name (first in the file name). +net.minecraft.util.text.Style getHoverEvent() getChatHoverEvent() +``` + +Adding a mapping like this is the easiest way to fix a broken method call, field access, or class reference. It will also apply to all +files, so you might be fixing issues in files you didn't even look at. It will even work in mixin targets, as long as they are unambiguous +(consider using the method descriptor instead of just the method name for your mixin). However, if something aside from the name changed, +this will not suffice. + +#### Conditional compilation + +In addition to the built-in remapping, there is also the more complicated art of preprocessor directives. Directives allow you to comment or +uncomment sections of the code depending on the version you are on. Uncommented sections are renamed as usual, so even within those directives, +you only need to write code for the *lowest* version that your comment is active in. As such, I once again highly recommend to target your +directive to the lowest version in which it applies, so that other sections that call into that code as well as your code can make use of +as many automatic renames as possible. + +There is only really one directive, which is `if`. Take this function, for example: + +```kt +private fun WorldClient.getAllEntities(): Iterable<Entity> = +//#if MC < 1.16 + loadedEntityList +//#else +//$$ entitiesForRendering() +//#endif +``` + +The first `#if` instructs the preprocessor to only uncomment the following code if the Minecraft version is less than 1.16. Then, the `#else` +uncomments the other section on versions 1.16 and above. Finally, the `#endif` ends the else block and lets the following functions always remain +active. To distinguish regular comments from preprocessor comments, preprocessor only works with comments that start with `//$$`. So let's +walk through what is happening here. + +In 1.8.9, the code remains unchanged. **Note that this means the programmer is responsible for commenting out the unused parts. +The preprocessor will never change the `src/` directory**. + +Next, the preprocessor converts the code to 1.12. 1.12 still has the `loadedEntityList` as well as the same name for the `WorldClient` and +`Entity` classes, so nothing is changed. + +Next, the code gets converted to 1.16 Forge. Since 1.16 is not less than 1.16, it will comment out the first line and uncomment the second line. +1.16 Forge also uses a different name for `WorldClient` and a different package for `Entity`, so those are also changed (the package change +is only visible in the imports): + +```kt +private fun ClientLevel.getAllEntities(): Iterable<Entity> = +//#if MC < 1.14 +//$$ loadedEntityList +//#else + entitiesForRendering() +//#endif +``` + +Now the code gets converted to 1.16 Fabric. Since those two targets are on the same Minecraft version name changes almost never fail to be +done automatically. Notice the different names for `ClientWorld` as well as `entities`. The method is called `getEntities()` on Fabric, but +since this is Kotlin code, the preprocessor automatically cleans up `getEntities()` using +[Kotlin property access syntax](https://kotlinlang.org/docs/java-interop.html#getters-and-setters). + +```kt +private fun ClientWorld.getAllEntities(): Iterable<Entity> = +//#if MC < 1.14 +//$$ loadedEntityList +//#else + entities +//#endif +``` + +Finally, the code gets converted to 1.21 using intermediary mappings. This last step does not bring any new challenges, so we end up with: + +```kt +private fun ClientWorld.getAllEntities(): Iterable<Entity> = +//#if MC < 1.14 +//$$ loadedEntityList +//#else + entities +//#endif +``` + +#### If expressions + +Let's look at the syntax of those `#if` expressions. + +First of all, the `#else` block is optional. If you just want code on some versions (for example for adding a method call that is implicitly +done on newer versions, or simply because the corresponding code for newer versions has to be done in some other place), you can just omit +the `#else` section and you will simply not compile any code at that spot. + +There is also an `#elseif` in case you want to switch behaviour based on multiple version brackets. Again, while we don't actually target +1.12 or 1.16, making those versions compile will help other parts of the code to upgrade to 1.21 more cleanly and easily. So, making those +versions work (or at least providing a stub like `error("Not implemented on this version") as List<Entity>` to make types infer correctly) +should be something you look out for. + +`#if` and `#elseif` also do not support complicated expressions. The only operations supported are `!=`, `==`, `>=`, `<=`, `<` and `>`. You +cannot join two checks using `&&` or similar, instead needing to use nested `#if`s. + +The actual versions being worked with here are not actually semantically compared Minecraft versions, but instead integers in the form +`major * 10000 + minor * 100 + patch`. So, for example, `1.12` turns into `11200`. Both `11200` and `1.12` can be used in directives, but +`1.12` style values are generally easier to understand. + +You can also check if you are on Forge using the `FORGE` variable. It is set to either 1 or 0. Similarly, there is also a `JAVA` variable to +check the Java version this Minecraft version is on. For the `FORGE` variable there is an implicit `!= 0` to check added if you just check +for the variable using `#if FORGE`. + +#### Helpers + +Sadly, `#if` expressions cannot be applied globally (unlike name changes), so it is often very helpful to create a helper method and call +that method from various places in the codebase. This is generally already policy in SH for a lot of things. For more complex types that +change beyond just their name (for example different generics), a `typealias` can be used in combination with `#if` expressions. diff --git a/build.gradle.kts b/build.gradle.kts index 8aaa2d638..773c7ac28 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,52 +1,39 @@ -import org.apache.commons.lang3.SystemUtils +import at.skyhanni.sharedvariables.MinecraftVersion +import at.skyhanni.sharedvariables.MultiVersionStage +import at.skyhanni.sharedvariables.ProjectTarget +import at.skyhanni.sharedvariables.SHVersionInfo +import at.skyhanni.sharedvariables.versionString +import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.tasks.KotlinCompile -import java.io.ByteArrayOutputStream plugins { idea java - id("gg.essential.loom") version "0.10.0.+" - id("dev.architectury.architectury-pack200") version "0.1.3" id("com.github.johnrengelman.shadow") version "7.1.2" - kotlin("jvm") version "1.9.0" - id("com.bnorm.power.kotlin-power-assert") version "0.13.0" + id("gg.essential.loom") + id("dev.deftu.gradle.preprocess") + kotlin("jvm") + id("com.google.devtools.ksp") + kotlin("plugin.power-assert") `maven-publish` - id("com.google.devtools.ksp") version "1.9.0-1.0.13" id("moe.nea.shot") version "1.0.0" } -group = "at.hannibal2.skyhanni" -version = "0.26.Beta.21" - -val gitHash by lazy { - val baos = ByteArrayOutputStream() - exec { - standardOutput = baos - commandLine("git", "rev-parse", "--short", "HEAD") - isIgnoreExitValue = true - } - baos.toByteArray().decodeToString().trim() -} - -// Toolchains: -java { - toolchain.languageVersion.set(JavaLanguageVersion.of(8)) -} - -sourceSets.main { - output.setResourcesDir(sourceSets.main.flatMap { it.java.classesDirectory }) - java.srcDir(layout.projectDirectory.dir("src/main/kotlin")) - kotlin.destinationDirectory.set(java.destinationDirectory) -} +val target = ProjectTarget.values().find { it.projectPath == project.path }!! repositories { mavenCentral() mavenLocal() + maven("https://maven.minecraftforge.net") { + metadataSources { + artifact() // We love missing POMs + } + } maven("https://repo.spongepowered.org/maven/") // mixin maven("https://pkgs.dev.azure.com/djtheredstoner/DevAuth/_packaging/public/maven/v1") // DevAuth maven("https://jitpack.io") { // NotEnoughUpdates (compiled against) content { - includeGroupByRegex("com\\.github\\..*") + includeGroupByRegex("(com|io)\\.github\\..*") } } maven("https://repo.nea.moe/releases") // libautoupdate @@ -55,6 +42,53 @@ repositories { maven("https://maven.teamresourceful.com/repository/thatgravyboat/") // DiscordIPC } +// Toolchains: +java { + toolchain.languageVersion.set(target.minecraftVersion.javaLanguageVersion) + // We specifically request ADOPTIUM because if we do not restrict the vendor DCEVM is a + // possible candidate. Some DCEVMs are however incompatible with some things gradle is doing, + // causing crashes during tests. You can still manually select DCEVM in the Minecraft Client + // IntelliJ run configuration. + toolchain.vendor.set(JvmVendorSpec.ADOPTIUM) +} +val runDirectory = rootProject.file("run") +runDirectory.mkdirs() +// Minecraft configuration: +loom { + if (this.isForgeLike) + forge { + pack200Provider.set(dev.architectury.pack200.java.Pack200Adapter()) + mixinConfig("mixins.skyhanni.json") + } + mixin { + useLegacyMixinAp.set(true) + defaultRefmapName.set("mixins.skyhanni.refmap.json") + } + runs { + named("client") { + if (target == ProjectTarget.MAIN) { + isIdeConfigGenerated = true + appendProjectPathToConfigName.set(false) + } + this.runDir(runDirectory.relativeTo(projectDir).toString()) + property("mixin.debug", "true") + if (System.getenv("repo_action") != "true") { + property("devauth.configDir", rootProject.file(".devauth").absolutePath) + } + vmArgs("-Xmx4G") + programArgs("--tweakClass", "at.hannibal2.skyhanni.tweaker.SkyHanniTweaker") + programArgs("--tweakClass", "io.github.notenoughupdates.moulconfig.tweaker.DevelopmentResourceTweaker") + } + removeIf { it.name == "server" } + } +} + +if (target == ProjectTarget.MAIN) + sourceSets.main { + resources.destinationDirectory.set(kotlin.destinationDirectory) + output.setResourcesDir(kotlin.destinationDirectory) + } + val shadowImpl: Configuration by configurations.creating { configurations.implementation.get().extendsFrom(this) } @@ -72,13 +106,22 @@ val headlessLwjgl by configurations.creating { isTransitive = false isVisible = false } - -val shot = shots.shot("minecraft", project.file("shots.txt")) +tasks.runClient { + this.javaLauncher.set(javaToolchains.launcherFor { + languageVersion.set(target.minecraftVersion.javaLanguageVersion) + }) +} +val shot = shots.shot("minecraft", rootProject.file("shots.txt")) dependencies { - minecraft("com.mojang:minecraft:1.8.9") - mappings("de.oceanlabs.mcp:mcp_stable:22-1.8.9") - forge("net.minecraftforge:forge:1.8.9-11.15.1.2318-1.8.9") + minecraft("com.mojang:minecraft:${target.minecraftVersion.versionName}") + if (target.mappingDependency == "official") { + mappings(loom.officialMojangMappings()) + } else { + mappings(target.mappingDependency) + } + if (target.forgeDep != null) + "forge"(target.forgeDep!!) // Discord RPC client shadowImpl("com.jagrosh:DiscordIPC:0.5.3") { @@ -93,10 +136,16 @@ dependencies { compileOnly(ksp(project(":annotation-processors"))!!) - shadowImpl("org.spongepowered:mixin:0.7.11-SNAPSHOT") { - isTransitive = false + val mixinVersion = if (target.minecraftVersion >= MinecraftVersion.MC11200) "0.8.2" else "0.7.11-SNAPSHOT" + + if (!target.isFabric) { + shadowImpl("org.spongepowered:mixin:$mixinVersion") { + isTransitive = false + } + annotationProcessor("org.spongepowered:mixin:0.8.5-SNAPSHOT") + annotationProcessor("com.google.code.gson:gson:2.10.1") + annotationProcessor("com.google.guava:guava:17.0") } - annotationProcessor("org.spongepowered:mixin:0.8.4-SNAPSHOT") implementation(kotlin("stdlib-jdk8")) shadowImpl("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") { @@ -133,14 +182,16 @@ dependencies { implementation("net.hypixel:mod-api:0.3.1") } -configurations.getByName("minecraftNamed").dependencies.forEach { - shot.applyTo(it as HasConfigurableAttributes<*>) +afterEvaluate { + loom.runs.named("client") { + programArgs("--mods", devenvMod.resolve().joinToString(",") { it.relativeTo(runDirectory).path }) + } } tasks.withType(Test::class) { useJUnitPlatform() javaLauncher.set(javaToolchains.launcherFor(java.toolchain)) - workingDir(file("run")) + workingDir(file(runDirectory)) systemProperty("junit.jupiter.extensions.autodetection.enabled", "true") } @@ -151,46 +202,6 @@ kotlin { enableLanguageFeature("BreakContinueInInlineLambdas") } } - sourceSets.main { - kotlin.srcDir("build/generated/ksp/main/kotlin") - } - sourceSets.test { - kotlin.srcDir("build/generated/ksp/test/kotlin") - } -} - -// Minecraft configuration: -loom { - launchConfigs { - "client" { - property("mixin.debug", "true") - if (System.getenv("repo_action") != "true") { - property("devauth.configDir", rootProject.file(".devauth").absolutePath) - } - arg("--tweakClass", "at.hannibal2.skyhanni.tweaker.SkyHanniTweaker") - arg("--tweakClass", "io.github.notenoughupdates.moulconfig.tweaker.DevelopmentResourceTweaker") - arg("--mods", devenvMod.resolve().joinToString(",") { it.relativeTo(file("run")).path }) - } - } - forge { - pack200Provider.set(dev.architectury.pack200.java.Pack200Adapter()) - mixinConfig("mixins.skyhanni.json") - } - @Suppress("UnstableApiUsage") - mixin { - defaultRefmapName.set("mixins.skyhanni.refmap.json") - } - runConfigs { - "client" { - if (SystemUtils.IS_OS_MAC_OSX) { - vmArgs.remove("-XstartOnFirstThread") - } - vmArgs.add("-Xmx4G") - } - "server" { - isIdeConfigGenerated = false - } - } } // Tasks: @@ -199,51 +210,70 @@ tasks.processResources { filesMatching(listOf("mcmod.info", "fabric.mod.json")) { expand("version" to version) } + if (target.isFabric) { + exclude("mcmod.info") + } // else do NOT exclude fabric.mod.json. We use fabric.mod.json in order to show a logo in prism launcher. } -val generateRepoPatterns by tasks.creating(JavaExec::class) { - javaLauncher.set(javaToolchains.launcherFor(java.toolchain)) - mainClass.set("net.fabricmc.devlaunchinjector.Main") - workingDir(project.file("run")) - classpath(sourceSets.main.map { it.runtimeClasspath }, sourceSets.main.map { it.output }) - jvmArgs( - "-Dfabric.dli.config=${project.file(".gradle/loom-cache/launch.cfg").absolutePath}", - "-Dfabric.dli.env=client", - "-Dfabric.dli.main=net.minecraft.launchwrapper.Launch", - "-Dorg.lwjgl.opengl.Display.allowSoftwareOpenGL=true", - "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5006", - "-javaagent:${headlessLwjgl.singleFile.absolutePath}" - ) - val outputFile = project.file("build/regexes/constants.json") - environment("SKYHANNI_DUMP_REGEXES", "${gitHash}:${outputFile.absolutePath}") - environment("SKYHANNI_DUMP_REGEXES_EXIT", "true") +if (target == ProjectTarget.MAIN) { + val generateRepoPatterns by tasks.creating(JavaExec::class) { + javaLauncher.set(javaToolchains.launcherFor(java.toolchain)) + mainClass.set("net.fabricmc.devlaunchinjector.Main") + dependsOn(tasks.configureLaunch) + workingDir(project.file(runDirectory)) + classpath(sourceSets.main.map { it.runtimeClasspath }, sourceSets.main.map { it.output }) + jvmArgs( + "-Dfabric.dli.config=${project.file(".gradle/loom-cache/launch.cfg").absolutePath}", + "-Dfabric.dli.env=client", + "-Dfabric.dli.main=net.minecraft.launchwrapper.Launch", + "-Dorg.lwjgl.opengl.Display.allowSoftwareOpenGL=true", + "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5006", + "-javaagent:${headlessLwjgl.singleFile.absolutePath}", + ) + val outputFile = project.file("build/regexes/constants.json") + environment("SKYHANNI_DUMP_REGEXES", "${SHVersionInfo.gitHash}:${outputFile.absolutePath}") + environment("SKYHANNI_DUMP_REGEXES_EXIT", "true") + } } -tasks.compileJava { - dependsOn(tasks.processResources) +if (target == ProjectTarget.MAIN) + tasks.compileJava { + dependsOn(tasks.processResources) + } + +if (target.parent == ProjectTarget.MAIN) { + val mainRes = project(ProjectTarget.MAIN.projectPath).tasks.getAt("processResources") + tasks.named("processResources") { + dependsOn(mainRes) + } + tasks.named("preprocessCode") { + dependsOn(mainRes) + } } tasks.withType(JavaCompile::class) { options.encoding = "UTF-8" } -tasks.withType(Jar::class) { +tasks.withType(org.gradle.jvm.tasks.Jar::class) { archiveBaseName.set("SkyHanni") - duplicatesStrategy = DuplicatesStrategy.EXCLUDE + duplicatesStrategy = DuplicatesStrategy.EXCLUDE // Why do we have this here? This only *hides* errors. manifest.attributes.run { - this["FMLCorePluginContainsFMLMod"] = "true" - this["ForceLoadAsMod"] = "true" this["Main-Class"] = "SkyHanniInstallerFrame" - - this["TweakClass"] = "at.hannibal2.skyhanni.tweaker.SkyHanniTweaker" - this["MixinConfigs"] = "mixins.skyhanni.json" + if (target == ProjectTarget.MAIN) { + this["FMLCorePluginContainsFMLMod"] = "true" + this["ForceLoadAsMod"] = "true" + this["TweakClass"] = "at.hannibal2.skyhanni.tweaker.SkyHanniTweaker" + this["MixinConfigs"] = "mixins.skyhanni.json" + } } } val remapJar by tasks.named<net.fabricmc.loom.task.RemapJarTask>("remapJar") { archiveClassifier.set("") - from(tasks.shadowJar) - input.set(tasks.shadowJar.get().archiveFile) + dependsOn(tasks.shadowJar) + inputFile.set(tasks.shadowJar.get().archiveFile) + destinationDirectory.set(rootProject.layout.buildDirectory.dir("libs")) } tasks.shadowJar { @@ -267,13 +297,31 @@ tasks.jar { } tasks.assemble.get().dependsOn(tasks.remapJar) -val compileKotlin: KotlinCompile by tasks -compileKotlin.kotlinOptions { - jvmTarget = "1.8" +tasks.withType(KotlinCompile::class) { + compilerOptions { + jvmTarget.set(JvmTarget.fromTarget(target.minecraftVersion.javaLanguageVersion.versionString())) + } +} + +if (!MultiVersionStage.activeState.shouldCompile(target)) { + tasks.withType<JavaCompile> { + onlyIf { false } + } + tasks.withType<KotlinCompile> { + onlyIf { false } + } + tasks.withType<AbstractArchiveTask> { + onlyIf { false } + } + tasks.withType<ProcessResources> { + onlyIf { false } + } } -val compileTestKotlin: KotlinCompile by tasks -compileTestKotlin.kotlinOptions { - jvmTarget = "1.8" +preprocess { + vars.put("MC", target.minecraftVersion.versionNumber) + vars.put("FORGE", if (target.forgeDep != null) 1 else 0) + vars.put("JAVA", target.minecraftVersion.javaVersion) + patternAnnotation.set("at.hannibal2.skyhanni.utils.compat.Pattern") } val sourcesJar by tasks.creating(Jar::class) { destinationDirectory.set(layout.buildDirectory.dir("badjars")) diff --git a/gradle.properties b/gradle.properties index 16cc55dfa..f5b8c6325 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,3 @@ -loom.platform=forge org.gradle.jvmargs=-Xmx2g org.gradle.parallel=true org.gradle.caching=true diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index db9a6b825..19cfad969 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.3-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/root.gradle.kts b/root.gradle.kts new file mode 100644 index 000000000..bc43f053d --- /dev/null +++ b/root.gradle.kts @@ -0,0 +1,42 @@ +import at.skyhanni.sharedvariables.ProjectTarget +import com.replaymod.gradle.preprocess.Node + +plugins { + id("dev.deftu.gradle.preprocess") version "0.6.1" + id("gg.essential.loom") version "1.6.+" apply false + kotlin("jvm") version "2.0.0" apply false + kotlin("plugin.power-assert") version "2.0.0" apply false + id("com.google.devtools.ksp") version "2.0.0-1.0.24" apply false + id("dev.architectury.architectury-pack200") version "0.1.3" +} + +allprojects { + group = "at.hannibal2.skyhanni" + version = "0.26.Beta.21" +} + +preprocess { + val nodes = mutableMapOf<ProjectTarget, Node>() + ProjectTarget.activeVersions().forEach { target -> + nodes[target] = createNode(target.projectName, target.minecraftVersion.versionNumber, target.mappingStyle.identifier) + val p = project(target.projectPath) + if (target.isForge) + p.extra.set("loom.platform", "forge") + } + ProjectTarget.activeVersions().forEach { child -> + val parent = child.linkTo ?: return@forEach + val pNode = nodes[parent] + if (pNode == null) { + println("Parent target to ${child.projectName} not available in this multi version stage. Not setting parent.") + return@forEach + } + val mappingFile = file("versions/mapping-${parent.projectName}-${child.projectName}.txt") + if (mappingFile.exists()) { + pNode.link(nodes[child]!!, mappingFile) + println("Loading mappings from $mappingFile") + } else { + pNode.link(nodes[child]!!) + println("Skipped loading mappings from $mappingFile") + } + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index 8bd4de72f..2de2760b2 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -1,4 +1,8 @@ +import at.skyhanni.sharedvariables.MultiVersionStage +import at.skyhanni.sharedvariables.ProjectTarget + pluginManagement { + includeBuild("sharedVariables") repositories { mavenCentral() gradlePluginPortal() @@ -9,6 +13,12 @@ pluginManagement { maven("https://repo.spongepowered.org/maven/") maven("https://repo.nea.moe/releases") maven("https://repo.sk1er.club/repository/maven-releases/") + maven("https://maven.deftu.xyz/releases") + maven("https://jitpack.io") { + content { + includeGroupByRegex("(com|io)\\.github\\..*") + } + } } resolutionStrategy { eachPlugin { @@ -20,8 +30,20 @@ pluginManagement { } plugins { - id("org.gradle.toolchains.foojay-resolver-convention") version("0.6.0") + id("org.gradle.toolchains.foojay-resolver-convention") version ("0.8.0") + id("at.skyhanni.shared-variables") } +MultiVersionStage.initFrom(file(".gradle/private.properties")) + include("annotation-processors") rootProject.name = "SkyHanni" +rootProject.buildFileName = "root.gradle.kts" + +ProjectTarget.activeVersions().forEach { target -> + include(target.projectPath) + val p = project(target.projectPath) + p.projectDir = file("versions/${target.projectName}") + p.buildFileName = "../../build.gradle.kts" +} + diff --git a/sharedVariables/build.gradle.kts b/sharedVariables/build.gradle.kts new file mode 100644 index 000000000..27799fb86 --- /dev/null +++ b/sharedVariables/build.gradle.kts @@ -0,0 +1,25 @@ +plugins { + `kotlin-dsl` + `java-gradle-plugin` +} + +repositories { + this.mavenCentral() + this.mavenLocal() +} +dependencies { + this.implementation("com.google.code.gson:gson:2.10.1") + this.implementation("com.google.guava:guava:33.2.1-jre") +} + +sourceSets.main { + this.kotlin.srcDir(file("src")) +} +gradlePlugin { + this.plugins { + this.create("simplePlugin") { + this.id = "at.skyhanni.shared-variables" + this.implementationClass = "at.skyhanni.sharedvariables.NoOp" + } + } +} diff --git a/sharedVariables/settings.gradle.kts b/sharedVariables/settings.gradle.kts new file mode 100644 index 000000000..e3b5e4896 --- /dev/null +++ b/sharedVariables/settings.gradle.kts @@ -0,0 +1,2 @@ +// This space has been intentionally left blank +// It serves such that this folder is a new gradle workspace rather than being a subproject diff --git a/sharedVariables/src/MappingStyle.kt b/sharedVariables/src/MappingStyle.kt new file mode 100644 index 000000000..604495ce1 --- /dev/null +++ b/sharedVariables/src/MappingStyle.kt @@ -0,0 +1,6 @@ +package at.skyhanni.sharedvariables + +enum class MappingStyle(val identifier: String) { + SEARGE("srg"), + YARN("yarn"), +} diff --git a/sharedVariables/src/MinecraftVersion.kt b/sharedVariables/src/MinecraftVersion.kt new file mode 100644 index 000000000..7384c8fbd --- /dev/null +++ b/sharedVariables/src/MinecraftVersion.kt @@ -0,0 +1,25 @@ +package at.skyhanni.sharedvariables + +import org.gradle.jvm.toolchain.JavaLanguageVersion + +enum class MinecraftVersion( + val versionName: String, + val javaVersion: Int, +) { + MC189("1.8.9", 8), + MC11200("1.12", 8), + MC11202("1.12.2", 8), + MC1144("1.14.4", 8), + MC11605("1.16.5", 8), + MC121("1.21", 21), + ; + + val javaLanguageVersion = JavaLanguageVersion.of(javaVersion) + + val versionNumber = run { + val parts = versionName.split('.').mapTo(mutableListOf()) { it.toInt() } + if (parts.size == 2) parts.add(0) + require(parts.size == 3) + parts[0] * 10000 + parts[1] * 100 + parts[2] + } +} diff --git a/sharedVariables/src/MultiVersionStage.kt b/sharedVariables/src/MultiVersionStage.kt new file mode 100644 index 000000000..78914a91d --- /dev/null +++ b/sharedVariables/src/MultiVersionStage.kt @@ -0,0 +1,43 @@ +package at.skyhanni.sharedvariables + +import java.io.File +import java.util.Properties + +enum class MultiVersionStage(val label: String) { + OFF("off"), + PREPROCESS_ONLY("preprocess-only"), + FULL("compile") + ; + + fun shouldCompile(projectTarget: ProjectTarget): Boolean { + if (projectTarget == ProjectTarget.MAIN) return true + return when (this) { + OFF -> false + PREPROCESS_ONLY -> false + FULL -> projectTarget == ProjectTarget.MODERN + } + } + + fun shouldCreateProject(projectTarget: ProjectTarget): Boolean { + if (projectTarget == ProjectTarget.MAIN) return true + return when (this) { + OFF -> false + PREPROCESS_ONLY -> true + FULL -> true + } + } + + + companion object { + lateinit var activeState: MultiVersionStage + fun initFrom(file: File) { + val prop = Properties() + if (file.exists()) { + file.inputStream().use(prop::load) + } + val multiVersion = prop["skyhanni.multi-version"] + activeState = MultiVersionStage.values().find { it.label == multiVersion } ?: OFF + println("SkyHanni multi version stage loaded: $activeState") + } + } +} diff --git a/sharedVariables/src/NoOp.kt b/sharedVariables/src/NoOp.kt new file mode 100644 index 000000000..d0103a956 --- /dev/null +++ b/sharedVariables/src/NoOp.kt @@ -0,0 +1,11 @@ +package at.skyhanni.sharedvariables + +import org.gradle.api.Plugin +/** + * This class is a no op plugin. It can be applied to any project or gradle workspace and serves only as a marker + * for gradle to pull in the other classes in the sharedVariables project. We use a subproject rather than buildSrc + * since buildSrc is not available during settings configuration time (and also buildSrc tends to be slower). + */ +class NoOp : Plugin<Any> { + override fun apply(target: Any) {} +} diff --git a/sharedVariables/src/ProjectTarget.kt b/sharedVariables/src/ProjectTarget.kt new file mode 100644 index 000000000..9bf46c781 --- /dev/null +++ b/sharedVariables/src/ProjectTarget.kt @@ -0,0 +1,74 @@ +package at.skyhanni.sharedvariables + +private fun yarn(version: String): String = "net.fabricmc:yarn:${version}:v2" + +enum class ProjectTarget( + val projectName: String, + val minecraftVersion: MinecraftVersion, + val mappingDependency: String, + val mappingStyle: MappingStyle, + val forgeDep: String?, + linkTo: String?, +) { + MAIN( + "1.8.9", + MinecraftVersion.MC189, + "de.oceanlabs.mcp:mcp_stable:22-1.8.9@zip", + MappingStyle.SEARGE, + "net.minecraftforge:forge:1.8.9-11.15.1.2318-1.8.9", + "BRIDGE112", + ), + BRIDGE112( + "1.12.2", + MinecraftVersion.MC11202, + "de.oceanlabs.mcp:mcp_stable:39-1.12@zip", + MappingStyle.SEARGE, + "net.minecraftforge:forge:1.12.2-14.23.5.2847", + "BRIDGE116FORGE", + ), + BRIDGE116FORGE( + "1.16.5-forge", + MinecraftVersion.MC11605, + "official", + MappingStyle.SEARGE, + "net.minecraftforge:forge:1.16.5-36.2.39", + "BRIDGE116FABRIC", + ), + BRIDGE116FABRIC( + "1.16.5", + MinecraftVersion.MC11605, + yarn("1.16.5+build.10"), + MappingStyle.YARN, + null, + "MODERN", + ), + MODERN( + "1.21", + MinecraftVersion.MC121, + yarn("1.21+build.9"), + MappingStyle.YARN, + null, + null, + ) + ; + + val isBridge get() = name.contains("bridge") + + val linkTo by lazy { + if (linkTo == null) null + else { + ProjectTarget.values().find { it.name == linkTo }!! + } + } + val parent by lazy { + values().find { it.linkTo == this } + } + val isForge get() = forgeDep != null + val isFabric get() = forgeDep == null + + val projectPath get() = ":$projectName" + + companion object { + fun activeVersions() = values().filter { MultiVersionStage.activeState.shouldCreateProject(it) } + } +} diff --git a/sharedVariables/src/SHVersionInfo.kt b/sharedVariables/src/SHVersionInfo.kt new file mode 100644 index 000000000..2f948897e --- /dev/null +++ b/sharedVariables/src/SHVersionInfo.kt @@ -0,0 +1,12 @@ +package at.skyhanni.sharedvariables + +object SHVersionInfo { + val gitHash by lazy { + val proc = ProcessBuilder("git", "rev-parse", "--short", "HEAD") + .redirectOutput(ProcessBuilder.Redirect.PIPE) + .redirectInput(ProcessBuilder.Redirect.PIPE) + .start() + proc.waitFor() + proc.inputStream.readBytes().decodeToString().trim() + } +} diff --git a/sharedVariables/src/Util.kt b/sharedVariables/src/Util.kt new file mode 100644 index 000000000..750a73cbe --- /dev/null +++ b/sharedVariables/src/Util.kt @@ -0,0 +1,7 @@ +package at.skyhanni.sharedvariables + +import org.gradle.jvm.toolchain.JavaLanguageVersion + +fun JavaLanguageVersion.versionString() = + if (asInt() < 9) "1." + asInt() + else asInt().toString() diff --git a/src/main/java/at/hannibal2/skyhanni/config/core/config/gui/GuiPositionEditor.kt b/src/main/java/at/hannibal2/skyhanni/config/core/config/gui/GuiPositionEditor.kt index db6ca3c30..817defc5f 100644 --- a/src/main/java/at/hannibal2/skyhanni/config/core/config/gui/GuiPositionEditor.kt +++ b/src/main/java/at/hannibal2/skyhanni/config/core/config/gui/GuiPositionEditor.kt @@ -28,6 +28,8 @@ import at.hannibal2.skyhanni.mixins.transformers.gui.AccessorGuiContainer import at.hannibal2.skyhanni.utils.GuiRenderUtils import at.hannibal2.skyhanni.utils.KeyboardManager import at.hannibal2.skyhanni.utils.LorenzUtils.round +import at.hannibal2.skyhanni.utils.compat.GuiScreenUtils +import at.hannibal2.skyhanni.utils.compat.SkyhanniBaseScreen import net.minecraft.client.Minecraft import net.minecraft.client.gui.GuiScreen import net.minecraft.client.gui.ScaledResolution @@ -41,7 +43,7 @@ class GuiPositionEditor( private val positions: List<Position>, private val border: Int, private val oldScreen: GuiContainer? = null, -) : GuiScreen() { +) : SkyhanniBaseScreen() { private var grabbedX = 0 private var grabbedY = 0 @@ -147,15 +149,15 @@ class GuiPositionEditor( return hoveredPos } - private fun getScaledHeight() = ScaledResolution(Minecraft.getMinecraft()).scaledHeight - private fun getScaledWidth() = ScaledResolution(Minecraft.getMinecraft()).scaledWidth + private fun getScaledHeight() = GuiScreenUtils.scaledWindowHeight + private fun getScaledWidth() = GuiScreenUtils.scaledWindowHeight @Throws(IOException::class) override fun mouseClicked(originalX: Int, priginalY: Int, mouseButton: Int) { super.mouseClicked(originalX, priginalY, mouseButton) - val mouseX = Mouse.getX() * width / Minecraft.getMinecraft().displayWidth - val mouseY = height - Mouse.getY() * height / Minecraft.getMinecraft().displayHeight - 1 + val mouseX = GuiScreenUtils.mouseX + val mouseY = GuiScreenUtils.mouseY for (i in positions.indices.reversed()) { val position = positions[i] val elementWidth = position.getDummySize().x diff --git a/src/main/java/at/hannibal2/skyhanni/events/LorenzEvent.kt b/src/main/java/at/hannibal2/skyhanni/events/LorenzEvent.kt index f5566e278..c895b1058 100644 --- a/src/main/java/at/hannibal2/skyhanni/events/LorenzEvent.kt +++ b/src/main/java/at/hannibal2/skyhanni/events/LorenzEvent.kt @@ -13,7 +13,7 @@ import net.minecraftforge.common.MinecraftForge import net.minecraftforge.fml.common.eventhandler.Event import net.minecraftforge.fml.common.eventhandler.IEventListener -@Deprecated("Use SkyHanniEvent instead", ReplaceWith("")) +@Deprecated("Use SkyHanniEvent instead") abstract class LorenzEvent : Event() { private val eventName by lazy { diff --git a/src/main/java/at/hannibal2/skyhanni/features/misc/HideArmor.kt b/src/main/java/at/hannibal2/skyhanni/features/misc/HideArmor.kt index 112b59213..32365458f 100644 --- a/src/main/java/at/hannibal2/skyhanni/features/misc/HideArmor.kt +++ b/src/main/java/at/hannibal2/skyhanni/features/misc/HideArmor.kt @@ -10,11 +10,11 @@ import at.hannibal2.skyhanni.utils.EntityUtils.getArmorInventory import at.hannibal2.skyhanni.utils.EntityUtils.hasPotionEffect import at.hannibal2.skyhanni.utils.EntityUtils.isNPC import at.hannibal2.skyhanni.utils.LorenzUtils +import at.hannibal2.skyhanni.utils.compat.Effects import net.minecraft.client.entity.EntityPlayerSP import net.minecraft.entity.EntityLivingBase import net.minecraft.entity.player.EntityPlayer import net.minecraft.item.ItemStack -import net.minecraft.potion.Potion import net.minecraftforge.fml.common.eventhandler.SubscribeEvent @SkyHanniModule @@ -26,7 +26,7 @@ object HideArmor { private fun shouldHideArmor(entity: EntityLivingBase): Boolean { if (!LorenzUtils.inSkyBlock) return false if (entity !is EntityPlayer) return false - if (entity.hasPotionEffect(Potion.invisibility)) return false + if (entity.hasPotionEffect(Effects.invisibility)) return false if (entity.isNPC()) return false return when (config.mode) { diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinFontRenderer.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinFontRenderer.java index 3e6d049ca..1aa8c1daf 100644 --- a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinFontRenderer.java +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinFontRenderer.java @@ -79,7 +79,7 @@ public abstract class MixinFontRenderer { return ModifyVisualWords.INSTANCE.modifyText(text); } - @ModifyVariable(method = "getStringWidth", at = @At("HEAD"), argsOnly = true) + @ModifyVariable(method = "getStringWidth(Ljava/lang/String;)I", at = @At("HEAD"), argsOnly = true) private String getStringWidth(String text) { return ModifyVisualWords.INSTANCE.modifyText(text); } diff --git a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/gui/MixinGuiContainer.java b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/gui/MixinGuiContainer.java index acec9ffac..0c58469cd 100644 --- a/src/main/java/at/hannibal2/skyhanni/mixins/transformers/gui/MixinGuiContainer.java +++ b/src/main/java/at/hannibal2/skyhanni/mixins/transformers/gui/MixinGuiContainer.java @@ -71,6 +71,6 @@ public abstract class MixinGuiContainer extends GuiScreen { ) public void drawScreen_after(int mouseX, int mouseY, float partialTicks, CallbackInfo ci) { skyHanni$hook.onDrawScreenAfter(mouseX, mouseY, ci); - ToolTipData.INSTANCE.setLastSlot(theSlot); + ToolTipData.INSTANCE.setLastSlot(this.theSlot); } } diff --git a/src/main/java/at/hannibal2/skyhanni/utils/ChatUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/ChatUtils.kt index d0a3c199e..be61fc81c 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/ChatUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/ChatUtils.kt @@ -13,6 +13,7 @@ import at.hannibal2.skyhanni.utils.chat.Text.hover import at.hannibal2.skyhanni.utils.chat.Text.onClick import at.hannibal2.skyhanni.utils.chat.Text.prefix import at.hannibal2.skyhanni.utils.chat.Text.url +import at.hannibal2.skyhanni.utils.compat.getFormattedTextCompat import net.minecraft.client.Minecraft import net.minecraft.util.ChatComponentText import net.minecraft.util.ChatStyle @@ -82,7 +83,7 @@ object ChatUtils { } fun chat(message: IChatComponent): Boolean { - val formattedMessage = message.formattedText + val formattedMessage = message.getFormattedTextCompat() log.log(formattedMessage) val minecraft = Minecraft.getMinecraft() diff --git a/src/main/java/at/hannibal2/skyhanni/utils/EntityUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/EntityUtils.kt index 430310709..d123fa991 100644 --- a/src/main/java/at/hannibal2/skyhanni/utils/EntityUtils.kt +++ b/src/main/java/at/hannibal2/skyhanni/utils/EntityUtils.kt @@ -11,9 +11,15 @@ import at.hannibal2.skyhanni.utils.LocationUtils.distanceToIgnoreY import at.hannibal2.skyhanni.utils.LorenzUtils.baseMaxHealth import at.hannibal2.skyhanni.utils.LorenzUtils.derpy import at.hannibal2.skyhanni.utils.StringUtils.removeColor +import at.hannibal2.skyhanni.utils.compat.getArmorOrFullInventory +import at.hannibal2.skyhanni.utils.compat.getLoadedPlayers +import at.hannibal2.skyhanni.utils.compat.getNameAsString +import at.hannibal2.skyhanni.utils.compat.isOnMainThread +import at.hannibal2.skyhanni.utils.compat.normalizeAsArray import net.minecraft.block.state.IBlockState import net.minecraft.client.Minecraft import net.minecraft.client.entity.EntityOtherPlayerMP +import net.minecraft.client.multiplayer.WorldClient import net.minecraft.client.resources.DefaultPlayerSkin import net.minecraft.entity.Entity import net.minecraft.entity.EntityLivingBase @@ -26,8 +32,11 @@ import net.minecraft.potion.Potion import net.minecraft.scoreboard.ScorePlayerTeam import net.minecraft.util.AxisAlignedBB import net.minecraftforge.client.event.RenderLivingEvent + +//#if FORGE import net.minecraftforge.fml.common.eventhandler.Event import net.minecraftforge.fml.common.eventhandler.SubscribeEvent +//#endif @SkyHanniModule object EntityUtils { @@ -44,7 +53,7 @@ object EntityUtils { fun getPlayerEntities(): MutableList<EntityOtherPlayerMP> { val list = mutableListOf<EntityOtherPlayerMP>() - for (entity in Minecraft.getMinecraft().theWorld.playerEntities) { + for (entity in Minecraft.getMinecraft().theWorld?.getLoadedPlayers() ?: emptyList()) { if (!entity.isNPC() && entity is EntityOtherPlayerMP) { list.add(entity) } @@ -56,7 +65,7 @@ object EntityUtils { contains: String, radius: Double = 3.0, ): List<EntityArmorStand> = getArmorStandsInRadius(getLorenzVec().add(y = 3), radius).filter { - it.name.contains(contains) + it.getNameAsString().contains(contains) } fun EntityLivingBase.getNameTagWith( @@ -76,7 +85,7 @@ object EntityUtils { ): List<EntityArmorStand> { val center = getLorenzVec().add(y = y) return getArmorStandsInRadius(center, inaccuracy).filter { - val result = it.name.contains(contains) + val result = it.getNameAsString().contains(contains) if (debugWrongEntity && !result) { LorenzUtils.consoleLog("wrong entity in aabb: '" + it.name + "'") } @@ -139,31 +148,57 @@ object EntityUtils { fun EntityLivingBase.isAtFullHealth() = baseMaxHealth == health.toInt() fun EntityArmorStand.hasSkullTexture(skin: String): Boolean { - if (inventory == null) return false + val inventory = this.getArmorOrFullInventory() ?: return false return inventory.any { it != null && it.getSkullTexture() == skin } } fun EntityPlayer.isNPC() = !isRealPlayer() - fun EntityLivingBase.hasPotionEffect(potion: Potion) = getActivePotionEffect(potion) != null + fun EntityLivingBase.hasPotionEffect( + potion: + //#if MC <1.21 + Potion, + //#else + //$$ net.minecraft.registry.entry.RegistryEntry<net.minecraft.entity.effect.StatusEffect> + //#endif + ) = getActivePotionEffect(potion) != null fun EntityLivingBase.getArmorInventory(): Array<ItemStack?>? = - if (this is EntityPlayer) inventory.armorInventory else null + if (this is EntityPlayer) inventory.armorInventory.normalizeAsArray() else null fun EntityEnderman.getBlockInHand(): IBlockState? = heldBlockState inline fun <reified R : Entity> getEntities(): Sequence<R> = getAllEntities().filterIsInstance<R>() - fun getAllEntities(): Sequence<Entity> = Minecraft.getMinecraft()?.theWorld?.loadedEntityList?.let { - if (Minecraft.getMinecraft().isCallingFromMinecraftThread) it else it.toMutableList() + private fun WorldClient.getAllEntities(): Iterable<Entity> = +//#if MC < 1.14 + loadedEntityList +//#else +//$$ entitiesForRendering() +//#endif + + fun getAllEntities(): Sequence<Entity> = Minecraft.getMinecraft().theWorld?.getAllEntities()?.let { + if (Minecraft.getMinecraft() + .isOnMainThread() + ) it else it.toMutableList() // TODO: while i am here, i want to point out that copying the entity list does not constitute proper synchronization, but *does* make crashes because of it rarer. }?.asSequence()?.filterNotNull() ?: emptySequence() fun Entity.canBeSeen(radius: Double = 150.0) = getLorenzVec().add(y = 0.5).canBeSeen(radius) fun getEntityByID(entityId: Int) = Minecraft.getMinecraft()?.thePlayer?.entityWorld?.getEntityByID(entityId) +//#if FORGE + @SubscribeEvent - fun onEntityRenderPre(event: RenderLivingEvent.Pre<*>) { + fun onEntityRenderPre( + event: + //#if MC < 1.14 + RenderLivingEvent.Pre<*>, + //#else + //$$ RenderLivingEvent.Pre<*, *> + //#endif + + ) { val shEvent = SkyHanniRenderEntityEvent.Pre(event.entity, event.renderer, event.x, event.y, event.z) if (shEvent.postAndCatch()) { event.cancel() @@ -171,12 +206,24 @@ object EntityUtils { } @SubscribeEvent - fun onEntityRenderPost(event: RenderLivingEvent.Post<*>) { + fun onEntityRenderPost( + event: + //#if MC < 11400 + RenderLivingEvent.Post<*>, + //#else + //$$ RenderLivingEvent.Post<*, *> + //#endif + + ) { SkyHanniRenderEntityEvent.Post(event.entity, event.renderer, event.x, event.y, event.z).postAndCatch() } +//#if MC < 11400 @SubscribeEvent - fun onEntityRenderSpecialsPre(event: RenderLivingEvent.Specials.Pre<*>) { + fun onEntityRenderSpecialsPre( + event: + RenderLivingEvent.Specials.Pre<*>, + ) { val shEvent = SkyHanniRenderEntityEvent.Specials.Pre(event.entity, event.renderer, event.x, event.y, event.z) if (shEvent.postAndCatch()) { event.cancel() @@ -184,38 +231,47 @@ object EntityUtils { } @SubscribeEvent - fun onEntityRenderSpecialsPost(event: RenderLivingEvent.Specials.Post<*>) { + fun onEntityRenderSpecialsPost( + event: + RenderLivingEvent.Specials.Post<*>, + ) { SkyHanniRenderEntityEvent.Specials.Post(event.entity, event.renderer, event.x, event.y, event.z).postAndCatch() } +//#endif + +//#endif fun EntityLivingBase.isCorrupted() = baseMaxHealth == health.toInt().derpy() * 3 || isRunicAndCorrupt() fun EntityLivingBase.isRunic() = baseMaxHealth == health.toInt().derpy() * 4 || isRunicAndCorrupt() fun EntityLivingBase.isRunicAndCorrupt() = baseMaxHealth == health.toInt().derpy() * 3 * 4 - fun Entity.cleanName() = this.name.removeColor() + fun Entity.cleanName() = this.getNameAsString().removeColor() /** * @return a fake player with the same skin as the real player */ fun getFakePlayer(): EntityOtherPlayerMP { val mc = Minecraft.getMinecraft() + val player = mc.thePlayer!! return object : EntityOtherPlayerMP( mc.theWorld, - mc.thePlayer.gameProfile + player.gameProfile, ) { override fun getLocationSkin() = - mc.thePlayer.locationSkin ?: DefaultPlayerSkin.getDefaultSkin(mc.thePlayer.uniqueID) + player.getLocationSkin() ?: DefaultPlayerSkin.getDefaultSkin(player.uniqueID) override fun getTeam() = object : ScorePlayerTeam(null, null) { override fun getNameTagVisibility() = EnumVisible.NEVER } - override fun isWearing(part: EnumPlayerModelParts?) = - mc.thePlayer.isWearing(part) && part != EnumPlayerModelParts.CAPE + override fun isWearing(part: EnumPlayerModelParts): Boolean = + player.isWearing(part) && part != EnumPlayerModelParts.CAPE } } } +//#if FORGE private fun Event.cancel() { isCanceled = true } +//#endif diff --git a/src/main/java/at/hannibal2/skyhanni/utils/compat/ArrayNormalization.kt b/src/main/java/at/hannibal2/skyhanni/utils/compat/ArrayNormalization.kt new file mode 100644 index 000000000..2dde4bdc5 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/compat/ArrayNormalization.kt @@ -0,0 +1,6 @@ +package at.hannibal2.skyhanni.utils.compat + + +inline fun <reified T> List<T>.normalizeAsArray() = this.toTypedArray() +fun <T> Array<T>.normalizeAsArray() = this + diff --git a/src/main/java/at/hannibal2/skyhanni/utils/compat/GuiScreenUtils.kt b/src/main/java/at/hannibal2/skyhanni/utils/compat/GuiScreenUtils.kt new file mode 100644 index 000000000..0cec5aeb2 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/compat/GuiScreenUtils.kt @@ -0,0 +1,59 @@ +package at.hannibal2.skyhanni.utils.compat + +import net.minecraft.client.Minecraft +import net.minecraft.client.gui.ScaledResolution +import org.lwjgl.input.Mouse + +object GuiScreenUtils { + private val mc get() = Minecraft.getMinecraft() + val scaledWindowHeight + get() = +//#if MC < 1.16 + ScaledResolution(mc).scaledHeight +//#else +//$$ mc.window.guiScaledHeight +//#endif + + val scaledWindowWidth + get() = +//#if MC < 1.16 + ScaledResolution(mc).scaledWidth +//#else +//$$ mc.window.guiScaledWidth +//#endif + + val displayWidth + get() = +//#if MC < 1.16 + mc.displayWidth +//#else +//$$ mc.window.width +//#endif + + + val displayHeight + get() = +//#if MC < 1.16 + mc.displayHeight +//#else +//$$ mc.window.height +//#endif + + val globalMouseX get() = Mouse.getX() + val globalMouseY get() = Mouse.getY() + + val mouseX + get() = globalMouseX * scaledWindowWidth / displayWidth + val mouseY: Int + get() { + val height = this.scaledWindowHeight + //TODO: in later versions the height - factor is removed, i think + val y = globalMouseY * height / displayHeight +//#if MC < 1.16 + return height - y - 1 +//#else +//$$ return y +//#endif + } + +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/compat/Pattern.java b/src/main/java/at/hannibal2/skyhanni/utils/compat/Pattern.java new file mode 100644 index 000000000..f1e3a5f78 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/compat/Pattern.java @@ -0,0 +1,4 @@ +package at.hannibal2.skyhanni.utils.compat; + +public @interface Pattern { +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/compat/Pattterns.java b/src/main/java/at/hannibal2/skyhanni/utils/compat/Pattterns.java new file mode 100644 index 000000000..c8f6389a1 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/compat/Pattterns.java @@ -0,0 +1,15 @@ +package at.hannibal2.skyhanni.utils.compat; + +import net.minecraft.client.Minecraft; + +public class Pattterns { + + @Pattern + private static boolean isOnMainThread(Minecraft mc) { + //#if MC>=11400 + //$$ return mc.isOnThread(); + //#else + return mc.isCallingFromMinecraftThread(); + //#endif + } +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/compat/SkyhanniBaseScreen.kt b/src/main/java/at/hannibal2/skyhanni/utils/compat/SkyhanniBaseScreen.kt new file mode 100644 index 000000000..e9b8233ea --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/compat/SkyhanniBaseScreen.kt @@ -0,0 +1,10 @@ +package at.hannibal2.skyhanni.utils.compat + +import net.minecraft.client.gui.GuiScreen + +abstract class SkyhanniBaseScreen : GuiScreen( + //#if MC > 1.12 + //$$ net.minecraft.network.chat.TextComponent.EMPTY + //#endif +) { +} diff --git a/src/main/java/at/hannibal2/skyhanni/utils/compat/World.kt b/src/main/java/at/hannibal2/skyhanni/utils/compat/World.kt new file mode 100644 index 000000000..7916eb4e5 --- /dev/null +++ b/src/main/java/at/hannibal2/skyhanni/utils/compat/World.kt @@ -0,0 +1,63 @@ +package at.hannibal2.skyhanni.utils.compat + +import net.minecraft.client.Minecraft +import net.minecraft.client.multiplayer.WorldClient +import net.minecraft.entity.Entity +import net.minecraft.entity.item.EntityArmorStand +import net.minecraft.entity.player.EntityPlayer +import net.minecraft.potion.Potion +import net.minecraft.util.IChatComponent + +fun WorldClient.getLoadedPlayers(): List<EntityPlayer> = +//#if MC < 1.14 + this.playerEntities +//#else +//$$ this.players() +//#endif + + +fun Entity.getNameAsString(): String = + this.name +//#if MC >= 1.14 +//$$ .getString() +//#endif + +fun EntityArmorStand.getArmorOrFullInventory() = +//#if MC < 1.12 + this.inventory +//#else +//$$ this.getArmorInventoryList() +//#endif + +fun Minecraft.isOnMainThread() = +//#if MC < 1.14 + this.isCallingFromMinecraftThread +//#else +//$$ this.isSameThread +//#endif + +fun IChatComponent.getFormattedTextCompat() = +//#if MC < 1.16 + this.formattedText +//#else +//$$ run { +//$$ val sb = StringBuilder() +//$$ for (component in iterator()) { +//$$ sb.append(component.style.formattingCode) +//$$ sb.append(component.unformattedComponentText) +//$$ sb.append("§r") +//$$ } +//$$ sb.toString() +//$$ } +//#endif + +object Effects { + val invisibility = + //#if MC <1.12 + Potion.invisibility + //#else + //$$ net.minecraft.init.PotionTypes.INVISIBILITY + //#endif +} + + diff --git a/versions/mainProject b/versions/mainProject new file mode 100644 index 000000000..53ed4ba51 --- /dev/null +++ b/versions/mainProject @@ -0,0 +1 @@ +1.8.9 diff --git a/versions/mapping-1.12.2-1.8.9.txt b/versions/mapping-1.12.2-1.8.9.txt new file mode 100644 index 000000000..9ed58485b --- /dev/null +++ b/versions/mapping-1.12.2-1.8.9.txt @@ -0,0 +1,20 @@ + +net.minecraft.util.math.MathHelper net.minecraft.util.MathHelper +net.minecraft.util.math.BlockPos net.minecraft.util.BlockPos +net.minecraft.util.math.AxisAlignedBB net.minecraft.util.AxisAlignedBB +net.minecraft.util.math.Vec3d net.minecraft.util.Vec3 +net.minecraft.util.math.Vec3d net.minecraft.util.Vec3 +net.minecraft.util.math.Rotations net.minecraft.util.Rotations +net.minecraft.network.play.server.SPacketParticles net.minecraft.network.play.server.S2APacketParticles + +net.minecraft.util.text.TextComponentString net.minecraft.util.ChatComponentText +net.minecraft.util.text.TextComponentTranslation net.minecraft.util.ChatComponentTranslation +net.minecraft.util.text.Style net.minecraft.util.ChatStyle +net.minecraft.util.text.Style getHoverEvent() getChatHoverEvent() +net.minecraft.util.text.Style getClickEvent() getChatClickEvent() +net.minecraft.util.text.TextFormatting net.minecraft.util.EnumChatFormatting +net.minecraft.util.text.ITextComponent net.minecraft.util.IChatComponent +net.minecraft.util.text.event.HoverEvent net.minecraft.event.HoverEvent +net.minecraft.util.text.event.ClickEvent net.minecraft.event.ClickEvent + + diff --git a/versions/mapping-1.16.5-forge-1.12.2.txt b/versions/mapping-1.16.5-forge-1.12.2.txt new file mode 100644 index 000000000..58dd45107 --- /dev/null +++ b/versions/mapping-1.16.5-forge-1.12.2.txt @@ -0,0 +1,75 @@ +net.minecraft.world.level.block.state.BlockState net.minecraft.block.state.IBlockState +net.minecraft.client.player.RemotePlayer net.minecraft.client.entity.EntityOtherPlayerMP +net.minecraft.world.entity.decoration.ArmorStand net.minecraft.entity.item.EntityArmorStand +net.minecraft.world.entity.decoration.ArmorStand getArmorSlots() getArmorInventoryList() +net.minecraft.network.chat.TextComponent net.minecraft.util.text.TextComponentString +net.minecraft.client.gui.screens.inventory.ContainerScreen net.minecraft.client.gui.inventory.GuiContainer + +net.minecraft.world.entity.player.Inventory armor armorInventory + +net.minecraft.world.entity.monster.EnderMan net.minecraft.entity.monster.EntityEnderman +net.minecraft.world.entity.monster.EnderMan getCarriedBlock() getHeldBlockState() +net.minecraft.world.entity.player.PlayerModelPart net.minecraft.entity.player.EnumPlayerModelParts +net.minecraft.world.entity.player.Player net.minecraft.entity.player.EntityPlayer +net.minecraft.world.entity.player.Player isModelPartShown() isWearing() +net.minecraft.world.entity.Entity net.minecraft.entity.Entity +net.minecraft.world.entity.Entity xo posX +net.minecraft.world.entity.Entity yo posY +net.minecraft.world.entity.Entity zo posZ +net.minecraft.world.entity.Entity xOld prevPosX +net.minecraft.world.entity.Entity yOld prevPosY +net.minecraft.world.entity.Entity zOld prevPosZ +net.minecraft.world.entity.Entity level getEntityWorld() +net.minecraftforge.eventbus.api.Event net.minecraftforge.fml.common.eventhandler.Event +net.minecraftforge.eventbus.api.SubscribeEvent net.minecraftforge.fml.common.eventhandler.SubscribeEvent +com.mojang.blaze3d.platform.GlStateManager net.minecraft.client.renderer.GlStateManager +com.mojang.blaze3d.platform.GlStateManager _disableRescaleNormal() disableRescaleNormal() +com.mojang.blaze3d.platform.GlStateManager _viewport() viewport() +com.mojang.blaze3d.platform.GlStateManager _enableCull() enableCull() +com.mojang.blaze3d.platform.GlStateManager _disableCull() disableCull() +com.mojang.blaze3d.platform.GlStateManager _enableBlend() enableBlend() +com.mojang.blaze3d.platform.GlStateManager _disableBlend() disableBlend() +com.mojang.blaze3d.platform.GlStateManager _blendFunc() blendFunc() +com.mojang.blaze3d.platform.GlStateManager _depthFunc() depthFunc() +com.mojang.blaze3d.platform.GlStateManager _depthMask() depthMask() +com.mojang.blaze3d.platform.GlStateManager _enableLighting() enableLighting() +com.mojang.blaze3d.platform.GlStateManager _enableDepthTest() enableDepth() +com.mojang.blaze3d.platform.GlStateManager _disableDepthTest() disableDepth() +com.mojang.blaze3d.platform.GlStateManager _disableLighting() disableLighting() +com.mojang.blaze3d.platform.GlStateManager _pushMatrix() pushMatrix() +com.mojang.blaze3d.platform.GlStateManager _popMatrix() popMatrix() +com.mojang.blaze3d.platform.GlStateManager _bindTexture() bindTexture() +com.mojang.blaze3d.platform.GlStateManager _deleteTexture() deleteTexture() +com.mojang.blaze3d.platform.GlStateManager _clearColor() clearColor() +com.mojang.math.Matrix4f org.lwjgl.util.vector.Matrix4f +com.mojang.math.Matrix3f org.lwjgl.util.vector.Matrix3f +net.minecraft.client.util.math.Vector4f org.lwjgl.util.vector.Vector4f + +net.minecraft.world.level.Level getEntity() getEntityByID() +net.minecraft.world.scores.Team$Visibility net.minecraft.scoreboard.Team$EnumVisible + +net.minecraft.client.player.AbstractClientPlayer getSkinTextureLocation() getLocationSkin() + +net.minecraft.client.multiplayer.ClientLevel net.minecraft.client.multiplayer.WorldClient +net.minecraft.client.multiplayer.ClientLevel players() getPlayers() +net.minecraft.client.player.LocalPlayer net.minecraft.client.entity.EntityPlayerSP +net.minecraft.client.gui.Gui net.minecraft.client.gui.GuiIngame +net.minecraft.client.gui.components.ChatComponent net.minecraft.client.gui.GuiNewChat +net.minecraft.client.multiplayer.ClientPacketListener net.minecraft.client.network.NetHandlerPlayClient +net.minecraft.client.Options net.minecraft.client.settings.GameSettings +net.minecraft.client.renderer.texture.TextureManager register() loadTexture() +net.minecraft.client.renderer.texture.AbstractTexture load() loadTexture() +net.minecraft.client.renderer.texture.AbstractTexture getId() getGlTextureId() +net.minecraft.client.renderer.texture.AbstractTexture releaseId() deleteGlTexture() +net.minecraft.server.packs.resources.ResourceManager net.minecraft.client.resources.IResourceManager +net.minecraft.client.gui.screens.Screen net.minecraft.client.gui.GuiScreen +net.minecraft.client.gui.screens.Screen isPauseScreen() doesGuiPauseGame() +net.minecraft.client.Minecraft setScreen() displayGuiScreen() +net.minecraft.world.entity.LivingEntity net.minecraft.entity.EntityLivingBase +net.minecraft.world.entity.LivingEntity getEffect() getActivePotionEffect() +net.minecraft.world.entity.LivingEntity getMainHandItem() getHeldItemMainhand() +net.minecraft.world.entity.LivingEntity getOffhandItem() getHeldItemOffhand() +net.minecraft.world.entity.LivingEntity getItemBySlot() getItemStackFromSlot() +net.minecraft.world.entity.EquipmentSlot net.minecraft.inventory.EntityEquipmentSlot +net.minecraft.world.effect.MobEffect net.minecraft.potion.Potion + |