aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinnea Gräf <nea@nea.moe>2024-08-11 13:22:24 +0200
committerGitHub <noreply@github.com>2024-08-11 13:22:24 +0200
commit8903d9fa783455558d20eb32a2038c87ab5913be (patch)
tree2c925d0ecf625f8e8a5505960d0d51d2201e88e2
parenta8d4861b051298cc8a1db3c5210a32abab866b1e (diff)
downloadskyhanni-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>
-rw-r--r--.github/workflows/build.yml97
-rw-r--r--.github/workflows/generate-constants.yaml6
-rw-r--r--.gitignore1
-rw-r--r--CONTRIBUTING.md191
-rw-r--r--build.gradle.kts278
-rw-r--r--gradle.properties1
-rw-r--r--gradle/wrapper/gradle-wrapper.properties2
-rw-r--r--root.gradle.kts42
-rw-r--r--settings.gradle.kts24
-rw-r--r--sharedVariables/build.gradle.kts25
-rw-r--r--sharedVariables/settings.gradle.kts2
-rw-r--r--sharedVariables/src/MappingStyle.kt6
-rw-r--r--sharedVariables/src/MinecraftVersion.kt25
-rw-r--r--sharedVariables/src/MultiVersionStage.kt43
-rw-r--r--sharedVariables/src/NoOp.kt11
-rw-r--r--sharedVariables/src/ProjectTarget.kt74
-rw-r--r--sharedVariables/src/SHVersionInfo.kt12
-rw-r--r--sharedVariables/src/Util.kt7
-rw-r--r--src/main/java/at/hannibal2/skyhanni/config/core/config/gui/GuiPositionEditor.kt12
-rw-r--r--src/main/java/at/hannibal2/skyhanni/events/LorenzEvent.kt2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/features/misc/HideArmor.kt4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/mixins/transformers/MixinFontRenderer.java2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/mixins/transformers/gui/MixinGuiContainer.java2
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/ChatUtils.kt3
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/EntityUtils.kt90
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/compat/ArrayNormalization.kt6
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/compat/GuiScreenUtils.kt59
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/compat/Pattern.java4
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/compat/Pattterns.java15
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/compat/SkyhanniBaseScreen.kt10
-rw-r--r--src/main/java/at/hannibal2/skyhanni/utils/compat/World.kt63
-rw-r--r--versions/mainProject1
-rw-r--r--versions/mapping-1.12.2-1.8.9.txt20
-rw-r--r--versions/mapping-1.16.5-forge-1.12.2.txt75
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
+