diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index a190fb110..000000000 --- a/.dockerignore +++ /dev/null @@ -1,19 +0,0 @@ -**/.git -**/.gitlab -**/.cache - -buildAllJars - -**/_Misc Files -*.bat -*.md -*.sh -*.txt - -coreSubProjects/*.md -coreSubProjects/*.txt - -**/.gitignore -**/.gitattributes -**/.gitlab-cy.yml -**/.gitmodules diff --git a/.gitignore b/.gitignore index 1d9b3b01b..71dbf2226 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,8 @@ hs_err_pid* Merged/ # Folder created by the buildAll scripts buildAllJars/ +_buildAllJars/ +_buildWorkers/ relocate_natives/.venv/ relocate_natives/__pycache__/ diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 34252342f..540b56661 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -1,6 +1,6 @@ # use Eclipse's JDK # The ci should always use a unix(-like) OS to work -image: eclipse-temurin:21 +image: eclipse-temurin:25 # all stages need to be defined here stages: @@ -35,6 +35,7 @@ build: parallel: matrix: - MC_VER: [ + "26.1.2", "1.21.11", "1.21.10", "1.21.9", "1.21.8", "1.21.6", "1.21.5", "1.21.4", "1.21.3", "1.21.1", "1.20.6", "1.20.4", "1.20.2", "1.20.1", "1.19.4", "1.19.2", @@ -46,8 +47,7 @@ build: # this both runs the unit tests and assembles the code - ./gradlew clean -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/; - ./gradlew build -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/; - - ./gradlew mergeJars -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/; - - cp ./fabric/build/libs/* ./forge/build/libs/* ./neoforge/build/libs/* ./build/merged/* . || true + - cp ./fabric/build/libs/* ./forge/build/libs/* ./neoforge/build/libs/* ./build/forgix/* . || true artifacts: name: "NightlyBuild_${MC_VER}-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}" paths: diff --git a/.mailmap b/.mailmap new file mode 100644 index 000000000..78e4f9642 --- /dev/null +++ b/.mailmap @@ -0,0 +1,16 @@ +# See mailmap docs: https://git-scm.com/docs/gitmailmap +# Test with git shortlog --summary --email +# Keep sorted for easier editing and smaller diffs + +Ada Aster +CodeF53 <37855219+CodeF53@users.noreply.github.com> +coolGi +James Seibel +Morippi +Morippi +Ran <43445785+Ran-Mewo@users.noreply.github.com> <10044908-_Ran@users.noreply.gitlab.com> +Ran <43445785+Ran-Mewo@users.noreply.github.com> <43445785+Ran-Mewo@users.noreply.github.com> +Ran <43445785+Ran-Mewo@users.noreply.github.com> <43445785+RanCraftPlayz@users.noreply.github.com> +TomTheFurry +TomTheFurry <46843632+TomTheFurry@users.noreply.github.com> +Yeshi diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index 8df81edfb..000000000 --- a/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM eclipse-temurin:17-jdk - -WORKDIR /home/build/ -COPY ./gradlew . -RUN chmod +x ./gradlew -CMD echo "\r========== [CLEAN: $MC_VER] ==========" && \ - ./gradlew clean -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \ - echo "\r========== [BUILD: $MC_VER] ==========" && \ - ./gradlew build -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \ - echo "\r========== [MERGE: $MC_VER] ==========" && \ - ./gradlew mergeJars -PmcVer="$MC_VER" --gradle-user-home .gradle-cache/ && \ - echo "\r========== [DONE: $MC_VER] ==========" diff --git a/Readme.md b/Readme.md index fcdcee7f4..10bc40634 100644 --- a/Readme.md +++ b/Readme.md @@ -14,89 +14,11 @@ Below is a video demonstrating the system:
-## Minecraft and Library Versions - -### This branch supports the following versions of Minecraft: - -#### 1.20.4, 1.20.3 (Default) -Fabric: 0.15.1\ -Fabric API: 0.91.2+1.20.4\ -Forge: 49.0.30\ -NeoForge: 118-beta\ -Parchment: 1.20.2:2023.12.10\ -Modmenu: 9.0.0-pre.1 - -#### 1.20.2 -Fabric: 0.14.24\ -Fabric API: 0.90.4+1.20.2\ -Forge: 48.0.13\ -Parchment: 1.20.1:2023.09.03\ -Modmenu: 8.0.0 - -#### 1.20.1, 1.20 -Fabric: 0.14.24\ -Fabric API: 0.90.4+1.20.1\ -Forge: 47.2.1\ -Parchment: 1.20.1:2023.09.03\ -Modmenu: 7.2.2 - -#### 1.19.4 -Fabric: 0.14.24\ -Fabric API: 0.87.1+1.19.4\ -Forge: 45.2.4\ -Parchment: 1.19.4:2023.06.26\ -Modmenu: 6.3.1 - -#### 1.19.2 -Fabric: 0.14.24\ -Fabric API: 0.76.1+1.19.2\ -Forge: 43.3.2\ -Parchment: 1.19.2:2022.11.27\ -Modmenu: 4.2.0-beta.2 - -#### 1.18.2 -Fabric: 0.14.24\ -Fabric API: 0.76.0+1.18.2\ -Forge: 40.2.10\ -Parchment: 1.18.2:2022.11.06\ -Modmenu: 3.2.5 - -#### 1.17.1, 1.17 -Fabric: 0.14.24\ -Fabric API: 0.46.1+1.17\ -Forge: 37.1.1\ -Parchment: 1.17.1:2021.12.12\ -Modmenu: 2.0.14 - -#### 1.16.5, 1.16.4 -Fabric: 0.14.24\ -Fabric API: 0.42.0+1.16\ -Forge: 36.2.39\ -Parchment: 1.16.5:2022.03.06\ -Modmenu: 1.16.22 - -### Versions no longer supported -- 1.18.1, 1.18 -- 1.19.1, 1.19 -- 1.19.3 - -
- -### Plugin and Library versions - -Gradle: 8.5\ -Fabric loom: 1.4-SNAPSHOT\ -Architectury loom (Forge gradle replacement): 1.4-SNAPSHOT\ -Sponge vanilla gradle: 0.2.1-SNAPSHOT\ -Java Preprocessor plugin: Manifold Preprocessor - -
- ## Source Code Installation ### Prerequisites -* A Java Development Kit (JDK) for Java 17 (recommended) or newer.
+* A Java Development Kit (JDK) for Java 25 (recommended) or newer.
Visit https://www.oracle.com/java/technologies/downloads/ for installers. * Git or someway to clone git projects.
Visit https://git-scm.com/ for installers. @@ -104,15 +26,10 @@ Java Preprocessor plugin: Manifold Preprocessor **If using IntelliJ:** 1. Install the Manifold plugin + - https://plugins.jetbrains.com/plugin/10057-manifold-ij 2. Open IDEA and import the build.gradle 3. Refresh the Gradle project in IDEA if required -**If using Eclipse: (Note that Eclipse doesn't support Manifold's preprocessor!)** -1. Run the command: `./gradlew geneclipseruns` -2. Run the command: `./gradlew eclipse` -3. Make sure eclipse has the JDK 17 installed. (This is needed so that eclipse can run minecraft) -4. Import the project into eclipse -
## Switching Versions @@ -127,72 +44,42 @@ In IntelliJ, you will also need to do a gradle sync if it didn't happen automati ## Compiling Prerequisites: -- JDK 17 or newer - -From the File Explorer: -1. Download and extract the project zip -2. Download the core from https://gitlab.com/distant-horizons-team/distant-horizons-core and extract into a folder called `coreSubProjects` -3. Open a terminal emulator in the project folder (On Windows you can type `cmd` in the title bar) -4. Run the commands: `./gradlew assemble` (You may need to use a `.\` on Windows) -5. Merge the jars with `./gradlew mergeJars` -6. The compiled jar file will be in the folder `Merged` +- JDK 25 or newer From the command line: 1. `git clone --recurse-submodules https://gitlab.com/distant-horizons-team/distant-horizons.git` 2. `cd distant-horizons` 3. `./gradlew assemble` -4. `./gradlew mergeJars` -5. The compiled jar file will be in the folder `Merged` +5. The compiled jar file will be in the folder `\build\libs` -Run tests with: `./gradlew test` +From the File Explorer: +1. Download and extract the project zip +2. Download the core from https://gitlab.com/distant-horizons-team/distant-horizons-core and extract into a folder called `coreSubProjects` +3. Open command prompt/terminal in the project folder +4. Run the commands: `./gradlew assemble` +6. The compiled jar file will be in the folder `\build\libs` >Note: You can add the argument `-PmcVer=?` to tell gradle to build a selected MC version instead of having to modify the `gradle.properties` file.\ > For example: `./gradlew assemble -PmcVer=1.18.2`
-## Compiling with Docker - -`./compile ` - -You can also locally compile the DH jars without a Java environment by using Docker. Where `` is the version of Minecraft to compile for (ie `1.20.1`), or the keyword `all`. See [Versions](#minecraft-and-library-versions) for a list of all supported values. - -
- ## Other commands -`./gradlew --refresh-dependencies` to refresh local dependencies. - -`./gradlew clean` to delete any compiled code. - -
- -## Note to self - -The Minecraft source code is NOT added to your workspace in an editable way. Minecraft is treated like a normal Library. Sources are there for documentation and research purposes only. - -Source code uses Mojang mappings & [Parchment](https://parchmentmc.org/) mappings. - -To generate the source code run `./gradlew genSources`
-If your IDE fails to auto-detect the source jars when browsing Minecraft classes; manually select the JAR file ending with -sources.jar when prompted by your IDE.
-(In IntelliJ it's at the top where it says "choose sources" when browsing a Minecraft class) - -
- -## Other Useful commands - Run the standalone jar: `./gradlew run`
Build the standalone jar: `./gradlew core:build`
Only build Fabric: `./gradlew fabric:assemble` or `./gradlew fabric:build`
Only build Forge: `./gradlew forge:assemble` or `./gradlew forge:build`
Run the Fabric client (for debugging): `./gradlew fabric:runClient`
Run the Forge client (for debugging): `./gradlew forge:runClient`
+Delete all compiled code: `./gradlew clean`
+Refresh local dependencies: `./gradlew --refresh-dependencies` -To build all versions: `./buildAll` (all builds will end up in the `Merged` folder) +To build all versions: `./buildAll`
-## Open Source Acknowledgements +## Open Source Libraries Forgix (To merge multiple mod versions into one jar) [_Formerly_ [_DHJarMerger_](https://github.com/Ran-helo/DHJarMerger)]\ https://github.com/PacifistMC/Forgix @@ -208,3 +95,18 @@ https://github.com/blackears/svgSalamander sqlite-jdbc\ https://github.com/xerial/sqlite-jdbc + + +## Acknowledgements + +Distant Horizons has been graciously provided an open source license for YourKit Java Profiler. + +> +> +> YourKit supports open source projects with innovative and intelligent tools +for monitoring and profiling Java and .NET applications. +YourKit is the creator of YourKit Java Profiler, +YourKit .NET Profiler, +and YourKit YouMonitor. + + diff --git a/build.gradle b/build.gradle index 78bb4b2ce..982ccf12c 100644 --- a/build.gradle +++ b/build.gradle @@ -1,715 +1,28 @@ -import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer -import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext -import org.apache.tools.zip.ZipEntry - -import javax.annotation.Nonnull -import org.apache.tools.zip.ZipOutputStream - -import java.util.function.Function -import java.util.function.Predicate - -buildscript { - configurations.configureEach { - resolutionStrategy.eachDependency { details -> - if (details.requested.group == "org.apache.commons" && details.requested.name == "commons-compress") { - details.useVersion("1.28.0") - details.because("Needed because of Cleanroom. net.minecraftforge:DiffPatch:2.0.7 from dev.architectury.loom pulls ancient commons-compress 1.18") - } - - } - } -} - plugins { - id "java" - - // Plugin to put dependencies inside our final jar - id "com.github.johnrengelman.shadow" version '8.1.1' apply false - - // Plugin to create merged jars - id "io.github.pacifistmc.forgix" version "1.3.4" - - // Manifold preprocessor - id "systems.manifold.manifold-gradle-plugin" version "0.0.2-alpha" - - // Architectury is used here only as a replacement for forge's own loom - id "dev.architectury.loom" version "1.13-SNAPSHOT" apply false - - // Plugin used for 1.12.2 - id 'xyz.wagyourtail.unimined' version '1.4.10-kappa' apply false - - id 'org.spongepowered.gradle.vanilla' version '0.2.1-20240507.024226-82' apply false + id 'root' + id 'io.github.pacifistmc.forgix' version '2.+' } -/** - * Creates the list of preprocessors to use. - * - * @param mcVers array of all MC versions - * @param mcIndex array index of the currently active MC version - */ -def writeBuildGradlePredefine(List mcVers, int mcIndex) -{ - // Build the list of preprocessors to use - StringBuilder sb = new StringBuilder(); - - sb.append("# DON'T TOUCH THIS FILE, This is handled by the build script\n"); +forgix { + autoRun = true - - for (int i = 0; i < mcVers.size(); i++) - { - String verStr = mcVers[i].replace(".", "_"); - sb.append("MC_" + verStr + "=" + i.toString() + "\n"); - - if (mcIndex == i) - { - sb.append("MC_VER=" + i.toString() + "\n"); - } - } - - - // Check if this is a development build - if (mod_version.toLowerCase().contains("dev")) - { - // WARNING: only use this for logging, we don't want to have confusion - // when a method doesn't work correctly in the release build. - sb.append("DEV_BUILD=\n"); - } - - new File(projectDir, "build.properties").text = sb.toString() -} - - -// Transfers the values set in settings.gradle to the rest of the project -project.gradle.ext.getProperties().each { prop -> - rootProject.ext.set(prop.key, prop.value) - //println "Added prop [key:" + prop.key + ", value:" + prop.value + "]" -} -// Sets up manifold stuff -writeBuildGradlePredefine(rootProject.mcVers, rootProject.mcIndex) - -// Sets up the version string (the name we use for our jar) -rootProject.versionStr = rootProject.mod_version + "-" + rootProject.minecraft_version // + "-" + new Date().format("yyyy_MM_dd_HH_mm") - -class NativeTransformer implements Transformer { - private Predicate fileMatcher - private Function filePathMapper - - private final HashMap replacements = new HashMap() - private final HashMap rewrittenFiles = new HashMap() - private var nativeRelocator - - public File rootDir - - void matchFiles(Predicate matcher) { - fileMatcher = matcher - } - - void mapPaths(Function mapper) { - filePathMapper = mapper - } - - void relocateNative(String target, String replacement) { - if (replacement.length() > target.length()) { - throw new GradleException("Length of value \"${replacement}\" exceeds the length of \"${target}\": ${replacement.length()} > ${target.length()}") - } - - replacements.put(target, replacement) - } - - @Override - boolean canTransformResource(@Nonnull FileTreeElement element) { - return fileMatcher.test(element.name) - } - - @Override - void transform(@Nonnull TransformerContext context) { - byte[] content = context.is.readAllBytes() - - if (nativeRelocator == null) { - nativeRelocator = new NativeRelocator(rootDir.toPath().resolve("relocate_natives")) - } - - try { - String path = filePathMapper != null - ? filePathMapper.apply(context.path) - : context.path - content = nativeRelocator.processBinary(path, content, replacements) - - rewrittenFiles.put(path, content) - } - catch (Throwable e) { - throw new GradleException("Failed to relocate", e) - } - } - - @Override - boolean hasTransformedResource() { return !rewrittenFiles.isEmpty() } - - @Override - void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) { - for (Map.Entry rewrittenFile : rewrittenFiles.entrySet()) { - os.putNextEntry(new ZipEntry(rewrittenFile.key)) - os.write(rewrittenFile.value) - } - } -} - -subprojects { p -> - // Does the same as "p == project(":common") || p == project(":fabric") || p == project(":quilt") || p == project(":forge") || p == project("WhateverWeAddLaterOn")" - // Useful later on so we dont have duplicated code - def isMinecraftSubProject = p != project(":core") && p != project(":api") - - - // Apply plugins - apply plugin: "java" - apply plugin: "com.github.johnrengelman.shadow" - if (isMinecraftSubProject) - apply plugin: "systems.manifold.manifold-gradle-plugin" - - - if (rootProject.minecraft_version == "1.12.2" && p == project(":common")) { - p.afterEvaluate { - def cleanroomProject = project(":cleanroom") - cleanroomProject.afterEvaluate { - p.dependencies { - compileOnly p.files(cleanroomProject.configurations.minecraftLibraries.files) - compileOnly p.files(cleanroomProject.configurations.forgeRuntimeLibrary.files) - compileOnly p.files(cleanroomProject.configurations.minecraft.files) - } + // add the mod loaders to the end of the jar + // put together in the format: "a", "a-b", "a-b-c" + String modLoaders = ""; + ((String) gradle.builds_for) + .split(",") + .each + { loader -> + def loaderName = loader.trim() + if (modLoaders != "") + { + modLoaders += "-"; } - } - } - - // Apply forge's loom - if ((findProject(":forge") && p == project(":forge")) || - (findProject(":neoforge") && p == project(":neoforge")) - ) - { - apply plugin: "dev.architectury.loom" - } - - - // Set the manifold version (may not be required tough) - manifold { - manifoldVersion = rootProject.manifold_version - } - - - // set up custom configurations (configurations are a way to handle dependencies) - configurations { - // extends the shadowJar configuration - shadowMe - // have implemented dependencies automatically embedded in the final jar - implementation.extendsFrom(shadowMe) - - // Configuration fpr core & api - coreProjects - shadowMe.extendsFrom(coreProjects) - - - // FIXME this additional configuration is necessary because forge - // needs forgeRuntimeLibrary, although adding it to shadowMe - // causes runtime issues where the libraries aren't properly added - forgeShadowMe - // this should match shadowMe pretty closely - implementation.extendsFrom(forgeShadowMe) - shadowMe.extendsFrom(forgeShadowMe) - forgeRuntimeLibrary.extendsFrom(forgeShadowMe) - shadow.extendsFrom(forgeShadowMe) - - - if (isMinecraftSubProject && p != project(":common")) { - // Shadow common - common - shadowCommon // Don't use shadow from the shadow plugin because we don't want IDEA to index this. - compileClasspath.extendsFrom common - runtimeClasspath.extendsFrom common - if (findProject(":forge")) - developmentForge.extendsFrom common - if (findProject(":neoforge")) - developmentNeoForge.extendsFrom common - compileClasspath.extendsFrom coreProjects - runtimeClasspath.extendsFrom coreProjects - if (findProject(":forge")) - developmentForge.extendsFrom coreProjects - if (findProject(":neoforge")) - developmentNeoForge.extendsFrom coreProjects - } - } - - - dependencies { - //=====================// - // shared dependencies // - //=====================// - - // Manifold - if (isMinecraftSubProject) { - annotationProcessor("systems.manifold:manifold-preprocessor:${rootProject.manifold_version}") - } - - // Log4j - if (p == project(":core")) - { - // the standalone core jar needs logging shaded otherwise it won't run - forgeShadowMe("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}") - forgeShadowMe("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}") - } - else - { - // When running in MC, MC already includes logging - implementation("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}") - implementation("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}") - } - - - // JOML - if (project.hasProperty("embed_joml") && embed_joml == "true") - forgeShadowMe("org.joml:joml:${rootProject.joml_version}") - else - implementation("org.joml:joml:${rootProject.joml_version}") - - // JUnit tests - implementation("org.junit.jupiter:junit-jupiter:5.8.2") - implementation("org.junit.jupiter:junit-jupiter-engine:5.8.2") - implementation("junit:junit:4.13") - - // FastUtil - // Note: MC 1.16 uses 8.2.1, and versions after use 8.5.12 - // We cannot relocate this library since we call some MC classes that reference it - implementation("it.unimi.dsi:fastutil:${rootProject.fastutil_version}") - - forgeShadowMe("com.github.luben:zstd-jni:${rootProject.zstd_version}") - - // Compression - forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}") // LZ4 - forgeShadowMe("org.tukaani:xz:${rootProject.xz_version}") // LZMA - - // Sqlite Database - forgeShadowMe("org.xerial:sqlite-jdbc:${rootProject.sqlite_jdbc_version}") - - // NightConfig (includes Toml & Json) - forgeShadowMe("com.electronwill.night-config:toml:${rootProject.nightconfig_version}") - forgeShadowMe("com.electronwill.night-config:json:${rootProject.nightconfig_version}") - - // SVG (not needed atm) -// forgeShadowMe("com.formdev:svgSalamander:${rootProject.svgSalamander_version}") - - // Netty - implementation("io.netty:netty-buffer:${rootProject.netty_version}") - - - - //==========================// - // conditional dependencies // - //==========================// - - - // Add core - if (isMinecraftSubProject) { - coreProjects(project(":core")) { - // Remove Junit test libraries - exclude group: "org.junit.jupiter", module: "junit-jupiter" - exclude group: "org.junit.jupiter", module: "junit-jupiter-engine" - exclude group: "junit", module: "junit" - // Removed dependencies - transitive false - } - } - - // Add the api - if (p != project(":api")) { - coreProjects(project(":api")) { - // Remove Junit test libraries - exclude group: "org.junit.jupiter", module: "junit-jupiter" - exclude group: "org.junit.jupiter", module: "junit-jupiter-engine" - exclude group: "junit", module: "junit" - // Removed dependencies - transitive false - } - } - - // Add common - if (isMinecraftSubProject && p != project(":common")) { - // Common - common(project(":common")) { transitive false } - shadowCommon(project(":common")) { transitive false } - } - } - - - shadowJar { - configurations = [project.configurations.shadowMe] - if (isMinecraftSubProject && p != project(":common")) { - configurations.push(project.configurations.shadowCommon) // Shadow the common subproject - relocate "com.seibel.distanthorizons.common", "loaderCommon.${p.name}.com.seibel.distanthorizons.common" // Move the loader files to a different location - } - def librariesLocation = "DistantHorizons.libraries" - - // Compression (LZ4) - relocate "net.jpountz", "${librariesLocation}.jpountz" - - // Logging - relocate "org.slf4j", "${librariesLocation}.slf4j" - - // Sqlite Database - // librariesLocation isn't used because it's too long for replacing paths in native libraries - // Allowing strings larger than the original string would require shifting the entire binary's contents - relocate "org.sqlite", "dh_sqlite", { - exclude "org/sqlite/native/**" - } - relocate "jdbc:sqlite", "jdbc:dh_sqlite" - - transform(NativeTransformer) { - rootDir = project.rootDir - - matchFiles { it.startsWith("org/sqlite") } - mapPaths { it.replace("org/sqlite", "dh_sqlite") } - - relocateNative "org/sqlite", "dh_sqlite" - relocateNative "org_sqlite", "dh_1sqlite" - } - - // ZStd - // librariesLocation isn't used because it's too long for replacing paths in native libraries - // Allowing strings larger than the original string would require shifting the entire binary's contents - relocate "com.github.luben", "dhcomgithubluben" - relocate "libzstd-jni", "libzstd-jni_dh" - relocate "zstd-jni", "zstd-jni_dh" - - transform(NativeTransformer) { - rootDir = project.rootDir - - matchFiles { it.contains("libzstd-jni") && !it.contains("aix/ppc64") } - mapPaths { it.replace("libzstd-jni", "libzstd-jni_dh") } - - relocateNative "com/github/luben", "dhcomgithubluben" - relocateNative "com_github_luben", "dhcomgithubluben" - } - - - // JOML - if (project.hasProperty("embed_joml") && embed_joml == "true") - relocate "org.joml", "${librariesLocation}.joml" - - // NightConfig (includes Toml & Json) - relocate "com.electronwill.nightconfig", "${librariesLocation}.electronwill.nightconfig" - - // Netty - // Don't relocate, it causes problems with using MC's FriendlyByteBufs -// relocate "io.netty", "${librariesLocation}.netty" - - mergeServiceFiles() - } - // Using jar.finalizedBy(shadowJar) causes issues so we do this scuffed bypass - jar.dependsOn(shadowJar) - - - // Put stuff from gradle.properties into the mod info - // Note: these resources are only included in the mod jars, the core and API jars don't include these files - processResources { - def resourceTargets = [ // Location of where to inject the properties - // Holds info like git commit - "build_info.json", - - // Properties for each of the loaders - "fabric.mod.json", - "quilt.mod.json", - "META-INF/mods.toml", - "META-INF/neoforge.mods.toml", - - // The mixins for each of the loaders - //"DistantHorizons."+ p.name +".fabricLike.mixins.json" - ] - def intoTargets = ["$buildDir/resources/main/"] // Location of the built resources folder - - // Fix forge version numbering system as it is weird - // For whatever reason forge uses [1.18, 1.18.1, 1.18.2) instead of the standard ["1.18", "1.18.1", "1.18.2"] - def compatible_forgemc_versions = "${compatible_minecraft_versions}".replaceAll("\"", "").replaceAll("]", ",)") -// println compatible_forgemc_versions - - // Quilt's custom contributors system - // This has to be like - // "Person": "Developer", "Another person": "Developer" - def quilt_contributors = [] - def mod_author_list = mod_authors.replaceAll("\"", "").replace("[", "").replace("]", "").split(",") - for (dev in mod_author_list) { - quilt_contributors.push("\"${dev.strip()}\": \"Developer\"") - } - quilt_contributors.reverse() - //println quilt_contributors.join(", ") - - - // These "hasProperty"'s are so that they can be passed through the cli (ie in the CI) - try { - if (infoGitCommit == "null") - infoGitCommit = 'git rev-parse --verify HEAD'.execute().text.trim() - if (infoGitBranch == "null") - infoGitBranch = 'git symbolic-ref --short HEAD'.execute().text.trim() - } catch (Exception e) { - infoGitCommit = infoGitBranch = "Git not found" - println "Git or Git project not found" - } - - // The left side is what gets replaced in the mod info and the right side is where to get it from in the gradle.properties - def replaceProperties = [ - version : mod_version, - mod_name : mod_readable_name, - group : maven_group, - authors : mod_authors, - description : mod_description, - homepage : mod_homepage, - source : mod_source, - issues : mod_issues, - discord : mod_discord, - minecraft_version : minecraft_version, - compatible_minecraft_versions: compatible_minecraft_versions, - compatible_forgemc_versions : compatible_forgemc_versions, - java_version : java_version, - quilt_contributors : "{"+quilt_contributors.join(", ")+"}", - - info_git_commit : infoGitBranch, - info_git_branch : infoGitCommit, - info_build_source : infoBuildSource, - - fabric_incompatibility_list : fabric_incompatibility_list, - fabric_recommend_list : fabric_recommend_list, - - neoforge_version_range : neoforge_version_range, - ] - - // replace any properties in the sub-projects with the values defined here - inputs.properties replaceProperties - replaceProperties.put "project", project - filesMatching(resourceTargets) { - expand replaceProperties - } - - intoTargets.each { target -> - if (file(target).exists()) { - copy { - from(sourceSets.main.resources) { - include resourceTargets - expand replaceProperties - } - into target - } - } - } - - - - // ==================== Delete un-needed files ==================== - // Jank solution to remove all unused accesswideners - // (neo)forge (well, mainly architectury) requires the original accesswidener file, meaning we require this jank solution to keep it - exclude { file -> - if (file.name.contains(".distanthorizons.accesswidener") && file.name != "${accessWidenerVersion}.distanthorizons.accesswidener") { - return true - } - return false - } - } - - - // Adds the standalone jar's entrypoint - jar { - from "LICENSE.txt" - manifest { - attributes( - 'Implementation-Title': rootProject.mod_name, - 'Implementation-Version': rootProject.mod_version, - 'Multi-Release': true, // needed for logging in the standalone core jar - 'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain', // When changing the main of the jar change this line - ) - } - } - - // this can be un-commented if we ever wanted to make DH modular (AKA use a module-info.java file) again - /* - // Tells gradle where to look for other modules - // Why isn't the classpath added to the modules path by default? - if (p == project(":core")) { - compileJava { - inputs.property('moduleName', 'dhApi') - doFirst { - options.compilerArgs = [ - '--module-path', classpath.asPath - ] - classpath = files() - } - } - } - */ -} - -allprojects { p -> - // Does the same as "p == project(":common") || p == project(":fabric") || p == project(":quilt") || p == project(":forge") || p == project("WhateverWeAddLaterOn")" - // Useful later on so we dont have duplicated code - def isMinecraftSubProject = p != project(":core") && p != project(":api") - - - apply plugin: "java" - apply plugin: "maven-publish" - - // Sets the name of the jar, the version will contain the name of the project if it isn't the root project - archivesBaseName = rootProject.mod_name - version = (project == rootProject ? "" : project.name + "-") + rootProject.versionStr - group = rootProject.maven_group - - // this is the text that appears at the top of the overview (home) page - // and is used when bookmarking a page - javadoc.title = rootProject.mod_name + "-" + project.name - - // Some annotations arent "technically" part of the official java standard, - // so we define it ourself here - javadoc { - configure( options ) { - tags( - 'todo:X"', - 'apiNote:a:API Note:', - 'implSpec:a:Implementation Requirements:', - 'implNote:a:Implementation Note:' - ) - } - } - - - repositories { - // Mojang overrides (added to fix downloading the wrong LWJGL libs on M1 Mac's and potentially other arm64 based machines) - maven { url "https://libraries.minecraft.net/" } - - // The central repo - mavenCentral() - - // Used for Google's Collect library - maven { url "https://repo.enonic.com/public/" } - - // For parchment mappings - // versions can be found here: https://ldtteam.jfrog.io/ui/native/parchmentmc-public/org/parchmentmc/data/ - maven { url "https://maven.parchmentmc.org" } - - // For Architectury API - maven { url "https://maven.architectury.dev" } - - // For Git repositories - maven { url "https://jitpack.io" } - - // For Manifold Preprocessor - maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } - - // Required for importing Modrinth mods - maven { - name = "Modrinth" - url = "https://api.modrinth.com/maven" - content { - includeGroup "maven.modrinth" - } - } - - // Required for importing CursedForge mods - maven { - url "https://www.cursemaven.com" - content { - includeGroup "curse.maven" - } - } - - // VanillaGradle and Mixins in common - maven { url "https://repo.spongepowered.org/maven/" } - - // Canvas mod - maven { url "https://maven.vram.io/" } - - // ModMenu mod - maven { url "https://maven.terraformersmc.com/" } - - // neoforge - maven { url "https://maven.neoforged.net/releases/" } - - // These 3 are for importing mods that arnt on CursedForge, Modrinth, GitHub, GitLab or anywhere opensource - flatDir { - dirs "${rootDir}/mods/fabric" - content { - includeGroup "fabric-mod" - } - } - flatDir { - dirs "${rootDir}/mods/quilt" - content { - includeGroup "quilt-mod" - } - } - flatDir { - dirs "${rootDir}/mods/forge" - content { - includeGroup "forge-mod" - } - } - } - - // Adds some dependencies that are in vanilla but not in core - if (p == project(":core")) { - OperatingSystem os = org.gradle.nativeplatform.platform.internal.DefaultNativePlatform.currentOperatingSystem; - - // Set the OS lwjgl is using to the current os - project.ext.lwjglNatives = "natives-" + os.toFamilyName() - - dependencies { - // All of these dependencies are in Vanilla Minecraft, but we need to depend on them as we arent importing Minecraft in the core - // Imports most of lwjgl's libraries - implementation platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}") - - // REMEMBER: Don't shadow stuff here, these are just the libs that are included in Minecraft so that the core can use them - implementation "org.lwjgl:lwjgl" - implementation "org.lwjgl:lwjgl-assimp" - implementation "org.lwjgl:lwjgl-glfw" - // OpenGL is removed since DH now handles rendering in the "Common" project - // so we can use OpenGL for old MC versions and Blaze3D (IE Vulkan) for newer ones -// implementation "org.lwjgl:lwjgl-openal" -// implementation "org.lwjgl:lwjgl-opengl" - implementation "org.lwjgl:lwjgl-stb" - implementation "org.lwjgl:lwjgl-tinyfd" - runtimeOnly "org.lwjgl:lwjgl::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-assimp::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-glfw::$lwjglNatives" -// runtimeOnly "org.lwjgl:lwjgl-openal::$lwjglNatives" -// runtimeOnly "org.lwjgl:lwjgl-opengl::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-stb::$lwjglNatives" - runtimeOnly "org.lwjgl:lwjgl-tinyfd::$lwjglNatives" - implementation "org.joml:joml:${rootProject.joml_version}" - - - // Some other dependencies - implementation("org.jetbrains:annotations:16.0.2") - implementation("com.google.code.findbugs:jsr305:3.0.2") - implementation("com.google.common:google-collect:0.5") - implementation("com.google.guava:guava:31.1-jre") + modLoaders += loaderName; } - } - - - task copyCommonLoaderResources(type: Copy) { - from project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener") - into(file(p.file("build/resources/main"))) - rename "${accessWidenerVersion}.distanthorizons.accesswidener", "distanthorizons.accesswidener" - } - - task copyCoreResources(type: Copy) { - from fileTree(project(":core").file("src/main/resources")) - into p.file("build/resources/main") - } - - tasks.withType(JavaCompile) { - if (isMinecraftSubProject) { - options.release = rootProject.java_version as Integer - } else { - options.release = 8; // Core & Api should use Java 8 no matter what - } - options.encoding = "UTF-8" - } - - java { - withSourcesJar() - } + // merged jars are named in the format: + // "DistantHorizons-3.0.1-b-dev-26.1-fabric-neoforge.jar" + archiveClassifier = modLoaders } + diff --git a/buildAll b/buildAll index 72ad0a208..3808fb95e 100755 --- a/buildAll +++ b/buildAll @@ -3,6 +3,7 @@ echo "==================== Note: All build jars will be in the folder called 'buildAllJars' ====================" mkdir -p buildAllJars rm -rf buildAllJars/* +rm -rf build/forgix/* # Loop trough everything in the version properties folder for d in versionProperties/*; do @@ -19,10 +20,6 @@ for d in versionProperties/*; do sh gradlew build -PmcVer=$version if [ $? != 0 ]; then continue; fi - echo "==================== Merging $version ====================" - sh gradlew mergeJars -PmcVer=$version - if [ $? != 0 ]; then continue; fi - echo "==================== Moving jar ====================" - mv build/merged/*.jar buildAllJars/ + mv build/forgix/*.jar buildAllJars/ done diff --git a/buildAll.bat b/buildAll.bat index 5dfeb4984..cdbd0b323 100644 --- a/buildAll.bat +++ b/buildAll.bat @@ -6,6 +6,7 @@ echo ==================== Note: All build jars will be in the folder called 'buildAllJars' ==================== mkdir buildAllJars del buildAllJars/* +del build/forgix/* @rem Loop trough everything in the version properties folder for %%f in (versionProperties\*) do ( @@ -19,11 +20,8 @@ for %%f in (versionProperties\*) do ( echo ==================== Building !version! ==================== call .\gradlew.bat build -PmcVer="!version!" - echo ==================== Merging !version! ==================== - call .\gradlew.bat mergeJars -PmcVer="!version!" - echo ==================== Moving jar ==================== - move build\merged\*.jar buildAllJars\ + move build\forgix\*.jar buildAllJars\ ) endlocal diff --git a/buildAllParallel.bat b/buildAllParallel.bat new file mode 100644 index 000000000..c99a0f8ab --- /dev/null +++ b/buildAllParallel.bat @@ -0,0 +1,35 @@ +@echo off & setlocal enabledelayedexpansion + + +echo ==================== Getting versions to build... ==================== +mkdir _buildAllJars 2>nul +del _buildAllJars\* /Q 2>nul + +set "ROOT=%~dp0" +set "WORK_DIR=%ROOT%_buildWorkers" +mkdir "%WORK_DIR%" 2>nul + + +REM get the number of versions to compile +set count=0 +for %%f in (versionProperties\*) do set /a count+=1 +echo ==================== Found %count% versions to build in parallel ==================== + +REM Launch a parallel job for each version +for %%f in (%ROOT%versionProperties\*) do ( + set version=%%~nf + + echo starting [!version!]... + start "Build !version!" cmd /c ""%ROOT%build_worker.bat" "!version!" "%ROOT%" "%WORK_DIR%" ""..\..\_buildAllJars""" + + REM Minor timeout between launches so we can stop the build early if we only want + REM to test part of the script and to reduce startup load + timeout /t 3 /nobreak + +REM 2>nul to supress a harmless warning that the for loop +REM "cannot find the drive specified" +) 2>nul + + +echo ==================== All builds started... Completed Jars will be in _buildAllJars ==================== +endlocal \ No newline at end of file diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle new file mode 100644 index 000000000..f10281be6 --- /dev/null +++ b/buildSrc/build.gradle @@ -0,0 +1,24 @@ +plugins { + id 'groovy-gradle-plugin' +} + +repositories { + gradlePluginPortal() + mavenCentral() + maven { url = 'https://maven.wagyourtail.xyz/releases' } // Jvmdowngrader & unimined libs + maven { url = 'https://maven.outlands.top/releases' } // Hosts the kappa fork of unimined + maven { url = 'https://maven.wagyourtail.xyz/snapshots' } // The manifold gradle plugin we use + maven { url = 'https://maven.architectury.dev/' } // Minecraft mod libs + maven { url = 'https://maven.fabricmc.net/' } // Fabric + maven { url = 'https://maven.neoforged.net/releases/' } // NeoForge + maven { url = 'https://maven.minecraftforge.net/' } // Forge + maven { url = 'https://repo.spongepowered.org/repository/maven-public/' } // Hosts minecraft libs + maven { url = 'https://oss.sonatype.org/content/repositories/snapshots/' } // Hosts a few dependencies we use +} + +dependencies { + implementation 'com.gradleup.shadow:shadow-gradle-plugin:9.0.0' + implementation 'xyz.wagyourtail.unimined:xyz.wagyourtail.unimined.gradle.plugin:1.4.18-kappa' + implementation 'xyz.wagyourtail:manifold-gradle:1.0.0-SNAPSHOT' + implementation 'xyz.wagyourtail.jvmdowngrader:xyz.wagyourtail.jvmdowngrader.gradle.plugin:1.3.4' +} diff --git a/buildSrc/src/main/groovy/dh-loader.gradle b/buildSrc/src/main/groovy/dh-loader.gradle new file mode 100644 index 000000000..09268cf07 --- /dev/null +++ b/buildSrc/src/main/groovy/dh-loader.gradle @@ -0,0 +1,516 @@ +import com.github.jengelman.gradle.plugins.shadow.transformers.ResourceTransformer +import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext +import org.apache.tools.zip.ZipEntry +import org.apache.tools.zip.ZipOutputStream + +import javax.annotation.Nonnull +import java.util.function.Function +import java.util.function.Predicate + +// Convention plugin for all MC-facing subprojects (common + loaders). +// Common uses this directly; loaders use it via unimined-fabric/forge/neoforge. +// IMPORTANT: unimined MUST be applied before shadow/jvmdowngrader +// so its afterEvaluate runs first and can modify configs. + +plugins { + id 'java' + id 'maven-publish' + id 'xyz.wagyourtail.unimined' + id 'com.gradleup.shadow' + id 'xyz.wagyourtail.manifold' + id 'xyz.wagyourtail.jvmdowngrader' +} + +def isNotCommonProject = project.name != "common" + + +// ==================== Version Properties ==================== + +project.gradle.ext.getProperties().each { prop -> + rootProject.ext.set(prop.key, prop.value) +} + +manifold { + version = rootProject.manifold_version +} + + +// ==================== Repositories ==================== + +repositories { + maven { url "https://libraries.minecraft.net/" } + mavenCentral() + maven { url "https://repo.enonic.com/public/" } + maven { url "https://maven.parchmentmc.org" } + maven { url "https://maven.architectury.dev" } + maven { url "https://jitpack.io" } + maven { url "https://oss.sonatype.org/content/repositories/snapshots/" } + maven { + name = "Modrinth" + url = "https://api.modrinth.com/maven" + content { includeGroup "maven.modrinth" } + } + maven { + url "https://www.cursemaven.com" + content { includeGroup "curse.maven" } + } + maven { + url "https://repo.spongepowered.org/maven/" + // exclusion is needed since sponge has a deprecated version of fabric + // that gradle would prefer to get over the up-to-date version modrinth provides + content { excludeGroupByRegex "net\\.fabricmc(\\..*)?" } + } + maven { url "https://maven.terraformersmc.com/" } + maven { url "https://maven.neoforged.net/releases/" } + flatDir { + dirs "${rootDir}/mods/fabric" + content { includeGroup "fabric-mod" } + } + flatDir { + dirs "${rootDir}/mods/quilt" + content { includeGroup "quilt-mod" } + } + flatDir { + dirs "${rootDir}/mods/forge" + content { includeGroup "forge-mod" } + } +} + + +// ==================== Java Config ==================== + +tasks.withType(JavaCompile).configureEach { + options.release = rootProject.java_version as Integer + options.encoding = "UTF-8" +} + +java { + sourceCompatibility = JavaVersion.toVersion(gradle.ext.java_version as Integer) + targetCompatibility = JavaVersion.toVersion(gradle.ext.java_version as Integer) + withSourcesJar() +} + + +// ==================== Loader-Only Config ==================== + +if (isNotCommonProject) { + base { archivesName = rootProject.mod_name } + rootProject.ext.versionStr = rootProject.mod_version + "-" + rootProject.minecraft_version + version = project.name + "-" + rootProject.versionStr + group = rootProject.maven_group + + javadoc.title = rootProject.mod_name + "-" + project.name + + tasks.withType(GenerateModuleMetadata).configureEach { + enabled = false + } + tasks.withType(Test).configureEach { + enabled = false + } + compileTestJava.enabled = false + tasks.withType(Sign).configureEach { + enabled = false + } + + jar { + from "LICENSE.txt" + manifest { + attributes( + 'Implementation-Title': rootProject.mod_name, + 'Implementation-Version': rootProject.mod_version, + 'Multi-Release': true, + 'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain', + ) + } + } +} + + +// ==================== Unimined Minecraft Config ==================== + +unimined.minecraft(sourceSets.main, true) { + version gradle.ext.minecraft_version + + if (gradle.ext.minecraft_version.startsWith("1.")) { // 26.1+ doesn't use obfuscation + mappings { + mojmap() + devNamespace "mojmap" + } + } +} + +if (isNotCommonProject) { + // Mixin remapping and common project wiring + unimined.minecraft(sourceSets.main, true) { + mods.modImplementation { + mixinRemap { + reset() + enableBaseMixin() + enableMixinExtra() + } + // Some Fabric API modules ship AW in 'named' namespace instead of 'intermediary' + catchAWNamespaceAssertion() + } + } + + dependencies { + implementation(project(":common")) + } + + processResources { + from project(":common").sourceSets.main.resources + } + + tasks.withType(JavaCompile).configureEach { + source(project(":common").sourceSets.main.allSource) + } +} else { + // Common: fabric for compilation + access widener, no jar remapping or runs + unimined.minecraft { + fabric { + loader gradle.ext.fabric_loader_version + accessWidener project.file("src/main/resources/${gradle.ext.accessWidenerVersion}.distanthorizons.accesswidener") + } + defaultRemapJar = false + runs.off = true + } +} + + +// ==================== Configurations ==================== + +evaluationDependsOn(":core") + +configurations { + shadowMe + coreProjects + shadowMe.extendsFrom(coreProjects) + implementation.extendsFrom(shadowMe) + + common + implementation.extendsFrom(common) +} + + +// ==================== Dependencies ==================== + +// Copy core's compileOnly deps so MC-provided deps are visible without redeclaring them. +project(":core").configurations.compileOnly.allDependencies.each { dep -> + if (!(dep instanceof ProjectDependency)) + dependencies.add("compileOnly", dep) +} + +dependencies { + // Manifold preprocessor & strings + annotationProcessor(manifold.module("preprocessor")) + + // NightConfig: implementation in core (bundled) but Unimined strips it from compile classpath + compileOnly("com.electronwill.night-config:toml:${rootProject.nightconfig_version}") + + // Core & API projects — bundled into shadow jar + coreProjects(project(":core")) + coreProjects(project(":api")) + + // JOML: shadow for old MC versions that don't bundle it (core has it compileOnly already) + if (project.hasProperty("embed_joml") && embed_joml == "true") + shadowMe("org.joml:joml:${rootProject.joml_version}") + + // Common project dependency + if (isNotCommonProject) + common(project(":common")) { transitive false } +} + + +// ==================== NativeTransformer ==================== + +class NativeTransformer implements ResourceTransformer { + private Predicate fileMatcher + private Function filePathMapper + + private final HashMap replacements = new HashMap() + private final HashMap rewrittenFiles = new HashMap() + private nativeRelocator + + public File rootDir + + void matchFiles(Predicate matcher) { + fileMatcher = matcher + } + + void mapPaths(Function mapper) { + filePathMapper = mapper + } + + void relocateNative(String target, String replacement) { + if (replacement.length() > target.length()) { + throw new GradleException("Length of value \"${replacement}\" exceeds the length of \"${target}\": ${replacement.length()} > ${target.length()}") + } + replacements.put(target, replacement) + } + + @Override + boolean canTransformResource(@Nonnull FileTreeElement element) { + return fileMatcher != null && fileMatcher.test(element.relativePath.pathString) + } + + @Override + void transform(@Nonnull TransformerContext context) { + byte[] content = context.inputStream.readAllBytes() + + if (nativeRelocator == null) { + nativeRelocator = new NativeRelocator(rootDir.toPath().resolve("relocate_natives")) + } + + try { + String path = filePathMapper != null + ? filePathMapper.apply(context.path) + : context.path + content = nativeRelocator.processBinary(path, content, replacements) + rewrittenFiles.put(path, content) + } + catch (Throwable e) { + throw new GradleException("Failed to relocate", e) + } + } + + @Override + boolean hasTransformedResource() { return !rewrittenFiles.isEmpty() } + + @Override + void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) { + for (Map.Entry rewrittenFile : rewrittenFiles.entrySet()) { + os.putNextEntry(new ZipEntry(rewrittenFile.key)) + os.write(rewrittenFile.value) + } + } +} + + +// ==================== Shadow JAR (loaders only) ==================== + +if (isNotCommonProject) { + shadowJar { + configurations = [project.configurations.shadowMe] + def librariesLocation = "DistantHorizons.libraries" + + // LZ4 + relocate "net.jpountz", "${librariesLocation}.jpountz" + + // SLF4J + relocate "org.slf4j", "${librariesLocation}.slf4j" + + // SQLite + relocate "org.sqlite", "dh_sqlite", { exclude "org/sqlite/native/**" } + relocate "jdbc:sqlite", "jdbc:dh_sqlite" + + transform(NativeTransformer) { + rootDir = project.rootDir + matchFiles { it.startsWith("org/sqlite") } + mapPaths { it.replace("org/sqlite", "dh_sqlite") } + relocateNative "org/sqlite", "dh_sqlite" + relocateNative "org_sqlite", "dh_1sqlite" + } + + // ZStd + relocate "com.github.luben", "dhcomgithubluben" + relocate "libzstd-jni", "libzstd-jni_dh" + relocate "zstd-jni", "zstd-jni_dh" + + transform(NativeTransformer) { + rootDir = project.rootDir + matchFiles { it.contains("libzstd-jni") && !it.contains("aix/ppc64") } + mapPaths { it.replace("libzstd-jni", "libzstd-jni_dh") } + relocateNative "com/github/luben", "dhcomgithubluben" + relocateNative "com_github_luben", "dhcomgithubluben" + } + + // JOML (conditional) + if (project.hasProperty("embed_joml") && embed_joml == "true") + relocate "org.joml", "${librariesLocation}.joml" + + // NightConfig + relocate "com.electronwill.nightconfig", "${librariesLocation}.electronwill.nightconfig" + + mergeServiceFiles() + } + afterEvaluate { + tasks.named("remapJar").configure { + dependsOn(shadowJar) + inputFile.set(shadowJar.archiveFile) + } + + // Make run tasks use the shadow jar so relocated deps work in dev. + // Filter out jars bundled in the shadow jar, but keep jars that the loader also + // needs (e.g. NightConfig — DH relocates it, but NeoForge needs the original). + def shadowedPaths = configurations.shadowMe.resolve().collect { it.path }.toSet() + def loaderPaths = configurations.minecraftLibraries.resolve().collect { it.path }.toSet() + tasks.withType(JavaExec).configureEach { runTask -> + dependsOn(shadowJar) + classpath = files(shadowJar.archiveFile) + classpath.filter { file -> + !file.path.contains(project.buildDir.path) && + !file.path.contains("core${File.separator}build") && + !file.path.contains("api${File.separator}build") && + !file.path.contains("common${File.separator}build") && + !(shadowedPaths.contains(file.path) && !loaderPaths.contains(file.path)) + } + + // Shared run directory so all loaders use the same worlds + def isClient = runTask.name.toLowerCase().contains("client") + runTask.workingDir = rootProject.file("run/${isClient ? 'client' : 'server'}") + + // Minecraft automatically has G1GC args present, + // remove them so we can use ZGC instead + def filteredArgs = runTask.jvmArgs.findAll { arg -> + !arg.startsWith("-XX:+UseG1GC") && + !arg.startsWith("-XX:G1") && + !arg.startsWith("-XX:MaxGCPauseMillis") + } + runTask.jvmArgs = filteredArgs + + // fix (Neo)forge debug running + doFirst { + def modsDir = rootProject.file("run/${isClient ? 'client' : 'server'}/mods") + modsDir.mkdirs() + + // Remove any stale DH jars before copying the fresh one + modsDir.listFiles()?.each { file -> + if (file.name.startsWith(rootProject.mod_name)) file.delete() + } + + // Copy shadow jar into mods folder so (Neo)Forge discovers it properly + copy { + from shadowJar.archiveFile + into modsDir + } + } + + // JVM args + runTask.jvmArgs( + "-Dio.netty.leakDetection.level=advanced", + // TODO only use for modern java versions + "-XX:+UseZGC", + // TODO don't use for even more modern-er java versions + //"-XX:+ZGenerational", + rootProject.minecraftMemoryJavaArg, + ) + if (isClient) + { + runTask.jvmArgs( + "-Dminecraft.api.auth.host=https://nope.invalid", + "-Dminecraft.api.account.host=https://nope.invalid", + "-Dminecraft.api.session.host=https://nope.invalid", + "-Dminecraft.api.services.host=https://nope.invalid", + ) + runTask.args( + // use a consistent username for easier debugging in a given world (vs randomly teleporting to a new user each time the game boots) + "--username", "Dev", + // "--renderDebugLabels" is a Mojang command to show render names in RenderDoc + "--renderDebugLabels" + ) + + // enabling tracy causes constant memory growth so it isn't always desired + if (rootProject.minecraftEnableTracy == "true") + { + // "--tracy" is a Mojang command to allow individual frames to be debugged using Tracy https://github.com/wolfpld/tracy/releases/tag/v0.13.1 + runTask.args("--tracy") + } + + + } + } + } +} + + +// ==================== Process Resources (loaders only) ==================== + +if (isNotCommonProject) { + processResources { + def resourceTargets = [ + "build_info.json", + "fabric.mod.json", + "quilt.mod.json", + "META-INF/mods.toml", + "META-INF/neoforge.mods.toml", + ] + + def compatible_forgemc_versions = "${rootProject.compatible_minecraft_versions}".replaceAll("\"", "").replaceAll("]", ",)") + + // Quilt contributors + def quilt_contributors = [] + def mod_author_list = rootProject.mod_authors.replaceAll("\"", "").replace("[", "").replace("]", "").split(",") + for (dev in mod_author_list) { + quilt_contributors.push("\"${dev.strip()}\": \"Developer\"") + } + quilt_contributors.reverse() + + try { + if (rootProject.infoGitCommit == "null") + rootProject.ext.infoGitCommit = 'git rev-parse --verify HEAD'.execute().text.trim() + if (rootProject.infoGitBranch == "null") + rootProject.ext.infoGitBranch = 'git symbolic-ref --short HEAD'.execute().text.trim() + } catch (Exception e) { + rootProject.ext.infoGitCommit = "Git not found" + rootProject.ext.infoGitBranch = "Git not found" + } + + def replaceProperties = [ + version : rootProject.mod_version, + mod_name : rootProject.mod_readable_name, + group : rootProject.maven_group, + authors : rootProject.mod_authors, + description : rootProject.mod_description, + homepage : rootProject.mod_homepage, + source : rootProject.mod_source, + issues : rootProject.mod_issues, + discord : rootProject.mod_discord, + minecraft_version : rootProject.minecraft_version, + accessWidenerVersion : rootProject.accessWidenerVersion, + compatible_minecraft_versions: rootProject.compatible_minecraft_versions, + compatible_forgemc_versions : compatible_forgemc_versions, + java_version : rootProject.java_version, + quilt_contributors : "{" + quilt_contributors.join(", ") + "}", + info_git_commit : rootProject.infoGitBranch, + info_git_branch : rootProject.infoGitCommit, + info_build_source : rootProject.infoBuildSource, + fabric_incompatibility_list : rootProject.fabric_incompatibility_list, + fabric_recommend_list : rootProject.fabric_recommend_list, + neoforge_version_range : rootProject.neoforge_version_range, + ] + + inputs.properties replaceProperties + replaceProperties.put "project", project + filesMatching(resourceTargets) { + expand replaceProperties + } + + // Remove unused access wideners + exclude { file -> + if (file.name.contains(".distanthorizons.accesswidener") && file.name != "${rootProject.accessWidenerVersion}.distanthorizons.accesswidener") { + return true + } + return false + } + } + + + // ==================== Resource Copy Tasks ==================== + + // task copyCommonLoaderResources(type: Copy) { + // from project(":common").file("src/main/resources/${rootProject.accessWidenerVersion}.distanthorizons.accesswidener") + // into(file(project.file("build/resources/main"))) + // rename "${rootProject.accessWidenerVersion}.distanthorizons.accesswidener", "distanthorizons.accesswidener" + // } + + task copyCoreResources(type: Copy) { + from fileTree(project(":core").file("src/main/resources")) + into project.file("build/resources/main") + } + + + // ==================== JVMDowngrader ==================== + + jvmdg.downgradeTo = JavaVersion.toVersion(rootProject.java_version) + downgradeJar.archiveClassifier.set(null) + shadeDowngradedApi.archiveClassifier.set(null) +} diff --git a/buildSrc/src/main/groovy/root.gradle b/buildSrc/src/main/groovy/root.gradle new file mode 100644 index 000000000..4f34000fc --- /dev/null +++ b/buildSrc/src/main/groovy/root.gradle @@ -0,0 +1,56 @@ +plugins { + id 'java' +} + +// Transfer version properties from settings.gradle to project +project.gradle.ext.getProperties().each { prop -> + rootProject.ext.set(prop.key, prop.value) +} + +// Version string for archives +rootProject.ext.versionStr = rootProject.mod_version + "-" + rootProject.minecraft_version +rootProject.allprojects { + version = (it == rootProject ? "" : it.name + "-") + rootProject.versionStr + group = rootProject.maven_group + + // Custom javadoc tags for all subprojects + plugins.withType(JavaPlugin) { + javadoc { + options.tags( + 'todo:X"', + 'apiNote:a:API Note:', + 'implSpec:a:Implementation Requirements:', + 'implNote:a:Implementation Note:' + ) + } + } +} + +// Create build.properties with preprocessor definitions +def writePreprocessorDefinitions() { + StringBuilder sb = new StringBuilder() + sb.append("# DON'T TOUCH THIS FILE, This is handled by the build script\n") + + gradle.ext.mcVers.eachWithIndex { ver, idx -> + sb.append("MC_${ver.replace('.', '_')}=${idx}\n") + if (gradle.ext.mcIndex == idx) + sb.append("MC_VER=${idx}\n") + } + + if (rootProject.mod_version.toLowerCase().contains("dev")) { + sb.append("DEV_BUILD=\n") + } + + new File(rootDir, "build.properties").text = sb.toString() +} +writePreprocessorDefinitions() + +// Wire JVMDowngrader to process remapped jars +gradle.projectsEvaluated { + rootProject.subprojects.each { + if (it.tasks.findByName('remapJar') == null) return + it.tasks.downgradeJar.inputFile = it.tasks.remapJar.archiveFile + it.tasks.jar.finalizedBy(it.tasks.remapJar) + it.tasks.remapJar.finalizedBy(it.tasks.shadeDowngradedApi) + } +} diff --git a/buildSrc/src/main/groovy/unimined-fabric.gradle b/buildSrc/src/main/groovy/unimined-fabric.gradle new file mode 100644 index 000000000..9e3de7851 --- /dev/null +++ b/buildSrc/src/main/groovy/unimined-fabric.gradle @@ -0,0 +1,13 @@ +plugins { + id 'dh-loader' +} + +unimined.minecraft { + fabric { + loader gradle.ext.fabric_loader_version + accessWidener project(":common").file("src/main/resources/${gradle.ext.accessWidenerVersion}.distanthorizons.accesswidener") + } +} + +runClient.javaLauncher = null +runServer.javaLauncher = null diff --git a/buildSrc/src/main/groovy/unimined-forge.gradle b/buildSrc/src/main/groovy/unimined-forge.gradle new file mode 100644 index 000000000..587553a8f --- /dev/null +++ b/buildSrc/src/main/groovy/unimined-forge.gradle @@ -0,0 +1,17 @@ +plugins { + id 'dh-loader' +} + +def awFile = project(":common").file("src/main/resources/${gradle.ext.accessWidenerVersion}.distanthorizons.accesswidener") + +unimined.minecraft { + forge { + loader gradle.ext.forge_version + useToolchains = false + mixinConfig("DistantHorizons.forge.mixins.json") + accessTransformer aw2at(awFile) + } +} + +runClient.javaLauncher = null +runServer.javaLauncher = null diff --git a/buildSrc/src/main/groovy/unimined-neoforge.gradle b/buildSrc/src/main/groovy/unimined-neoforge.gradle new file mode 100644 index 000000000..a97de717a --- /dev/null +++ b/buildSrc/src/main/groovy/unimined-neoforge.gradle @@ -0,0 +1,16 @@ +plugins { + id 'dh-loader' +} + +def awFile = project(":common").file("src/main/resources/${gradle.ext.accessWidenerVersion}.distanthorizons.accesswidener") + +unimined.minecraft { + neoForged { + loader gradle.ext.neoforge_version + useToolchains = false + accessTransformer aw2at(awFile) + } +} + +runClient.javaLauncher = null +runServer.javaLauncher = null diff --git a/build_worker.bat b/build_worker.bat new file mode 100644 index 000000000..ae0bc33d1 --- /dev/null +++ b/build_worker.bat @@ -0,0 +1,41 @@ +@echo off & setlocal enabledelayedexpansion + +set "VERSION=%~1" +set "ROOT=%~2" +set "WORK_DIR=%~3" +set "WORKER=%WORK_DIR%\%VERSION%" +set "JAR_OUTPUT_DIR=%~4" + +REM remove the ending "\" from the root folder, otherwise the final quote +REM in the robocopy command will be escaped and it won't run +if "%ROOT:~-1%"=="\" set "ROOT=%ROOT:~0,-1%" +set "WORKER=%~3\%~1" + +set "BUILT_JAR_DIR=%WORKER%\build\forgix" + + + +echo ==================== [%VERSION%] Copying workspace ==================== +mkdir "%WORKER%" +robocopy "%ROOT%" "%WORKER%" /E /XD "%WORKER%" "_buildWorkers" "buildAllJars" ".gradle" "build" ".git" ".idea" ".gitlab" "run" "testScripts" /NFL /NDL + +echo ==================== [%VERSION%] Cleaning ==================== +cd /d "%WORKER%" +call .\gradlew.bat clean +REM optional arg that can be added if we want to log the result to a file +REM >"%WORK_DIR%\build_%VERSION%.log" 2>&1 + +echo ==================== [%VERSION%] Assembling ==================== +call .\gradlew.bat assemble -PmcVer="%VERSION%" +REM optional arg that can be added if we want to log the result to a file +REM >>"%WORK_DIR%\build_%VERSION%.log" 2>&1 + +echo ==================== [%VERSION%] Exporting ==================== +mkdir "%JAR_OUTPUT_DIR%" +robocopy "%BUILT_JAR_DIR%" "%JAR_OUTPUT_DIR%" /NFL /NDL + +echo ==================== [%VERSION%] Done ==================== +endlocal + +REM can be uncommented for debugging +REM pause \ No newline at end of file diff --git a/common/build.gradle b/common/build.gradle index a2fea2fdd..018764b41 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -1,43 +1,3 @@ - -// temporary fix for broken spongepowered version -buildscript { - configurations.configureEach { - resolutionStrategy { - force 'org.spongepowered:vanillagradle:0.2.1-20240507.024226-82' - // newer versions can be found by going to the link: - // https://repo.spongepowered.org/#browse/browse:maven-public:org%2Fspongepowered%2Fvanillagradle%2F0.2.1-SNAPSHOT - } - } +plugins { + id 'dh-loader' } - -if (rootProject.minecraft_version != "1.12.2") { - apply plugin: "org.spongepowered.gradle.vanilla" -} - -if (rootProject.minecraft_version != "1.12.2") { - minecraft { - accessWideners(project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener")) - version(rootProject.minecraft_version) - } -} - -dependencies { - // So mixins can be written in common - compileOnly group:'org.spongepowered', name:'mixin', version:'0.8.5' -} - - -publishing { - publications { - mavenCommon(MavenPublication) { - artifactId = rootProject.mod_readable_name - from components.java - } - } - - // See https://docs.gradle.org/current/userguide/publishing_maven.html for information on how to set up publishing. - repositories { - // Add repositories to publish to here. - } -} - diff --git a/common/src/main/java/com/seibel/distanthorizons/common/AbstractModInitializer.java b/common/src/main/java/com/seibel/distanthorizons/common/AbstractModInitializer.java index fafc48283..4ebc2491a 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/AbstractModInitializer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/AbstractModInitializer.java @@ -1,5 +1,6 @@ package com.seibel.distanthorizons.common; +import com.mojang.brigadier.CommandDispatcher; import com.seibel.distanthorizons.api.enums.config.EDhApiRenderApi; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeDhInitEvent; @@ -7,8 +8,8 @@ import com.seibel.distanthorizons.common.commands.CommandInitializer; import com.seibel.distanthorizons.common.wrappers.DependencySetup; import com.seibel.distanthorizons.common.wrappers.gui.DhDebugScreenEntry; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper; +import com.seibel.distanthorizons.core.Initializer; import com.seibel.distanthorizons.core.api.internal.ClientApi; -import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.ConfigHandler; import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler; @@ -20,7 +21,8 @@ import com.seibel.distanthorizons.core.jar.updater.SelfUpdater; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer; import com.seibel.distanthorizons.core.render.renderer.StubDebugWireframeRenderer; -import com.seibel.distanthorizons.core.util.NativeDialogUtil; +import com.seibel.distanthorizons.common.wrappers.gui.NativeDialogUtil; +import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor; @@ -28,14 +30,12 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModAccesso import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import com.seibel.distanthorizons.coreapi.ModInfo; -#if MC_VER > MC_1_12_2 -import com.mojang.brigadier.CommandDispatcher; import net.minecraft.commands.CommandSourceStack; -#endif import net.minecraft.server.MinecraftServer; import net.minecraft.server.dedicated.DedicatedServer; import com.seibel.distanthorizons.core.logging.DhLogger; +import java.util.concurrent.CompletableFuture; import java.util.function.Consumer; import java.util.function.Supplier; @@ -62,9 +62,7 @@ public abstract class AbstractModInitializer protected abstract IEventProxy createServerProxy(boolean isDedicated); protected abstract void initializeModCompat(); - #if MC_VER > MC_1_12_2 protected abstract void subscribeRegisterCommandsEvent(Consumer> eventHandler); - #endif protected abstract void subscribeClientStartedEvent(Runnable eventHandler); protected abstract void subscribeServerStartingEvent(Consumer eventHandler); @@ -97,7 +95,8 @@ public abstract class AbstractModInitializer // Client uses config for auto-updater, so it's initialized here instead of post-init stage this.initConfig(); - logModIncompatibilityWarnings(); // needs to be called after config loading + logIncompatibilityWarnings(); // needs to be called after config loading + Initializer.postConfigInit(); LOGGER.info(ModInfo.READABLE_NAME + " client Initialized."); @@ -131,25 +130,22 @@ public abstract class AbstractModInitializer this.initializeModCompat(); LOGGER.info(ModInfo.READABLE_NAME + " server Initialized, adding event subscribers..."); - #if MC_VER > MC_1_12_2 this.commandInitializer = new CommandInitializer(); this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandInitializer.initCommands(dispatcher); }); - #endif this.subscribeServerStartingEvent(server -> { MinecraftServerWrapper.INSTANCE.dedicatedServer = (DedicatedServer)server; this.initConfig(); + Initializer.postConfigInit(); this.postInit(); this.postServerInit(); - #if MC_VER > MC_1_12_2 this.commandInitializer.onServerReady(); - #endif this.checkForUpdates(); - LOGGER.info(ModInfo.READABLE_NAME + " server Initialized at " + server. #if MC_VER <= MC_1_12_2 getDataDirectory() #else getServerDirectory() #endif); + LOGGER.info(ModInfo.READABLE_NAME + " server Initialized at " + server.getServerDirectory()); }); } @@ -165,7 +161,7 @@ public abstract class AbstractModInitializer private void startup() { DependencySetup.createSharedBindings(); - SharedApi.init(); + Initializer.preConfigInit(); this.createInitialSharedBindings(); } @@ -233,18 +229,43 @@ public abstract class AbstractModInitializer ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null); } - private void postClientInit() { DependencySetup.setRenderingApiBindings(); } - private void postServerInit() + private void postClientInit() { - SingletonInjector.INSTANCE.bind(AbstractDebugWireframeRenderer.class, new StubDebugWireframeRenderer()); + CompletableFuture future = new CompletableFuture<>(); + + // This method may be called from either the render thread, + // or some other random setup thread depending on the mod loader. + // In order to avoid confusion/inconsistent problems, we're always going + // to run setup on our own thread. + Thread dhSetupThread = new Thread(() -> + { + try + { + DependencySetup.setRenderingApiBindings(); + } + catch (Exception e) + { + future.completeExceptionally(e); + } + finally + { + future.complete(null); + } + }); + dhSetupThread.setName(ThreadUtil.THREAD_NAME_PREFIX + "PostClientInit Thread"); + dhSetupThread.start(); + + future.join(); } + private void postServerInit() { SingletonInjector.INSTANCE.bind(AbstractDebugWireframeRenderer.class, new StubDebugWireframeRenderer()); } + //endregion - //==================================// - // mod partial compatibility checks // - //==================================// + //======================// + // compatibility checks // + //======================// //region /** @@ -253,7 +274,7 @@ public abstract class AbstractModInitializer * This method will log (and display to chat if enabled) * these warnings and potential fixes. */ - private static void logModIncompatibilityWarnings() + private static void logIncompatibilityWarnings() { boolean showChatWarnings = Config.Common.Logging.Warning.showModCompatibilityWarningsOnStartup.get(); IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class); @@ -354,10 +375,10 @@ public abstract class AbstractModInitializer renderApi = versionConstants.getDefaultRenderingApi(); } - // Iris only supports nataive OpenGL + // Iris only supports native OpenGL if (renderApi != EDhApiRenderApi.OPEN_GL) { - String irisUnsupportedMessage = "Iris doesn't support DH when using the ["+EDhApiRenderApi.BLAZE_3D+"] rendering API, please change it to ["+EDhApiRenderApi.OPEN_GL+"] in DH's config file."; + String irisUnsupportedMessage = "Iris doesn't support DH when using the ["+EDhApiRenderApi.BLAZE_3D+"] rendering API, this will need to be fixed on Iris end. As a temporary fix please change the rendering API to ["+EDhApiRenderApi.OPEN_GL+"] in the DH config file."; LOGGER.fatal(irisUnsupportedMessage); NativeDialogUtil.showDialog(ModInfo.READABLE_NAME, irisUnsupportedMessage, "ok", "error"); diff --git a/common/src/main/java/com/seibel/distanthorizons/common/commonMixins/MixinChunkMapCommon.java b/common/src/main/java/com/seibel/distanthorizons/common/commonMixins/MixinChunkMapCommon.java index ad316a03d..4f04819d0 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/commonMixins/MixinChunkMapCommon.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/commonMixins/MixinChunkMapCommon.java @@ -4,26 +4,32 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper; import com.seibel.distanthorizons.core.api.internal.ServerApi; import com.seibel.distanthorizons.core.api.internal.SharedApi; +import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; -#if MC_VER <= MC_1_12_2 -import net.minecraft.world.WorldServer; -import net.minecraft.world.chunk.Chunk; -#else import net.minecraft.server.level.ServerLevel; import net.minecraft.world.level.chunk.ChunkAccess; -#endif - +import net.minecraft.world.level.chunk.ProtoChunk; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; public class MixinChunkMapCommon { - public static void onChunkSave(#if MC_VER <= MC_1_12_2 WorldServer #else ServerLevel #endif level, #if MC_VER <= MC_1_12_2 Chunk #else ChunkAccess #endif chunk #if MC_VER > MC_1_12_2, CallbackInfoReturnable ci #endif) + public static void onChunkSave(ServerLevel level, ChunkAccess chunk, CallbackInfoReturnable ci) { IServerLevelWrapper levelWrapper = ServerLevelWrapper.getWrapper(level); + int chunkPosX; + int chunkPosZ; + #if MC_VER <= MC_1_21_11 + chunkPosX = chunk.getPos().x; + chunkPosZ = chunk.getPos().z; + #else + chunkPosX = chunk.getPos().x(); + chunkPosZ = chunk.getPos().z(); + #endif + // is this position already being updated? - if (SharedApi.isChunkAtChunkPosAlreadyUpdating(levelWrapper, chunk.getPos().x, chunk.getPos().z)) + if (SharedApi.isChunkAtChunkPosAlreadyUpdating(levelWrapper, chunkPosX, chunkPosZ)) { return; } @@ -31,7 +37,7 @@ public class MixinChunkMapCommon // is this chunk being saved to disk? - boolean savingChunkToDisk = #if MC_VER <= MC_1_12_2 true #else ci.getReturnValue() #endif; + boolean savingChunkToDisk = ci.getReturnValue(); // true means a chunk was saved to disk if (!savingChunkToDisk) { @@ -44,18 +50,13 @@ public class MixinChunkMapCommon // MC has a tendency to try saving incomplete or corrupted chunks (which show up as empty or black chunks) // this logic should prevent that from happening - #if MC_VER <= MC_1_12_2 - if (!chunk.isTerrainPopulated() || !chunk.isLightPopulated()) - { - return; - } - #elif MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 + #if MC_VER <= MC_1_17_1 if (chunk.isUnsaved() || chunk.getUpgradeData() != null || !chunk.isLightCorrect()) { return; } #else - if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect()) + if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect() || chunk instanceof ProtoChunk) { return; } @@ -67,7 +68,7 @@ public class MixinChunkMapCommon // some chunks may be missing their biomes, which cause issues when attempting to save them #if MC_VER <= MC_1_17_1 - if (chunk.#if MC_VER <= MC_1_12_2 getBiomeArray() #else getBiomes() #endif == null) + if (chunk.getBiomes() == null) { return; } @@ -84,7 +85,7 @@ public class MixinChunkMapCommon #endif - //System.out.println("SUBMITTING: " + chunk.getPos()); + // submit the update event ServerApi.INSTANCE.serverChunkSaveEvent( new ChunkWrapper(chunk, levelWrapper), diff --git a/common/src/main/java/com/seibel/distanthorizons/common/commonMixins/MixinVanillaFogCommon.java b/common/src/main/java/com/seibel/distanthorizons/common/commonMixins/MixinVanillaFogCommon.java new file mode 100644 index 000000000..fc4938e28 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/commonMixins/MixinVanillaFogCommon.java @@ -0,0 +1,110 @@ +package com.seibel.distanthorizons.common.commonMixins; + +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper; +import com.seibel.distanthorizons.core.api.internal.ClientApi; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.Camera; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; + + +import net.minecraft.client.Camera; +import net.minecraft.world.effect.MobEffects; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.LivingEntity; + +#if MC_VER < MC_1_17_1 +import net.minecraft.world.level.material.FluidState; +import net.minecraft.client.renderer.FogRenderer; +import net.minecraft.client.renderer.FogRenderer.FogMode; +import com.mojang.blaze3d.systems.RenderSystem; + +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +#elif MC_VER < MC_1_21_3 +import net.minecraft.world.level.material.FogType; +import net.minecraft.client.renderer.FogRenderer; +import net.minecraft.client.renderer.FogRenderer.FogMode; +import com.mojang.blaze3d.systems.RenderSystem; + +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +#elif MC_VER < MC_1_21_6 +import net.minecraft.world.level.material.FogType; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import com.mojang.blaze3d.shaders.FogShape; +import net.minecraft.client.renderer.FogRenderer; +import net.minecraft.client.renderer.FogRenderer.FogMode; +import net.minecraft.client.renderer.FogParameters; +import org.joml.Vector4f; +import com.mojang.blaze3d.systems.RenderSystem; + +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +#else +import net.minecraft.world.level.material.FogType; +#endif + + +public class MixinVanillaFogCommon +{ + + + #if MC_VER < MC_1_21_6 + public static boolean cancelFog(Camera camera, FogRenderer.FogMode fogMode) + #else + public static boolean cancelFog() + #endif + { + + + #if MC_VER < MC_1_21_6 + Entity entity = camera.getEntity(); + #elif MC_VER <= MC_1_21_10 + Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); + Entity entity = camera.getEntity(); + #else + Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); + Entity entity = camera.entity(); + #endif + + + boolean cameraNotInFluid = cameraNotInFluid(camera); + boolean isSpecialFog = (entity instanceof LivingEntity) && ((LivingEntity) entity).hasEffect(MobEffects.BLINDNESS); + + boolean cancelFog = !isSpecialFog; + cancelFog = cancelFog && cameraNotInFluid; + #if MC_VER < MC_1_21_6 + cancelFog = cancelFog && (fogMode == FogRenderer.FogMode.FOG_TERRAIN); + #endif + + cancelFog = cancelFog && !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial(); + cancelFog = cancelFog && !Config.Client.Advanced.Graphics.Fog.enableVanillaFog.get(); + + + return cancelFog; + } + + private static boolean cameraNotInFluid(Camera camera) + { + #if MC_VER < MC_1_17_1 + FluidState fluidState = camera.getFluidInCamera(); + boolean cameraNotInFluid = fluidState.isEmpty(); + #else + FogType fogTypes = camera.getFluidInCamera(); + boolean cameraNotInFluid = fogTypes == FogType.NONE; + #endif + + return cameraNotInFluid; + } + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDebugWireframeRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDebugWireframeRenderer.java index cd71bb320..44ba3d09e 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDebugWireframeRenderer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDebugWireframeRenderer.java @@ -29,7 +29,6 @@ import com.mojang.blaze3d.buffers.GpuBufferSlice; import com.mojang.blaze3d.buffers.Std140Builder; import com.mojang.blaze3d.buffers.Std140SizeCalculator; import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.platform.DepthTestFunction; import com.mojang.blaze3d.platform.PolygonMode; import com.mojang.blaze3d.shaders.UniformType; import com.mojang.blaze3d.systems.CommandEncoder; @@ -39,6 +38,7 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.VertexFormat; import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil; import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil; +import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; @@ -139,26 +139,27 @@ public class BlazeDebugWireframeRenderer extends AbstractDebugWireframeRenderer } private void createPipelines() { - VertexFormat vertexFormat = VertexFormat.builder() - .add("vPosition", BlazeDhVertexFormatUtil.FLOAT_XYZ_POS) - .build(); - - RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper(); { - pipelineBuilder.withCull(false); + pipelineBuilder.withFaceCulling(false); pipelineBuilder.withDepthWrite(true); - pipelineBuilder.withDepthTestFunction(DepthTestFunction.LESS_DEPTH_TEST); + pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.LESS); pipelineBuilder.withColorWrite(true); pipelineBuilder.withoutBlend(); - pipelineBuilder.withPolygonMode(PolygonMode.WIREFRAME); - pipelineBuilder.withLocation(Identifier.parse("distanthorizons:debug_wireframe_renderer")); + pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.WIREFRAME); + pipelineBuilder.withName("debug_wireframe_renderer"); - pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "debug/blaze/vert")); - pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "debug/blaze/frag")); + pipelineBuilder.withVertexShader("debug/blaze/vert"); + pipelineBuilder.withFragmentShader("debug/blaze/frag"); - pipelineBuilder.withUniform("uniformBlock", UniformType.UNIFORM_BUFFER); + pipelineBuilder.withUniformBuffer("uniformBlock"); - pipelineBuilder.withVertexFormat(vertexFormat, VertexFormat.Mode.DEBUG_LINES); + + VertexFormat vertexFormat = VertexFormat.builder() + .add("vPosition", BlazeDhVertexFormatUtil.FLOAT_XYZ_POS) + .build(); + pipelineBuilder.withVertexFormat(vertexFormat); + pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.LINES); } this.pipeline = pipelineBuilder.build(); @@ -229,11 +230,11 @@ public class BlazeDebugWireframeRenderer extends AbstractDebugWireframeRenderer { this.init(); - if (BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.isEmpty() - || BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.isEmpty()) - { - return; - } + //if (BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.isEmpty() + // || BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.isEmpty()) + //{ + // return; + //} // shouldn't happen, but just in case if (box == null) @@ -307,12 +308,12 @@ public class BlazeDebugWireframeRenderer extends AbstractDebugWireframeRenderer { // Bind instance data // renderPass.setUniform("uniformBlock", this.uniformBuffer); - + renderPass.setPipeline(this.pipeline); renderPass.setIndexBuffer(this.boxIndexBuffer, VertexFormat.IndexType.INT); - + renderPass.setVertexBuffer(0, this.boxVertexBuffer); - + renderPass.drawIndexed( /*indexStart*/ 0, /*firstIndex*/0, diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhGenericObjectRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhGenericObjectRenderer.java index 18fdf02f5..fd92d28fe 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhGenericObjectRenderer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhGenericObjectRenderer.java @@ -30,7 +30,6 @@ import com.mojang.blaze3d.buffers.Std140Builder; import com.mojang.blaze3d.buffers.Std140SizeCalculator; import com.mojang.blaze3d.pipeline.BlendFunction; import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.platform.DepthTestFunction; import com.mojang.blaze3d.platform.PolygonMode; import com.mojang.blaze3d.shaders.UniformType; import com.mojang.blaze3d.systems.CommandEncoder; @@ -49,6 +48,7 @@ import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBox; import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShading; import com.seibel.distanthorizons.common.render.blaze.objects.BlazeGenericObjectVertexContainer; import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil; +import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper; import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper; import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil; import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper; @@ -57,6 +57,7 @@ import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler; import com.seibel.distanthorizons.core.render.renderer.GenericRenderObjectFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer; import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup; @@ -108,8 +109,6 @@ public class BlazeDhGenericObjectRenderer implements IDhGenericRenderer // rendering setup private boolean init = false; - private VertexFormat vertexFormat; - private RenderPipeline pipeline; private GpuBuffer vertUniformBuffer; @@ -131,12 +130,6 @@ public class BlazeDhGenericObjectRenderer implements IDhGenericRenderer } this.init = true; - this.vertexFormat = VertexFormat.builder() - .add("vPosition", BlazeDhVertexFormatUtil.FLOAT_XYZ_POS) - .add("aColor", BlazeDhVertexFormatUtil.RGBA_UBYTE_COLOR) - .add("aMaterial", BlazeDhVertexFormatUtil.IRIS_MATERIAL) - .build(); - this.createPipelines(); if (RENDER_DEBUG_OBJECTS) @@ -146,26 +139,36 @@ public class BlazeDhGenericObjectRenderer implements IDhGenericRenderer } private void createPipelines() { - RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper(); { - pipelineBuilder.withCull(true); + pipelineBuilder.withFaceCulling(true); pipelineBuilder.withDepthWrite(true); - pipelineBuilder.withDepthTestFunction(DepthTestFunction.LESS_DEPTH_TEST); - pipelineBuilder.withBlend(BlendFunction.TRANSLUCENT); + pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.LESS); + pipelineBuilder.withBlend(BlendFunction.TRANSLUCENT); // TRANSLUCENT = new BlendFunction(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA, SourceFactor.ONE, DestFactor.ONE_MINUS_SRC_ALPHA); pipelineBuilder.withColorWrite(true); - pipelineBuilder.withPolygonMode(PolygonMode.FILL); - pipelineBuilder.withLocation(Identifier.parse("distanthorizons:generic")); + pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.FILL); + pipelineBuilder.withName("generic_objects"); - pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "generic/blaze/vert")); - pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "generic/blaze/frag")); + pipelineBuilder.withVertexShader("generic/blaze/vert"); + pipelineBuilder.withFragmentShader("generic/blaze/frag"); pipelineBuilder.withSampler("uLightMap"); - pipelineBuilder.withUniform("vertUniformBlock", UniformType.UNIFORM_BUFFER); + pipelineBuilder.withUniformBuffer("vertUniformBlock"); - pipelineBuilder.withVertexFormat(this.vertexFormat, VertexFormat.Mode.TRIANGLES); - this.pipeline = pipelineBuilder.build(); + VertexFormat vertexFormat = VertexFormat.builder() + .add("vPosition", BlazeDhVertexFormatUtil.FLOAT_XYZ_POS) + .add("aColor", BlazeDhVertexFormatUtil.RGBA_UBYTE_COLOR) + .add("aMaterial", BlazeDhVertexFormatUtil.IRIS_MATERIAL) + + .add("paddingOne", BlazeDhVertexFormatUtil.BYTE_PAD) + .add("paddingTwo", BlazeDhVertexFormatUtil.BYTE_PAD) + .add("paddingThree", BlazeDhVertexFormatUtil.BYTE_PAD) + .build(); + pipelineBuilder.withVertexFormat(vertexFormat); + pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLES); } + this.pipeline = pipelineBuilder.build(); } private void addGenericDebugObjects() { @@ -328,194 +331,194 @@ public class BlazeDhGenericObjectRenderer implements IDhGenericRenderer @Override public void render(RenderParams renderEventParam, IProfilerWrapper profiler, boolean renderingWithSsao) { - //==============// - // render setup // - //==============// - //#region - profiler.push("setup"); - - this.init(); - - ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderSetupEvent.class, renderEventParam); - - Vec3d camPos = MC_RENDER.getCameraExactPosition(); - - //#endregion - - if (BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.isEmpty() - || BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.isEmpty()) + try (IProfilerWrapper.IProfileBlock generic_profile = profiler.push("setup")) { - return; - } - - - - //===========// - // rendering // - //===========// - //#region - - Collection boxList = this.boxGroupById.values(); - for (RenderableBoxGroup boxGroup : boxList) - { - // validation // - // shouldn't happen, but just in case - if (boxGroup == null) + + //==============// + // render setup // + //==============// + //#region + + this.init(); + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderSetupEvent.class, renderEventParam); + + Vec3d camPos = MC_RENDER.getCameraExactPosition(); + + //#endregion + + if (BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.isEmpty() + || BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.isEmpty()) { - continue; + return; } - // skip boxes that shouldn't render this pass - if (boxGroup.ssaoEnabled != renderingWithSsao) - { - continue; - } - profiler.popPush("render prep"); - boxGroup.preRender(renderEventParam); // called even if the group is inactive, so the group can be activate if desired - // ignore inactive groups - if (!boxGroup.active) - { - continue; - } + //===========// + // rendering // + //===========// + //#region - // allow API users to cancel this object's rendering - boolean cancelRendering = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericObjectRenderEvent.class, new DhApiBeforeGenericObjectRenderEvent.EventParam(renderEventParam, boxGroup)); - if (cancelRendering) + Collection boxList = this.boxGroupById.values(); + for (RenderableBoxGroup boxGroup : boxList) { - continue; - } - - // update instanced data if needed - { - boxGroup.tryUpdateInstancedDataAsync(); + // validation // - // skip groups that haven't been uploaded yet - if (boxGroup.vertexBufferContainer.getState() != IDhGenericObjectVertexBufferContainer.EState.RENDER) + // shouldn't happen, but just in case + if (boxGroup == null) { continue; } + + // skip boxes that shouldn't render this pass + if (boxGroup.ssaoEnabled != renderingWithSsao) + { + continue; + } + + profiler.popPush("render prep"); + boxGroup.preRender(renderEventParam); // called even if the group is inactive, so the group can be activate if desired + + // ignore inactive groups + if (!boxGroup.active) + { + continue; + } + + // allow API users to cancel this object's rendering + boolean cancelRendering = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericObjectRenderEvent.class, new DhApiBeforeGenericObjectRenderEvent.EventParam(renderEventParam, boxGroup)); + if (cancelRendering) + { + continue; + } + + // update instanced data if needed + { + boxGroup.tryUpdateInstancedDataAsync(); + + // skip groups that haven't been uploaded yet + if (boxGroup.vertexBufferContainer.getState() != IDhGenericObjectVertexBufferContainer.EState.RENDER) + { + continue; + } + } + + + DhApiRenderableBoxGroupShading shading = boxGroup.shading; + if (shading == null) + { + shading = DEFAULT_SHADING; + } + + // uniforms + { + int uniformBufferSize = new Std140SizeCalculator() + .putIVec3() // uOffsetChunk + .putVec3() // uOffsetSubChunk + .putIVec3() // uCameraPosChunk + .putVec3() // uCameraPosSubChunk + + .putVec3() // aTranslateChunk + .putVec3() // aTranslateSubChunk + + .putMat4f() // uProjectionMvm + .putInt() // uSkyLight + .putInt() // uBlockLight + + .putFloat() // uNorthShading + .putFloat() // uSouthShading + .putFloat() // uEastShading + .putFloat() // uWestShading + .putFloat() // uTopShading + .putFloat() // uBottomShading + .get(); + + + // create data // + + Mat4f projectionMvmMatrix = new Mat4f(renderEventParam.dhProjectionMatrix); + projectionMvmMatrix.multiply(renderEventParam.dhModelViewMatrix); + + + // upload data // + + ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize); + buffer.order(ByteOrder.nativeOrder()); + buffer = Std140Builder.intoBuffer(buffer) + .putIVec3( + LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().x), + LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().y), + LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().z) + ) // uOffsetChunk + .putVec3( + LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().x), + LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().y), + LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().z) + ) // uOffsetSubChunk + .putIVec3( + LodUtil.getChunkPosFromDouble(camPos.x), + LodUtil.getChunkPosFromDouble(camPos.y), + LodUtil.getChunkPosFromDouble(camPos.z) + ) // uCameraPosChunk + .putVec3( + LodUtil.getSubChunkPosFromDouble(camPos.x), + LodUtil.getSubChunkPosFromDouble(camPos.y), + LodUtil.getSubChunkPosFromDouble(camPos.z) + ) // uCameraPosSubChunk + + .putMat4f(projectionMvmMatrix.createJomlMatrix()) // uProjectionMvm + .putInt(boxGroup.getSkyLight()) // uSkyLight + .putInt(boxGroup.getBlockLight()) // uBlockLight + + .putFloat(shading.north) + .putFloat(shading.south) + .putFloat(shading.east) + .putFloat(shading.west) + .putFloat(shading.top) + .putFloat(shading.bottom) + + .get() + ; + + this.vertUniformBuffer = BlazeUniformUtil.createBuffer("vertUniformBlock", uniformBufferSize, this.vertUniformBuffer); + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.vertUniformBuffer, 0, uniformBufferSize); + + COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); + } + + + + + // render // + + profiler.popPush("rendering"); + try (IProfilerWrapper.IProfileBlock namespace_profile = profiler.push(boxGroup.getResourceLocationNamespace()); + IProfilerWrapper.IProfileBlock location_profile = profiler.push(boxGroup.getResourceLocationPath())) + { + this.renderBoxGroupInstanced(renderEventParam, boxGroup, profiler); + } + + boxGroup.postRender(renderEventParam); } - - DhApiRenderableBoxGroupShading shading = boxGroup.shading; - if (shading == null) - { - shading = DEFAULT_SHADING; - } - - // uniforms - { - int uniformBufferSize = new Std140SizeCalculator() - .putIVec3() // uOffsetChunk - .putVec3() // uOffsetSubChunk - .putIVec3() // uCameraPosChunk - .putVec3() // uCameraPosSubChunk - - .putVec3() // aTranslateChunk - .putVec3() // aTranslateSubChunk - - .putMat4f() // uProjectionMvm - .putInt() // uSkyLight - .putInt() // uBlockLight - - .putFloat() // uNorthShading - .putFloat() // uSouthShading - .putFloat() // uEastShading - .putFloat() // uWestShading - .putFloat() // uTopShading - .putFloat() // uBottomShading - .get(); - - - // create data // - - Mat4f projectionMvmMatrix = new Mat4f(renderEventParam.dhProjectionMatrix); - projectionMvmMatrix.multiply(renderEventParam.dhModelViewMatrix); - - - // upload data // - - ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize); - buffer.order(ByteOrder.nativeOrder()); - buffer = Std140Builder.intoBuffer(buffer) - .putIVec3( - LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().x), - LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().y), - LodUtil.getChunkPosFromDouble(boxGroup.getOriginBlockPos().z) - ) // uOffsetChunk - .putVec3( - LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().x), - LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().y), - LodUtil.getSubChunkPosFromDouble(boxGroup.getOriginBlockPos().z) - ) // uOffsetSubChunk - .putIVec3( - LodUtil.getChunkPosFromDouble(camPos.x), - LodUtil.getChunkPosFromDouble(camPos.y), - LodUtil.getChunkPosFromDouble(camPos.z) - ) // uCameraPosChunk - .putVec3( - LodUtil.getSubChunkPosFromDouble(camPos.x), - LodUtil.getSubChunkPosFromDouble(camPos.y), - LodUtil.getSubChunkPosFromDouble(camPos.z) - ) // uCameraPosSubChunk - - .putMat4f(projectionMvmMatrix.createJomlMatrix()) // uProjectionMvm - .putInt(boxGroup.getSkyLight()) // uSkyLight - .putInt(boxGroup.getBlockLight()) // uBlockLight - - .putFloat(shading.north) - .putFloat(shading.south) - .putFloat(shading.east) - .putFloat(shading.west) - .putFloat(shading.top) - .putFloat(shading.bottom) - - .get() - ; - - this.vertUniformBuffer = BlazeUniformUtil.createBuffer("vertUniformBlock", uniformBufferSize, this.vertUniformBuffer); - GpuBufferSlice bufferSlice = new GpuBufferSlice(this.vertUniformBuffer, 0, uniformBufferSize); - - COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); - } + //#endregion + //==========// + // clean up // + //==========// + //region - // render // + profiler.popPush("cleanup"); - profiler.popPush("rendering"); - profiler.push(boxGroup.getResourceLocationNamespace()); - profiler.push(boxGroup.getResourceLocationPath()); + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderCleanupEvent.class, renderEventParam); - this.renderBoxGroupInstanced(renderEventParam, boxGroup, profiler); - - profiler.pop(); // resource path - profiler.pop(); // resource namespace - - boxGroup.postRender(renderEventParam); + //endregion } - - //#endregion - - - - //==========// - // clean up // - //==========// - //region - - profiler.popPush("cleanup"); - - ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderCleanupEvent.class, renderEventParam); - - profiler.pop(); - - //endregion } private String getRenderPassName() { return "distantHorizons:McGenericObjectRenderer"; } @@ -543,8 +546,6 @@ public class BlazeDhGenericObjectRenderer implements IDhGenericRenderer // update instance data // - profiler.push("vertex setup"); - BlazeGenericObjectVertexContainer container = (BlazeGenericObjectVertexContainer) boxGroup.vertexBufferContainer; LightMapWrapper lightMapWrapper = (LightMapWrapper) renderEventParam.lightmap; @@ -554,7 +555,6 @@ public class BlazeDhGenericObjectRenderer implements IDhGenericRenderer // Bind instance data // - profiler.popPush("binding"); renderPass.setUniform("vertUniformBlock", this.vertUniformBuffer); @@ -566,7 +566,6 @@ public class BlazeDhGenericObjectRenderer implements IDhGenericRenderer renderPass.setVertexBuffer(0, container.vboGpuBuffer); // Draw instanced - profiler.popPush("render"); if (container.uploadedBoxCount > 0) { renderPass.drawIndexed( @@ -577,7 +576,6 @@ public class BlazeDhGenericObjectRenderer implements IDhGenericRenderer } } - profiler.pop(); } //endregion @@ -618,5 +616,27 @@ public class BlazeDhGenericObjectRenderer implements IDhGenericRenderer + //================// + // base overrides // + //================// + //region + + @Override + public void close() + { + // close is called outside the render thread and buffer closing must be done on the render thread + RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("Generic Obj Cleanup", () -> + { + if (this.vertUniformBuffer != null) + { + this.vertUniformBuffer.close(); + } + }); + } + + //endregion + + + } #endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhMetaRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhMetaRenderer.java index d6d551539..d5f47624c 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhMetaRenderer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhMetaRenderer.java @@ -6,13 +6,16 @@ public class BlazeDhMetaRenderer {} #else import com.mojang.blaze3d.textures.GpuTexture; +import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterColorDepthTextureCreatedEvent; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiTextureCreatedParam; import com.seibel.distanthorizons.common.render.blaze.apply.BlazeDhApplyRenderer; import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.render.RenderParams; -import com.seibel.distanthorizons.core.util.ColorUtil; +import com.seibel.distanthorizons.coreapi.util.ColorUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer; +import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import net.minecraft.client.Minecraft; import java.awt.*; @@ -57,9 +60,24 @@ public class BlazeDhMetaRenderer implements IDhMetaRenderer @Override public void runRenderPassSetup(RenderParams renderParams) { - // textures - this.dhDepthTextureWrapper.tryCreateOrResize(); - this.dhColorTextureWrapper.tryCreateOrResize(); + int oldWidth = this.dhDepthTextureWrapper.getWidth(); + int oldHeight = this.dhDepthTextureWrapper.getHeight(); + + boolean texturesChanged = false; + texturesChanged = this.dhDepthTextureWrapper.tryCreateOrResize() | texturesChanged; + texturesChanged = this.dhColorTextureWrapper.tryCreateOrResize() | texturesChanged; + + if (texturesChanged) + { + int newTextureWidth = MC_RENDER.getTargetFramebufferViewportWidth(); + int newTextureHeight = MC_RENDER.getTargetFramebufferViewportHeight(); + + DhApiTextureCreatedParam textureCreatedParam = new DhApiTextureCreatedParam( + oldWidth, oldHeight, + newTextureWidth, newTextureHeight + ); + ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterColorDepthTextureCreatedEvent.class, textureCreatedParam); + } } @Override diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhTerrainRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhTerrainRenderer.java index 90bb7539a..989225473 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhTerrainRenderer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/BlazeDhTerrainRenderer.java @@ -11,7 +11,6 @@ import com.mojang.blaze3d.buffers.Std140Builder; import com.mojang.blaze3d.buffers.Std140SizeCalculator; import com.mojang.blaze3d.pipeline.BlendFunction; import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.platform.DepthTestFunction; import com.mojang.blaze3d.platform.PolygonMode; import com.mojang.blaze3d.shaders.UniformType; import com.mojang.blaze3d.systems.CommandEncoder; @@ -20,20 +19,19 @@ import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.VertexFormat; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeBufferRenderEvent; +import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeRenderPassEvent; import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil; import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil; +import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper; import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper; import com.seibel.distanthorizons.common.render.blaze.wrappers.uniform.BlazeLodUniformBufferWrapper; import com.seibel.distanthorizons.common.render.blaze.wrappers.buffer.BlazeVertexBufferWrapper; import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer; -import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodQuadBuilder; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhSectionPos; -import com.seibel.distanthorizons.common.render.openGl.glObject.enums.GLEnums; -import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GlQuadIndexBuffer; import com.seibel.distanthorizons.core.render.RenderParams; import com.seibel.distanthorizons.core.util.RenderUtil; import com.seibel.distanthorizons.core.util.math.Mat4f; @@ -45,7 +43,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTe import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import net.minecraft.resources.Identifier; -import org.lwjgl.opengl.GL32; import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; @@ -89,35 +86,36 @@ public class BlazeDhTerrainRenderer implements IDhTerrainRenderer - VertexFormat vertexFormat = VertexFormat.builder() - .add("vPosition", BlazeDhVertexFormatUtil.SHORT_XYZ_POS) - .add("meta", BlazeDhVertexFormatUtil.META) - .add("vColor", BlazeDhVertexFormatUtil.RGBA_UBYTE_COLOR) - .add("irisMaterial", BlazeDhVertexFormatUtil.IRIS_MATERIAL) - .add("irisNormal", BlazeDhVertexFormatUtil.IRIS_NORMAL) - .add("paddingTwo", BlazeDhVertexFormatUtil.BYTE_PAD) - .add("paddingThree", BlazeDhVertexFormatUtil.BYTE_PAD) // padding is to make sure the format is a multiple of 4 - .build(); - - RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper(); { - pipelineBuilder.withCull(true); + pipelineBuilder.withFaceCulling(true); pipelineBuilder.withDepthWrite(true); - pipelineBuilder.withDepthTestFunction(DepthTestFunction.LESS_DEPTH_TEST); + pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.LESS); pipelineBuilder.withColorWrite(true); - pipelineBuilder.withPolygonMode(PolygonMode.FILL); - pipelineBuilder.withLocation(Identifier.parse("distanthorizons:lod_render")); - - pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "lod/blaze/vert")); - pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "lod/blaze/frag")); + pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.FILL); + pipelineBuilder.withName("terrain"); pipelineBuilder.withSampler("uLightMap"); - pipelineBuilder.withUniform("vertUniqueUniformBlock", UniformType.UNIFORM_BUFFER); - pipelineBuilder.withUniform("vertSharedUniformBlock", UniformType.UNIFORM_BUFFER); - pipelineBuilder.withUniform("fragUniformBlock", UniformType.UNIFORM_BUFFER); + pipelineBuilder.withVertexShader("lod/blaze/vert"); + pipelineBuilder.withFragmentShader("lod/blaze/frag"); - pipelineBuilder.withVertexFormat(vertexFormat, VertexFormat.Mode.TRIANGLES); + pipelineBuilder.withUniformBuffer("vertUniqueUniformBlock"); + pipelineBuilder.withUniformBuffer("vertSharedUniformBlock"); + pipelineBuilder.withUniformBuffer("fragUniformBlock"); + + VertexFormat vertexFormat = VertexFormat.builder() + .add("vPosition", BlazeDhVertexFormatUtil.SHORT_XYZ_POS) + .add("meta", BlazeDhVertexFormatUtil.META) + .add("vColor", BlazeDhVertexFormatUtil.RGBA_UBYTE_COLOR) + .add("irisMaterial", BlazeDhVertexFormatUtil.IRIS_MATERIAL) + .add("irisNormal", BlazeDhVertexFormatUtil.IRIS_NORMAL) + .add("paddingTwo", BlazeDhVertexFormatUtil.BYTE_PAD) + .add("paddingThree", BlazeDhVertexFormatUtil.BYTE_PAD) // padding is to make sure the format is a multiple of 4 + .build(); + pipelineBuilder.withVertexFormat(vertexFormat); + + pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLES); } // opaque @@ -128,6 +126,7 @@ public class BlazeDhTerrainRenderer implements IDhTerrainRenderer // transparent { + // TRANSLUCENT = new BlendFunction(SourceFactor.SRC_ALPHA, DestFactor.ONE_MINUS_SRC_ALPHA, SourceFactor.ONE, DestFactor.ONE_MINUS_SRC_ALPHA); pipelineBuilder.withBlend(BlendFunction.TRANSLUCENT); this.transparentPipeline = pipelineBuilder.build(); } @@ -153,207 +152,208 @@ public class BlazeDhTerrainRenderer implements IDhTerrainRenderer { this.tryInit(); - - profiler.push("vert unique uniforms"); + try(IProfilerWrapper.IProfileBlock terrain_profile = profiler.push("terrain render")) { - // create data // - - for (int lodIndex = 0; lodIndex < bufferContainers.size(); lodIndex++) + profiler.popPush("vert unique uniforms"); { - LodBufferContainer bufferContainer = bufferContainers.get(lodIndex); - bufferContainer.uniformContainer.tryUpload(); - } - } - - profiler.popPush("vert share uniforms"); - { - Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix); - combinedMatrix.multiply(renderEventParam.dhModelViewMatrix); - - float earthCurveRatio = Config.Client.Advanced.Graphics.Experimental.earthCurveRatio.get(); - if (earthCurveRatio < -1.0f || earthCurveRatio > 1.0f) - { - earthCurveRatio = /*6371KM*/ 6371000.0f / earthCurveRatio; - } - else - { - // disable curvature if the config value is between -1 and 1 - earthCurveRatio = 0.0f; - } - - - // upload data // - - int uniformBufferSize = new Std140SizeCalculator() - .putInt() // uIsWhiteWorld - - .putFloat() // uWorldYOffset - .putFloat() // uMircoOffset - .putFloat() // uEarthRadius - - .putVec3() // uCameraPos - .putMat4f() // uCombinedMatrix - .get(); - - ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize); - buffer.order(ByteOrder.nativeOrder()); - Std140Builder.intoBuffer(buffer) - .putInt(0) // uIsWhiteWorld - - .putFloat((float) renderEventParam.worldYOffset) // uWorldYOffset - .putFloat(0.01f) // uMircoOffset // 0.01 block offset - .putFloat(earthCurveRatio) // uEarthRadius - - .putVec3( - (float)renderEventParam.exactCameraPosition.x, - (float)renderEventParam.exactCameraPosition.y, - (float)renderEventParam.exactCameraPosition.z) // uCameraPos - .putMat4f(combinedMatrix.createJomlMatrix()) // uCombinedMatrix - .get(); - - this.vertSharedUniformBuffer = BlazeUniformUtil.createBuffer("vertSharedUniformBlock", uniformBufferSize, this.vertSharedUniformBuffer); - GpuBufferSlice bufferSlice = new GpuBufferSlice(this.vertSharedUniformBuffer, 0, uniformBufferSize); - - COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); - } - - profiler.popPush("set frag uniforms"); - { - int uniformBufferSize = new Std140SizeCalculator() - .putFloat() // uClipDistance - .putFloat() // uNoiseIntensity - .putInt() // uNoiseSteps - .putInt() // uNoiseDropoff - .putInt() // uDitherDhRendering - .putInt() // uNoiseEnabled - .get(); - - - // create data // - - float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocks(); - if (!Config.Client.Advanced.Debugging.lodOnlyMode.get()) - { - // this added value prevents the near clip plane and discard circle from touching, which looks bad - dhNearClipDistance += 16f; - } - - - // upload data // - - ByteBuffer buffer = ByteBuffer.allocateDirect(uniformBufferSize); - buffer.order(ByteOrder.nativeOrder()); - buffer = Std140Builder.intoBuffer(buffer) - .putFloat(dhNearClipDistance) // uClipDistance - .putFloat(Config.Client.Advanced.Graphics.NoiseTexture.noiseIntensity.get()) // uNoiseIntensity - .putInt(Config.Client.Advanced.Graphics.NoiseTexture.noiseSteps.get()) // uNoiseSteps - .putInt(Config.Client.Advanced.Graphics.NoiseTexture.noiseDropoff.get()) // uNoiseDropoff - .putInt(Config.Client.Advanced.Graphics.Quality.ditherDhFade.get() ? 1 : 0) // uDitherDhRendering - .putInt(Config.Client.Advanced.Graphics.NoiseTexture.enableNoiseTexture.get() ? 1 : 0) // uNoiseEnabled - .get() - ; - - this.fragUniformBuffer = BlazeUniformUtil.createBuffer("fragUniformBlock", uniformBufferSize, this.fragUniformBuffer); - GpuBufferSlice bufferSlice = new GpuBufferSlice(this.fragUniformBuffer, 0, uniformBufferSize); - - COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); - } - - - - // render pass setup - { - profiler.popPush("setup"); - - // create a render pass - try(RenderPass renderPass = COMMAND_ENCODER.createRenderPass( - this::getRenderPassName, - BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureView, - /*optionalClearColorAsInt*/ OptionalInt.empty(), - BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView, - /*optionalDepthValueAsDouble*/ OptionalDouble.empty()) - ) - { - LightMapWrapper lightMapWrapper = (LightMapWrapper) renderEventParam.lightmap; - BlazeTextureViewWrapper lightmapTextureViewWrapper = lightMapWrapper.getTextureViewWrapper(); - renderPass.bindTexture("uLightMap", lightmapTextureViewWrapper.textureView, lightmapTextureViewWrapper.textureSampler); - - // set pipeline - renderPass.setPipeline(opaquePass ? this.opaquePipeline : this.transparentPipeline); - - // shared uniforms - renderPass.setUniform("fragUniformBlock", this.fragUniformBuffer); - renderPass.setUniform("vertSharedUniformBlock", this.vertSharedUniformBuffer); - - + // create data // for (int lodIndex = 0; lodIndex < bufferContainers.size(); lodIndex++) { - profiler.popPush("binding"); - LodBufferContainer bufferContainer = bufferContainers.get(lodIndex); - BlazeLodUniformBufferWrapper uniformWrapper = (BlazeLodUniformBufferWrapper)bufferContainer.uniformContainer; - - boolean columnBuilderDebugEnabled = Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugEnable.get(); - if (columnBuilderDebugEnabled) - { - if (DhSectionPos.getDetailLevel(bufferContainer.pos) == Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugDetailLevel.get() - && DhSectionPos.getX(bufferContainer.pos) == Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugXPos.get() - && DhSectionPos.getZ(bufferContainer.pos) == Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugZPos.get()) - { - int breakpoint = 0; - } - else - { - continue; - } - } - - renderPass.setUniform("vertUniqueUniformBlock", uniformWrapper.gpuBuffer); - - - - profiler.popPush("rendering"); - - // render each buffer - IVertexBufferWrapper[] bufferWrapperList = opaquePass ? bufferContainer.vbos : bufferContainer.vbosTransparent; - for (int i = 0; i < bufferWrapperList.length; i++) - { - BlazeVertexBufferWrapper bufferWrapper = (BlazeVertexBufferWrapper) bufferWrapperList[i]; - if (!bufferWrapper.uploaded - || bufferWrapper.vertexCount == 0) - { - continue; - } - - // fire render event - { - Vec3d camPos = renderEventParam.exactCameraPosition; - Vec3f modelPos = new Vec3f( - (float) (bufferContainer.minCornerBlockPos.getX() - camPos.x), - (float) (bufferContainer.minCornerBlockPos.getY() - camPos.y), - (float) (bufferContainer.minCornerBlockPos.getZ() - camPos.z)); - ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos)); - } - - renderPass.setIndexBuffer(bufferWrapper.indexBuffer, VertexFormat.IndexType.INT); - renderPass.setVertexBuffer(0, bufferWrapper.vboGpuBuffer); // vertex buffer can only be "0" lol - - if (!bufferWrapper.vboGpuBuffer.isClosed()) - { - renderPass.drawIndexed( - /*indexStart*/ 0, - /*firstIndex*/0, - /*indexCount*/bufferWrapper.indexCount, - /*instanceCount*/1); - } - } + bufferContainer.uniformContainer.tryUpload(); + } + } + + profiler.popPush("vert share uniforms"); + { + Mat4f combinedMatrix = new Mat4f(renderEventParam.dhProjectionMatrix); + combinedMatrix.multiply(renderEventParam.dhModelViewMatrix); + + float earthCurveRatio = Config.Client.Advanced.Graphics.Experimental.earthCurveRatio.get(); + if (earthCurveRatio < -1.0f || earthCurveRatio > 1.0f) + { + earthCurveRatio = /*6371KM*/ 6371000.0f / earthCurveRatio; + } + else + { + // disable curvature if the config value is between -1 and 1 + earthCurveRatio = 0.0f; } + + // upload data // + + int uniformBufferSize = new Std140SizeCalculator() + .putInt() // uIsWhiteWorld + + .putFloat() // uWorldYOffset + .putFloat() // uMircoOffset + .putFloat() // uEarthRadius + + .putVec3() // uCameraPos + .putMat4f() // uCombinedMatrix + .get(); + + ByteBuffer buffer = MemoryUtil.memAlloc(uniformBufferSize); + buffer.order(ByteOrder.nativeOrder()); + Std140Builder.intoBuffer(buffer) + .putInt(0) // uIsWhiteWorld + + .putFloat((float) renderEventParam.worldYOffset) // uWorldYOffset + .putFloat(0.01f) // uMircoOffset // 0.01 block offset + .putFloat(earthCurveRatio) // uEarthRadius + + .putVec3( + (float) renderEventParam.exactCameraPosition.x, + (float) renderEventParam.exactCameraPosition.y, + (float) renderEventParam.exactCameraPosition.z) // uCameraPos + .putMat4f(combinedMatrix.createJomlMatrix()) // uCombinedMatrix + .get(); + + this.vertSharedUniformBuffer = BlazeUniformUtil.createBuffer("vertSharedUniformBlock", uniformBufferSize, this.vertSharedUniformBuffer); + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.vertSharedUniformBuffer, 0, uniformBufferSize); + + COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); + + MemoryUtil.memFree(buffer); + } + + profiler.popPush("set frag uniforms"); + { + int uniformBufferSize = new Std140SizeCalculator() + .putFloat() // uClipDistance + .putFloat() // uNoiseIntensity + .putInt() // uNoiseSteps + .putInt() // uNoiseDropoff + .putInt() // uDitherDhRendering + .putInt() // uNoiseEnabled + .get(); + + + // create data // + + float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocks(); + if (!Config.Client.Advanced.Debugging.lodOnlyMode.get()) + { + // this added value prevents the near clip plane and discard circle from touching, which looks bad + dhNearClipDistance += 16f; + } + + + // upload data // + + ByteBuffer buffer = MemoryUtil.memAlloc(uniformBufferSize); + buffer.order(ByteOrder.nativeOrder()); + buffer = Std140Builder.intoBuffer(buffer) + .putFloat(dhNearClipDistance) // uClipDistance + .putFloat(Config.Client.Advanced.Graphics.NoiseTexture.noiseIntensity.get()) // uNoiseIntensity + .putInt(Config.Client.Advanced.Graphics.NoiseTexture.noiseSteps.get()) // uNoiseSteps + .putInt(Config.Client.Advanced.Graphics.NoiseTexture.noiseDropoff.get()) // uNoiseDropoff + .putInt(Config.Client.Advanced.Graphics.Quality.ditherDhFade.get() ? 1 : 0) // uDitherDhRendering + .putInt(Config.Client.Advanced.Graphics.NoiseTexture.enableNoiseTexture.get() ? 1 : 0) // uNoiseEnabled + .get() + ; + + this.fragUniformBuffer = BlazeUniformUtil.createBuffer("fragUniformBlock", uniformBufferSize, this.fragUniformBuffer); + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.fragUniformBuffer, 0, uniformBufferSize); + + COMMAND_ENCODER.writeToBuffer(bufferSlice, buffer); + MemoryUtil.memFree(buffer); + } + + + + // render pass setup + { + profiler.popPush("rendering"); + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam); + + // create a render pass + try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass( + this::getRenderPassName, + BlazeDhMetaRenderer.INSTANCE.dhColorTextureWrapper.textureView, + /*optionalClearColorAsInt*/ OptionalInt.empty(), + BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView, + /*optionalDepthValueAsDouble*/ OptionalDouble.empty()) + ) + { + LightMapWrapper lightMapWrapper = (LightMapWrapper) renderEventParam.lightmap; + BlazeTextureViewWrapper lightmapTextureViewWrapper = lightMapWrapper.getTextureViewWrapper(); + renderPass.bindTexture("uLightMap", lightmapTextureViewWrapper.textureView, lightmapTextureViewWrapper.textureSampler); + + // set pipeline + renderPass.setPipeline(opaquePass ? this.opaquePipeline : this.transparentPipeline); + + // shared uniforms + renderPass.setUniform("fragUniformBlock", this.fragUniformBuffer); + renderPass.setUniform("vertSharedUniformBlock", this.vertSharedUniformBuffer); + + + + for (int lodIndex = 0; lodIndex < bufferContainers.size(); lodIndex++) + { + LodBufferContainer bufferContainer = bufferContainers.get(lodIndex); + BlazeLodUniformBufferWrapper uniformWrapper = (BlazeLodUniformBufferWrapper) bufferContainer.uniformContainer; + + boolean columnBuilderDebugEnabled = Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugEnable.get(); + if (columnBuilderDebugEnabled) + { + if (DhSectionPos.getDetailLevel(bufferContainer.pos) == Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugDetailLevel.get() + && DhSectionPos.getX(bufferContainer.pos) == Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugXPos.get() + && DhSectionPos.getZ(bufferContainer.pos) == Config.Client.Advanced.Debugging.ColumnBuilderDebugging.columnBuilderDebugZPos.get()) + { + int breakpoint = 0; + } + else + { + continue; + } + } + + renderPass.setUniform("vertUniqueUniformBlock", uniformWrapper.gpuBuffer); + + + + // render each buffer + IVertexBufferWrapper[] bufferWrapperList = opaquePass ? bufferContainer.vboOpaqueWrappers : bufferContainer.vboTransparentWrappers; + for (int i = 0; i < bufferWrapperList.length; i++) + { + BlazeVertexBufferWrapper bufferWrapper = (BlazeVertexBufferWrapper) bufferWrapperList[i]; + if (!bufferWrapper.uploaded + || bufferWrapper.vertexCount == 0) + { + continue; + } + + // fire render event + { + Vec3d camPos = renderEventParam.exactCameraPosition; + Vec3f modelPos = new Vec3f( + (float) (bufferContainer.minCornerBlockPos.getX() - camPos.x), + (float) (bufferContainer.minCornerBlockPos.getY() - camPos.y), + (float) (bufferContainer.minCornerBlockPos.getZ() - camPos.z)); + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos)); + } + + renderPass.setIndexBuffer(bufferWrapper.getIndexGpuBuffer(), VertexFormat.IndexType.INT); + renderPass.setVertexBuffer(0, bufferWrapper.vertexGpuBuffer); // vertex buffer can only be "0" lol + + if (!bufferWrapper.vertexGpuBuffer.isClosed()) + { + renderPass.drawIndexed( + /*indexStart*/ 0, + /*firstIndex*/0, + /*indexCount*/bufferWrapper.indexCount, + /*instanceCount*/1); + } + } + } + + } } } - - profiler.pop(); } private String getIndexBufferName() { return "distantHorizons:LodIndexBuffer"; } private String getRenderPassName() { return "distantHorizons:McLodRenderer"; } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/apply/BlazeDhApplyRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/apply/BlazeDhApplyRenderer.java index 83accc661..239cb2ac7 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/apply/BlazeDhApplyRenderer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/apply/BlazeDhApplyRenderer.java @@ -27,9 +27,6 @@ public class BlazeDhApplyRenderer {} import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.pipeline.BlendFunction; import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.platform.DepthTestFunction; -import com.mojang.blaze3d.platform.PolygonMode; -import com.mojang.blaze3d.shaders.UniformType; import com.mojang.blaze3d.systems.CommandEncoder; import com.mojang.blaze3d.systems.GpuDevice; import com.mojang.blaze3d.systems.RenderPass; @@ -37,12 +34,13 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.*; import com.mojang.blaze3d.vertex.VertexFormat; import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil; +import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper; import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper; import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil; +import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.coreapi.ModInfo; -import net.minecraft.resources.Identifier; import org.jetbrains.annotations.Nullable; import java.util.Arrays; @@ -81,6 +79,10 @@ public class BlazeDhApplyRenderer private final BlazeTextureViewWrapper sourceDepthTextureViewWrapper = new BlazeTextureViewWrapper(); private final BlazeTextureViewWrapper destinationColorTextureViewWrapper = new BlazeTextureViewWrapper(); + /** We don't want to actually write any depth data, but blaze3D complains if we don't bind a depth texture. */ + private final BlazeTextureWrapper dummyDepthTextureWrapper = BlazeTextureWrapper.createDepth("apply_dummy_depth"); + + /** * Can be set for special application shaders that need @@ -134,8 +136,12 @@ public class BlazeDhApplyRenderer GpuTexture sourceDepthTexture, GpuTexture destinationColorTexture) { - this.createPipeline(); - this.vboGpuBuffer = BlazePostProcessUtil.createAndUploadScreenVertexData(this.name); + // one-time setup + if (this.pipeline == null) + { + this.createPipeline(); + this.vboGpuBuffer = BlazePostProcessUtil.createAndUploadScreenVertexData(this.name); + } this.sourceColorTextureViewWrapper.tryWrap(sourceColorTexture); this.sourceDepthTextureViewWrapper.tryWrap(sourceDepthTexture); @@ -145,20 +151,11 @@ public class BlazeDhApplyRenderer } private void createPipeline() { - if (this.pipeline != null) + RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper(); { - return; - } - - VertexFormat vertexFormat = VertexFormat.builder() - .add("vPosition", BlazeDhVertexFormatUtil.SCREEN_POS) - .build(); - - RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); - { - pipelineBuilder.withCull(false); + pipelineBuilder.withFaceCulling(false); pipelineBuilder.withDepthWrite(false); - pipelineBuilder.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST); + pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.NONE); pipelineBuilder.withColorWrite(true); if (this.blendFunction != null) @@ -170,23 +167,26 @@ public class BlazeDhApplyRenderer pipelineBuilder.withoutBlend(); } - pipelineBuilder.withPolygonMode(PolygonMode.FILL); - pipelineBuilder.withLocation(Identifier.parse(this.identifierName)); // TODO will complain if capital letters are included + pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.FILL); + pipelineBuilder.withName(this.name); - // TODO manually validate paths to confirm they exist and end with ".fsh" or ".vsh", MC silently fails if the files are missing/improperly named - pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", this.vertexShaderPath)); - pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", this.fragmentShaderPath)); + pipelineBuilder.withVertexShader(this.vertexShaderPath); + pipelineBuilder.withFragmentShader(this.fragmentShaderPath); for (int i = 0; i < this.uniformNames.length; i++) { String uniformName = this.uniformNames[i]; - pipelineBuilder.withUniform(uniformName, UniformType.UNIFORM_BUFFER); + pipelineBuilder.withUniformBuffer(uniformName); } pipelineBuilder.withSampler("uSourceColorTexture"); pipelineBuilder.withSampler("uSourceDepthTexture"); - pipelineBuilder.withVertexFormat(vertexFormat, VertexFormat.Mode.TRIANGLE_FAN); + VertexFormat vertexFormat = VertexFormat.builder() + .add("vPosition", BlazeDhVertexFormatUtil.SCREEN_POS) + .build(); + pipelineBuilder.withVertexFormat(vertexFormat); + pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLE_FAN); } this.pipeline = pipelineBuilder.build(); } @@ -223,11 +223,13 @@ public class BlazeDhApplyRenderer { this.tryInit(sourceColorTexture, sourceDepthTexture, destinationColorTexture); + this.dummyDepthTextureWrapper.tryCreateOrResize(); + try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass( this::getIdentifierName, this.destinationColorTextureViewWrapper.textureView, /*optionalClearColorAsInt*/ OptionalInt.empty(), - /*depthTexture*/ null, + this.dummyDepthTextureWrapper.textureView, /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) { renderPass.bindTexture("uSourceColorTexture", this.sourceColorTextureViewWrapper.textureView, this.sourceColorTextureViewWrapper.textureSampler); diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/apply/BlazeDhCopyRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/apply/BlazeDhCopyRenderer.java index 7286a0758..5f9469902 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/apply/BlazeDhCopyRenderer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/apply/BlazeDhCopyRenderer.java @@ -26,20 +26,17 @@ public class BlazeDhCopyRenderer {} import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.platform.DepthTestFunction; -import com.mojang.blaze3d.platform.PolygonMode; import com.mojang.blaze3d.systems.CommandEncoder; import com.mojang.blaze3d.systems.GpuDevice; import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.*; -import com.mojang.blaze3d.vertex.VertexFormat; +import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper; import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper; import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper; import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import net.minecraft.resources.Identifier; import java.util.OptionalDouble; import java.util.OptionalInt; @@ -63,6 +60,8 @@ public class BlazeDhCopyRenderer private GpuBuffer vboGpuBuffer; + private BlazeTextureWrapper dummyDepthTextureWrapper; + //=============// @@ -81,23 +80,25 @@ public class BlazeDhCopyRenderer this.init = true; + this.dummyDepthTextureWrapper = BlazeTextureWrapper.createDepth("dh_copy_depth_texture"); - RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper(); { - pipelineBuilder.withCull(false); + pipelineBuilder.withFaceCulling(false); pipelineBuilder.withDepthWrite(false); - pipelineBuilder.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST); + pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.NONE); pipelineBuilder.withColorWrite(true); pipelineBuilder.withoutBlend(); - pipelineBuilder.withPolygonMode(PolygonMode.FILL); - pipelineBuilder.withLocation(Identifier.parse("distanthorizons:copy_render")); + pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.FILL); + pipelineBuilder.withName("copy"); - pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "copy/blaze/vert")); - pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "copy/blaze/frag")); + pipelineBuilder.withVertexShader("copy/blaze/vert"); + pipelineBuilder.withFragmentShader("copy/blaze/frag"); pipelineBuilder.withSampler("uCopyTexture"); - pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat(), VertexFormat.Mode.TRIANGLE_FAN); + pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat()); + pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLE_FAN); } this.pipeline = pipelineBuilder.build(); @@ -139,11 +140,13 @@ public class BlazeDhCopyRenderer { this.tryInit(); + this.dummyDepthTextureWrapper.tryCreateOrResize(); + try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass( this::getRenderPassName, destinationTextureView, /*optionalClearColorAsInt*/ OptionalInt.empty(), - /*depthTexture*/ null, + this.dummyDepthTextureWrapper.textureView, /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) { renderPass.bindTexture("uCopyTexture", sourceTextureView, sourceTextureSampler); diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/objects/BlazeGenericObjectVertexContainer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/objects/BlazeGenericObjectVertexContainer.java index 58954ced6..4b0fab8a9 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/objects/BlazeGenericObjectVertexContainer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/objects/BlazeGenericObjectVertexContainer.java @@ -17,7 +17,7 @@ import com.seibel.distanthorizons.common.render.openGl.glObject.enums.GLEnums; import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler; import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IDhGenericObjectVertexBufferContainer; import com.seibel.distanthorizons.core.render.renderer.RenderableBoxGroup; -import com.seibel.distanthorizons.core.util.ColorUtil; +import com.seibel.distanthorizons.coreapi.util.ColorUtil; import org.lwjgl.opengl.GL32; import java.nio.ByteBuffer; @@ -179,6 +179,11 @@ public class BlazeGenericObjectVertexContainer implements IDhGenericObjectVertex this.vertexBuffer.put(a); this.vertexBuffer.put(box.material); + + // padding so the vertex format's byte count is a multiple of 4 + this.vertexBuffer.put((byte)0); + this.vertexBuffer.put((byte)0); + this.vertexBuffer.put((byte)0); } } this.vertexBuffer.flip(); diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhFarFadeRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhFarFadeRenderer.java index 8d1d0a451..5839e9800 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhFarFadeRenderer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhFarFadeRenderer.java @@ -29,18 +29,15 @@ import com.mojang.blaze3d.buffers.GpuBufferSlice; import com.mojang.blaze3d.buffers.Std140Builder; import com.mojang.blaze3d.buffers.Std140SizeCalculator; import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.platform.DepthTestFunction; -import com.mojang.blaze3d.platform.PolygonMode; -import com.mojang.blaze3d.shaders.UniformType; import com.mojang.blaze3d.systems.CommandEncoder; import com.mojang.blaze3d.systems.GpuDevice; import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.VertexFormat; import com.seibel.distanthorizons.common.render.blaze.BlazeDhMetaRenderer; import com.seibel.distanthorizons.common.render.blaze.apply.BlazeDhCopyRenderer; import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil; import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil; +import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper; import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper; import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper; import com.seibel.distanthorizons.core.logging.DhLogger; @@ -50,7 +47,6 @@ import com.seibel.distanthorizons.core.util.RenderUtil; import com.seibel.distanthorizons.core.util.math.Mat4f; import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhFarFadeRenderer; import net.minecraft.client.Minecraft; -import net.minecraft.resources.Identifier; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -76,8 +72,11 @@ public class BlazeDhFarFadeRenderer implements IDhFarFadeRenderer private GpuBuffer vboGpuBuffer; - public final BlazeTextureWrapper dhFadeColorTextureWrapper = BlazeTextureWrapper.createColor("DhFadeColorTexture"); - public final BlazeTextureViewWrapper mcColorTextureViewWrapper = new BlazeTextureViewWrapper(); + private final BlazeTextureWrapper dhFadeColorTextureWrapper = BlazeTextureWrapper.createColor("dh_far_fade_color_texture"); + /** We don't want to actually write any depth data, but blaze3D complains if we don't bind a depth texture. */ + private final BlazeTextureWrapper dhFadeDepthTextureWrapper = BlazeTextureWrapper.createDepth("dh_far_fade_depth_texture"); + + private final BlazeTextureViewWrapper mcColorTextureViewWrapper = new BlazeTextureViewWrapper(); @@ -98,27 +97,28 @@ public class BlazeDhFarFadeRenderer implements IDhFarFadeRenderer - RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper(); { - pipelineBuilder.withCull(false); + pipelineBuilder.withFaceCulling(false); pipelineBuilder.withDepthWrite(false); - pipelineBuilder.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST); + pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.NONE); pipelineBuilder.withColorWrite(true); pipelineBuilder.withoutBlend(); - pipelineBuilder.withPolygonMode(PolygonMode.FILL); - pipelineBuilder.withLocation(Identifier.parse("distanthorizons:far_fade")); + pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.FILL); + pipelineBuilder.withName("far_fade"); - pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "fade/blaze/vert")); - pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "fade/blaze/dh_fade")); + pipelineBuilder.withVertexShader("fade/blaze/vert"); + pipelineBuilder.withFragmentShader("fade/blaze/dh_fade"); pipelineBuilder.withSampler("uMcColorTexture"); pipelineBuilder.withSampler("uDhDepthTexture"); pipelineBuilder.withSampler("uDhColorTexture"); - pipelineBuilder.withUniform("fragUniformBlock", UniformType.UNIFORM_BUFFER); + pipelineBuilder.withUniformBuffer("fragUniformBlock"); - pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat(), VertexFormat.Mode.TRIANGLE_FAN); + pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat()); + pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLE_FAN); } this.pipeline = pipelineBuilder.build(); @@ -153,6 +153,8 @@ public class BlazeDhFarFadeRenderer implements IDhFarFadeRenderer this.dhFadeColorTextureWrapper.tryCreateOrResize(); this.mcColorTextureViewWrapper.tryWrap(Minecraft.getInstance().getMainRenderTarget().getColorTexture()); + this.dhFadeDepthTextureWrapper.tryCreateOrResize(); + { int uniformBufferSize = new Std140SizeCalculator() .putFloat() // uStartFadeBlockDistance @@ -206,7 +208,7 @@ public class BlazeDhFarFadeRenderer implements IDhFarFadeRenderer this::getRenderPassName, this.dhFadeColorTextureWrapper.textureView, /*optionalClearColorAsInt*/ OptionalInt.empty(), - /*depthTexture*/ null, + this.dhFadeDepthTextureWrapper.textureView, /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) { // MC texture diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhFogRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhFogRenderer.java index 4d20e9c65..a91025ae2 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhFogRenderer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhFogRenderer.java @@ -30,21 +30,18 @@ import com.mojang.blaze3d.buffers.Std140Builder; import com.mojang.blaze3d.buffers.Std140SizeCalculator; import com.mojang.blaze3d.pipeline.BlendFunction; import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.platform.DepthTestFunction; import com.mojang.blaze3d.platform.DestFactor; -import com.mojang.blaze3d.platform.PolygonMode; import com.mojang.blaze3d.platform.SourceFactor; -import com.mojang.blaze3d.shaders.UniformType; import com.mojang.blaze3d.systems.CommandEncoder; import com.mojang.blaze3d.systems.GpuDevice; import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.VertexFormat; import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogColorMode; import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogDirection; import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode; import com.seibel.distanthorizons.common.render.blaze.BlazeDhMetaRenderer; import com.seibel.distanthorizons.common.render.blaze.apply.BlazeDhApplyRenderer; +import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper; import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper; import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil; import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil; @@ -58,7 +55,6 @@ import com.seibel.distanthorizons.core.util.math.Mat4f; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhFogRenderer; -import net.minecraft.resources.Identifier; import java.awt.*; import java.nio.ByteBuffer; @@ -91,7 +87,9 @@ public class BlazeDhFogRenderer implements IDhFogRenderer private GpuBuffer vboGpuBuffer; - public BlazeTextureWrapper fogColorTextureWrapper = BlazeTextureWrapper.createColor("DhFogColorTexture"); + private final BlazeTextureWrapper fogColorTextureWrapper = BlazeTextureWrapper.createColor("dh_fog_color_texture"); + /** We don't want to actually write any depth data, but blaze3D complains if we don't bind a depth texture. */ + private final BlazeTextureWrapper fogDepthTextureWrapper = BlazeTextureWrapper.createDepth("dh_fog_depth_texture"); @@ -119,24 +117,25 @@ public class BlazeDhFogRenderer implements IDhFogRenderer "apply/blaze/vert", "apply/blaze/frag" ); - RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper(); { - pipelineBuilder.withCull(false); + pipelineBuilder.withFaceCulling(false); pipelineBuilder.withDepthWrite(false); - pipelineBuilder.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST); + pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.NONE); pipelineBuilder.withColorWrite(true); pipelineBuilder.withoutBlend(); - pipelineBuilder.withPolygonMode(PolygonMode.FILL); - pipelineBuilder.withLocation(Identifier.parse("distanthorizons:fog_render")); + pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.FILL); + pipelineBuilder.withName("fog_render"); - pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "fog/blaze/vert")); - pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "fog/blaze/frag")); + pipelineBuilder.withVertexShader("fog/blaze/vert"); + pipelineBuilder.withFragmentShader("fog/blaze/frag"); pipelineBuilder.withSampler("uDhDepthTexture"); - pipelineBuilder.withUniform("fragUniformBlock", UniformType.UNIFORM_BUFFER); + pipelineBuilder.withUniformBuffer("fragUniformBlock"); - pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat(), VertexFormat.Mode.TRIANGLE_FAN); + pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat()); + pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLE_FAN); } this.pipeline = pipelineBuilder.build(); @@ -168,7 +167,7 @@ public class BlazeDhFogRenderer implements IDhFogRenderer this.fogColorTextureWrapper.tryCreateOrResize(); - + this.fogDepthTextureWrapper.tryCreateOrResize(); { int uniformBufferSize = new Std140SizeCalculator() @@ -339,7 +338,7 @@ public class BlazeDhFogRenderer implements IDhFogRenderer this::getRenderPassName, this.fogColorTextureWrapper.textureView, /*optionalClearColorAsInt*/ OptionalInt.empty(), - /*depthTexture*/ null, + this.fogDepthTextureWrapper.textureView, /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) { renderPass.bindTexture("uDhDepthTexture", BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView, BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureSampler); diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhSsaoRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhSsaoRenderer.java index 87f0e91b1..b7b80ee0d 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhSsaoRenderer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeDhSsaoRenderer.java @@ -30,18 +30,15 @@ import com.mojang.blaze3d.buffers.Std140Builder; import com.mojang.blaze3d.buffers.Std140SizeCalculator; import com.mojang.blaze3d.pipeline.BlendFunction; import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.platform.DepthTestFunction; import com.mojang.blaze3d.platform.DestFactor; -import com.mojang.blaze3d.platform.PolygonMode; import com.mojang.blaze3d.platform.SourceFactor; -import com.mojang.blaze3d.shaders.UniformType; import com.mojang.blaze3d.systems.CommandEncoder; import com.mojang.blaze3d.systems.GpuDevice; import com.mojang.blaze3d.systems.RenderPass; import com.mojang.blaze3d.systems.RenderSystem; -import com.mojang.blaze3d.vertex.VertexFormat; import com.seibel.distanthorizons.common.render.blaze.BlazeDhMetaRenderer; import com.seibel.distanthorizons.common.render.blaze.apply.BlazeDhApplyRenderer; +import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper; import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper; import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil; import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil; @@ -53,7 +50,6 @@ import com.seibel.distanthorizons.core.util.RenderUtil; import com.seibel.distanthorizons.core.util.math.Mat4f; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhSsaoRenderer; -import net.minecraft.resources.Identifier; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -83,7 +79,9 @@ public class BlazeDhSsaoRenderer implements IDhSsaoRenderer private GpuBuffer vboGpuBuffer; - public BlazeTextureWrapper ssaoColorTextureWrapper = BlazeTextureWrapper.createColor("DhSsaoTexture"); + private final BlazeTextureWrapper ssaoColorTextureWrapper = BlazeTextureWrapper.createColor("dh_ssao_color_texture"); + /** We don't want to actually write any depth data, but blaze3D complains if we don't bind a depth texture. */ + private final BlazeTextureWrapper ssaoDepthTextureWrapper = BlazeTextureWrapper.createDepth("dh_ssao_depth_texture"); @@ -110,24 +108,25 @@ public class BlazeDhSsaoRenderer implements IDhSsaoRenderer /*uniforms*/ new String[] { "applyFragUniformBlock" } ); - RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper(); { - pipelineBuilder.withCull(false); + pipelineBuilder.withFaceCulling(false); pipelineBuilder.withDepthWrite(false); - pipelineBuilder.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST); + pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.NONE); pipelineBuilder.withColorWrite(true); pipelineBuilder.withoutBlend(); - pipelineBuilder.withPolygonMode(PolygonMode.FILL); - pipelineBuilder.withLocation(Identifier.parse("distanthorizons:ssao_render")); + pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.FILL); + pipelineBuilder.withName("ssao_render"); - pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "ssao/blaze/vert")); - pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "ssao/blaze/frag")); + pipelineBuilder.withVertexShader("ssao/blaze/vert"); + pipelineBuilder.withFragmentShader("ssao/blaze/frag"); pipelineBuilder.withSampler("uDhDepthTexture"); - pipelineBuilder.withUniform("fragUniformBlock", UniformType.UNIFORM_BUFFER); + pipelineBuilder.withUniformBuffer("fragUniformBlock"); - pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat(), VertexFormat.Mode.TRIANGLE_FAN); + pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat()); + pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLE_FAN); } this.pipeline = pipelineBuilder.build(); @@ -160,6 +159,7 @@ public class BlazeDhSsaoRenderer implements IDhSsaoRenderer // textures this.ssaoColorTextureWrapper.tryCreateOrResize(); + this.ssaoDepthTextureWrapper.tryCreateOrResize(); // frag uniforms { @@ -259,7 +259,7 @@ public class BlazeDhSsaoRenderer implements IDhSsaoRenderer this::getRenderPassName, this.ssaoColorTextureWrapper.textureView, /*optionalClearColorAsInt*/ OptionalInt.empty(), - /*depthTexture*/ null, + this.ssaoDepthTextureWrapper.textureView, /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) { renderPass.bindTexture("uDhDepthTexture", BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureView, BlazeDhMetaRenderer.INSTANCE.dhDepthTextureWrapper.textureSampler); diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeVanillaFadeRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeVanillaFadeRenderer.java index f1d7166c2..73b3d4aa7 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeVanillaFadeRenderer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/postProcessing/BlazeVanillaFadeRenderer.java @@ -29,7 +29,6 @@ import com.mojang.blaze3d.buffers.GpuBufferSlice; import com.mojang.blaze3d.buffers.Std140Builder; import com.mojang.blaze3d.buffers.Std140SizeCalculator; import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.platform.DepthTestFunction; import com.mojang.blaze3d.platform.PolygonMode; import com.mojang.blaze3d.shaders.UniformType; import com.mojang.blaze3d.systems.CommandEncoder; @@ -41,6 +40,7 @@ import com.seibel.distanthorizons.common.render.blaze.BlazeDhMetaRenderer; import com.seibel.distanthorizons.common.render.blaze.apply.BlazeDhCopyRenderer; import com.seibel.distanthorizons.common.render.blaze.util.BlazePostProcessUtil; import com.seibel.distanthorizons.common.render.blaze.util.BlazeUniformUtil; +import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper; import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper; import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureWrapper; import com.seibel.distanthorizons.core.config.Config; @@ -82,7 +82,10 @@ public class BlazeVanillaFadeRenderer implements IDhVanillaFadeRenderer private GpuBuffer vboGpuBuffer; - public final BlazeTextureWrapper fadeColorTextureWrapper = BlazeTextureWrapper.createColor("DhVanillaFadeTexture"); + public final BlazeTextureWrapper fadeColorTextureWrapper = BlazeTextureWrapper.createColor("DhVanillaFadeColorTexture"); + /** We don't want to actually write any depth data, but blaze3D complains if we don't bind a depth texture. */ + private final BlazeTextureWrapper fadeDepthTextureWrapper = BlazeTextureWrapper.createDepth("DhVanillaFadeDepthTexture"); + public final BlazeTextureViewWrapper mcDepthTextureWrapper = new BlazeTextureViewWrapper(); public final BlazeTextureViewWrapper mcColorTextureWrapper = new BlazeTextureViewWrapper(); @@ -106,18 +109,18 @@ public class BlazeVanillaFadeRenderer implements IDhVanillaFadeRenderer - RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper(); { - pipelineBuilder.withCull(false); + pipelineBuilder.withFaceCulling(false); pipelineBuilder.withDepthWrite(false); - pipelineBuilder.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST); + pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.NONE); pipelineBuilder.withColorWrite(true); pipelineBuilder.withoutBlend(); - pipelineBuilder.withPolygonMode(PolygonMode.FILL); - pipelineBuilder.withLocation(Identifier.parse("distanthorizons:mc_vanilla_fade_render")); + pipelineBuilder.withPolygonMode(RenderPipelineBuilderWrapper.EDhPolygonMode.FILL); + pipelineBuilder.withName("vanilla_fade"); - pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "fade/blaze/vert")); - pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "fade/blaze/vanilla_fade")); + pipelineBuilder.withVertexShader("fade/blaze/vert"); + pipelineBuilder.withFragmentShader("fade/blaze/vanilla_fade"); pipelineBuilder.withSampler("uMcDepthTexture"); pipelineBuilder.withSampler("uCombinedMcDhColorTexture"); @@ -125,9 +128,10 @@ public class BlazeVanillaFadeRenderer implements IDhVanillaFadeRenderer pipelineBuilder.withSampler("uDhDepthTexture"); pipelineBuilder.withSampler("uDhColorTexture"); - pipelineBuilder.withUniform("fragUniformBlock", UniformType.UNIFORM_BUFFER); + pipelineBuilder.withUniformBuffer("fragUniformBlock"); - pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat(), VertexFormat.Mode.TRIANGLE_FAN); + pipelineBuilder.withVertexFormat(BlazePostProcessUtil.createVertexFormat()); + pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLE_FAN); } this.pipeline = pipelineBuilder.build(); @@ -158,6 +162,7 @@ public class BlazeVanillaFadeRenderer implements IDhVanillaFadeRenderer // textures this.fadeColorTextureWrapper.tryCreateOrResize(); + this.fadeDepthTextureWrapper.tryCreateOrResize(); this.mcDepthTextureWrapper.tryWrap(Minecraft.getInstance().getMainRenderTarget().getDepthTexture()); this.mcColorTextureWrapper.tryWrap(Minecraft.getInstance().getMainRenderTarget().getColorTexture()); @@ -235,7 +240,7 @@ public class BlazeVanillaFadeRenderer implements IDhVanillaFadeRenderer this::getRenderPassName, this.fadeColorTextureWrapper.textureView, /*optionalClearColorAsInt*/ OptionalInt.empty(), - /*depthTexture*/ null, + this.fadeDepthTextureWrapper.textureView, /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) { renderPass.bindTexture("uMcDepthTexture", this.mcDepthTextureWrapper.textureView, this.mcDepthTextureWrapper.textureSampler); diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/test/BlazeDhTestTriangleRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/test/BlazeDhTestTriangleRenderer.java index aaa295de2..4d86c4def 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/test/BlazeDhTestTriangleRenderer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/test/BlazeDhTestTriangleRenderer.java @@ -27,7 +27,6 @@ public class BlazeDhTestTriangleRenderer {} import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.buffers.GpuBufferSlice; import com.mojang.blaze3d.pipeline.RenderPipeline; -import com.mojang.blaze3d.platform.DepthTestFunction; import com.mojang.blaze3d.platform.PolygonMode; import com.mojang.blaze3d.systems.CommandEncoder; import com.mojang.blaze3d.systems.GpuDevice; @@ -36,6 +35,8 @@ import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.*; import com.mojang.blaze3d.vertex.VertexFormat; import com.seibel.distanthorizons.common.render.blaze.util.BlazeDhVertexFormatUtil; +import com.seibel.distanthorizons.common.render.blaze.wrappers.RenderPipelineBuilderWrapper; +import com.seibel.distanthorizons.common.render.blaze.wrappers.texture.BlazeTextureViewWrapper; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.render.RenderParams; @@ -48,6 +49,12 @@ import java.nio.ByteOrder; import java.util.OptionalDouble; import java.util.OptionalInt; +#if MC_VER <= MC_1_21_11 +#else +import com.mojang.blaze3d.pipeline.DepthStencilState; +import com.mojang.blaze3d.platform.CompareOp; +#endif + /** * Renders the OpenGL/Vulkan triangle * to the center of the screen to confirm DH's @@ -65,7 +72,8 @@ public class BlazeDhTestTriangleRenderer implements IDhTestTriangleRenderer private RenderPipeline pipeline; private boolean init = false; - private GpuTextureView mcColorTextureView; + public final BlazeTextureViewWrapper mcColorTextureViewWrapper = new BlazeTextureViewWrapper(); + public final BlazeTextureViewWrapper mcDepthTextureViewWrapper = new BlazeTextureViewWrapper(); private GpuBuffer vboGpuBuffer; @@ -86,42 +94,28 @@ public class BlazeDhTestTriangleRenderer implements IDhTestTriangleRenderer } this.init = true; - - - VertexFormat vertexFormat = VertexFormat.builder() - .add("vPosition", BlazeDhVertexFormatUtil.SCREEN_POS) - .add("vColor", BlazeDhVertexFormatUtil.RGBA_FLOAT_COLOR) - .build(); - - //int breakpointOne = 0; - // needs to manually be set if the VertexFormatElement isn't registered - //this.vertexFormat.getOffsetsByElement()[this.posForm.id()] = 0; - //this.vertexFormat.getOffsetsByElement()[this.colForm.id()] = Float.BYTES * 2; - // - //int breakpointTwo = 0; - - - RenderPipeline.Builder pipelineBuilder = RenderPipeline.builder(); + RenderPipelineBuilderWrapper pipelineBuilder = new RenderPipelineBuilderWrapper(); { - pipelineBuilder.withCull(false); + pipelineBuilder.withFaceCulling(false); pipelineBuilder.withDepthWrite(false); - pipelineBuilder.withDepthTestFunction(DepthTestFunction.NO_DEPTH_TEST); + pipelineBuilder.withDepthTest(RenderPipelineBuilderWrapper.EDhDepthTest.NONE); pipelineBuilder.withColorWrite(true); pipelineBuilder.withoutBlend(); - pipelineBuilder.withPolygonMode(PolygonMode.FILL); - pipelineBuilder.withLocation(Identifier.parse("distanthorizons:test_render")); + pipelineBuilder.withName("triangle_test"); - pipelineBuilder.withVertexShader(Identifier.fromNamespaceAndPath("distanthorizons", "test/blaze/vert")); - pipelineBuilder.withFragmentShader(Identifier.fromNamespaceAndPath("distanthorizons", "test/blaze/frag")); + pipelineBuilder.withVertexShader("test/blaze/vert"); + pipelineBuilder.withFragmentShader("test/blaze/frag"); - pipelineBuilder.withVertexFormat(vertexFormat, VertexFormat.Mode.TRIANGLES); + VertexFormat vertexFormat = VertexFormat.builder() + .add("vPosition", BlazeDhVertexFormatUtil.SCREEN_POS) + .add("vColor", BlazeDhVertexFormatUtil.RGBA_FLOAT_COLOR) + .build(); + pipelineBuilder.withVertexFormat(vertexFormat); + + pipelineBuilder.withVertexMode(RenderPipelineBuilderWrapper.EDhVertexMode.TRIANGLES); } this.pipeline = pipelineBuilder.build(); - - this.mcColorTextureView = GPU_DEVICE.createTextureView(Minecraft.getInstance().getMainRenderTarget().getColorTexture()); - - this.uploadVertexData(); } private void uploadVertexData() @@ -170,11 +164,14 @@ public class BlazeDhTestTriangleRenderer implements IDhTestTriangleRenderer { this.tryInit(); + this.mcColorTextureViewWrapper.tryWrap(Minecraft.getInstance().getMainRenderTarget().getColorTexture()); + this.mcDepthTextureViewWrapper.tryWrap(Minecraft.getInstance().getMainRenderTarget().getDepthTexture()); + try (RenderPass renderPass = COMMAND_ENCODER.createRenderPass( this::getRenderPassName, - this.mcColorTextureView, + this.mcColorTextureViewWrapper.textureView, /*optionalClearColorAsInt*/ OptionalInt.empty(), - /*mcDepthTextureView*/ null, + this.mcDepthTextureViewWrapper.textureView, /*optionalDepthValueAsDouble*/ OptionalDouble.empty())) { renderPass.setVertexBuffer(0, this.vboGpuBuffer); diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazeDhVertexFormatUtil.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazeDhVertexFormatUtil.java index 5de6f3be2..d36a5305d 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazeDhVertexFormatUtil.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazeDhVertexFormatUtil.java @@ -57,6 +57,7 @@ public class BlazeDhVertexFormatUtil try { + #if MC_VER <= MC_1_21_11 SCREEN_POS = VertexFormatElement.register(/*id*/22, /*index*/0, VertexFormatElement.Type.FLOAT, VertexFormatElement.Usage.POSITION, /*count*/ 2); RGBA_FLOAT_COLOR = VertexFormatElement.register(/*id*/23, /*index*/0, VertexFormatElement.Type.FLOAT, VertexFormatElement.Usage.COLOR, /*count*/ 4); @@ -69,6 +70,20 @@ public class BlazeDhVertexFormatUtil IRIS_NORMAL = VertexFormatElement.register(/*id*/29, /*index*/0, VertexFormatElement.Type.BYTE, VertexFormatElement.Usage.GENERIC, /*count*/ 1); FLOAT_XYZ_POS = VertexFormatElement.register(/*id*/30, /*index*/0, VertexFormatElement.Type.FLOAT, VertexFormatElement.Usage.POSITION, /*count*/ 3); + #else + SCREEN_POS = VertexFormatElement.register(/*id*/22, /*index*/0, VertexFormatElement.Type.FLOAT, false, /*count*/ 2); + RGBA_FLOAT_COLOR = VertexFormatElement.register(/*id*/23, /*index*/0, VertexFormatElement.Type.FLOAT, false, /*count*/ 4); + + SHORT_XYZ_POS = VertexFormatElement.register(/*id*/24, /*index*/0, VertexFormatElement.Type.USHORT, false, /*count*/ 3); + BYTE_PAD = VertexFormatElement.register(/*id*/25, /*index*/0, VertexFormatElement.Type.BYTE, false, /*count*/ 1); + + META = VertexFormatElement.register(/*id*/26, /*index*/0, VertexFormatElement.Type.USHORT, false, /*count*/ 1); + RGBA_UBYTE_COLOR = VertexFormatElement.register(/*id*/27, /*index*/0, VertexFormatElement.Type.UBYTE, true, /*count*/ 4); + IRIS_MATERIAL = VertexFormatElement.register(/*id*/28, /*index*/0, VertexFormatElement.Type.BYTE, false, /*count*/ 1); + IRIS_NORMAL = VertexFormatElement.register(/*id*/29, /*index*/0, VertexFormatElement.Type.BYTE, false, /*count*/ 1); + + FLOAT_XYZ_POS = VertexFormatElement.register(/*id*/30, /*index*/0, VertexFormatElement.Type.FLOAT, false, /*count*/ 3); + #endif } catch (Exception e) { diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazeUniformUtil.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazeUniformUtil.java index 6b40aaa11..8968ab014 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazeUniformUtil.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/util/BlazeUniformUtil.java @@ -9,9 +9,13 @@ import com.mojang.blaze3d.buffers.GpuBuffer; import com.mojang.blaze3d.systems.CommandEncoder; import com.mojang.blaze3d.systems.GpuDevice; import com.mojang.blaze3d.systems.RenderSystem; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; public class BlazeUniformUtil { + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); @@ -22,7 +26,11 @@ public class BlazeUniformUtil if (vboGpuBuffer == null || vboGpuBuffer.size() < size) { - // GpuBuffer.USAGE_UNIFORM = 128 + if (vboGpuBuffer != null) + { + vboGpuBuffer.close(); + } + int usage = GpuBuffer.USAGE_COPY_DST | GpuBuffer.USAGE_VERTEX | GpuBuffer.USAGE_UNIFORM; diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/RenderPipelineBuilderWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/RenderPipelineBuilderWrapper.java new file mode 100644 index 000000000..0885bc195 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/RenderPipelineBuilderWrapper.java @@ -0,0 +1,368 @@ +package com.seibel.distanthorizons.common.render.blaze.wrappers; + + +#if MC_VER <= MC_1_21_10 +public class RenderPipelineBuilderWrapper {} + +#else + +import com.mojang.blaze3d.pipeline.BlendFunction; +import com.mojang.blaze3d.pipeline.RenderPipeline; +import com.mojang.blaze3d.platform.PolygonMode; +import com.mojang.blaze3d.shaders.UniformType; +import com.mojang.blaze3d.vertex.VertexFormat; +import net.minecraft.resources.Identifier; + +#if MC_VER <= MC_1_21_11 +import com.mojang.blaze3d.platform.DepthTestFunction; +#else +import com.mojang.blaze3d.pipeline.ColorTargetState; +import com.mojang.blaze3d.pipeline.DepthStencilState; +import com.mojang.blaze3d.platform.CompareOp; +#endif + +import java.io.IOException; +import java.io.InputStream; +import java.util.Optional; + +public class RenderPipelineBuilderWrapper +{ + public static final String NAME_PREFIX = "distanthorizons:"; + + private static final String SHADER_RESOURCE_FOLDER = "assets/distanthorizons/shaders/"; + + private static final ClassLoader CLASS_LOADER = RenderPipelineBuilderWrapper.class.getClassLoader(); + + + private final RenderPipeline.Builder blazePipelineBuilder; + + // variables for specific builder options should be put next to their builder methods for simpler organization + + + + //=============// + // constructor // + //=============// + //region + + public RenderPipelineBuilderWrapper() + { + this.blazePipelineBuilder = RenderPipeline.builder(); + } + + //endregion + + + + //==========// + // building // + //==========// + //region + + private boolean writeDepth = false; + public RenderPipelineBuilderWrapper withDepthWrite(boolean write) + { + this.writeDepth = write; + return this; + } + + private boolean writeColor = false; + public RenderPipelineBuilderWrapper withColorWrite(boolean write) + { + this.writeColor = write; + return this; + } + + private BlendFunction blendFunction = null; + public RenderPipelineBuilderWrapper withBlend(BlendFunction blendFunction) + { + this.blendFunction = blendFunction; + return this; + } + public RenderPipelineBuilderWrapper withoutBlend() + { + this.blendFunction = null; + return this; + } + + private EDhDepthTest depthTest; + public RenderPipelineBuilderWrapper withDepthTest(EDhDepthTest depthTest) + { + this.depthTest = depthTest; + return this; + } + + public RenderPipelineBuilderWrapper withFaceCulling(boolean culling) + { + this.blazePipelineBuilder.withCull(culling); + return this; + } + + public RenderPipelineBuilderWrapper withPolygonMode(EDhPolygonMode dhMode) + { + PolygonMode blazeMode; + switch (dhMode) + { + case FILL: + blazeMode = PolygonMode.FILL; + break; + case WIREFRAME: + blazeMode = PolygonMode.WIREFRAME; + break; + + default: + throw new UnsupportedOperationException("No polygonMode defined for type ["+dhMode+"]."); + } + + this.blazePipelineBuilder.withPolygonMode(blazeMode); + return this; + } + + public RenderPipelineBuilderWrapper withName(String name) throws IllegalArgumentException + { + // Identifiers must be of a specific format + if (!isValidIdentifier(name)) + { + throw new IllegalArgumentException("Non [a-z0-9/._-] character in name: ["+name+"]."); + } + + this.blazePipelineBuilder.withLocation(Identifier.parse(NAME_PREFIX + name)); + return this; + } + + public RenderPipelineBuilderWrapper withSampler(String name) throws IllegalArgumentException + { + this.blazePipelineBuilder.withSampler(name); + return this; + } + + public RenderPipelineBuilderWrapper withUniformBuffer(String name) throws IllegalArgumentException + { + this.blazePipelineBuilder.withUniform(name, UniformType.UNIFORM_BUFFER); + return this; + } + + private VertexFormat vertexFormat = null; + public RenderPipelineBuilderWrapper withVertexFormat(VertexFormat vertexFormat) + { + this.vertexFormat = vertexFormat; + return this; + } + + private EDhVertexMode vertexMode = null; + public RenderPipelineBuilderWrapper withVertexMode(EDhVertexMode vertexMode) + { + this.vertexMode = vertexMode; + return this; + } + + public RenderPipelineBuilderWrapper withVertexShader(String scriptResourcePath) { return this.withShader(EDhShaderType.VERTEX, scriptResourcePath); } + public RenderPipelineBuilderWrapper withFragmentShader(String scriptResourcePath) { return this.withShader(EDhShaderType.FRAGMENT, scriptResourcePath); } + private RenderPipelineBuilderWrapper withShader(EDhShaderType shaderType, String scriptResourcePath) + { + String fullShaderResourcePath = SHADER_RESOURCE_FOLDER + scriptResourcePath + shaderType.fileExtension; + + // confirm the shader file exists + try (InputStream scriptListInputStream = CLASS_LOADER.getResourceAsStream(fullShaderResourcePath)) + { + if (scriptListInputStream == null) + { + throw new NullPointerException("Failed to find the SQL Script list file [" + fullShaderResourcePath + "], no auto update scripts can be run."); + } + } + catch (IOException e) + { + // shouldn't happen, but just in case + throw new RuntimeException("Unexpected issue closing resource stream for shader type: ["+shaderType+"] at: ["+fullShaderResourcePath+"], error: ["+e.getMessage()+"].", e); + } + + + + if (shaderType == EDhShaderType.VERTEX) + { + this.blazePipelineBuilder.withVertexShader(Identifier.parse(NAME_PREFIX + scriptResourcePath)); + } + else + { + this.blazePipelineBuilder.withFragmentShader(Identifier.parse(NAME_PREFIX + scriptResourcePath)); + } + + return this; + } + + //endregion + + + + //=====// + // end // + //=====// + //region + + public RenderPipeline build() throws UnsupportedOperationException + { + // depth/color + { + #if MC_VER <= MC_1_21_11 + + this.blazePipelineBuilder.withDepthWrite(this.writeDepth); + this.blazePipelineBuilder.withColorWrite(this.writeColor); + + if (this.blendFunction != null) + { + this.blazePipelineBuilder.withBlend(this.blendFunction); + } + else + { + this.blazePipelineBuilder.withoutBlend(); + } + + DepthTestFunction depthTestFunction; + switch (this.depthTest) + { + case NONE: + depthTestFunction = DepthTestFunction.NO_DEPTH_TEST; + break; + case LESS: + depthTestFunction = DepthTestFunction.LESS_DEPTH_TEST; + break; + + default: + throw new UnsupportedOperationException("No depth test defined for type ["+this.depthTest+"]."); + } + this.blazePipelineBuilder.withDepthTestFunction(depthTestFunction); + + #else + + CompareOp compareOp; + switch (this.depthTest) + { + case NONE: + compareOp = CompareOp.ALWAYS_PASS; + break; + case LESS: + compareOp = CompareOp.LESS_THAN; + break; + + default: + throw new UnsupportedOperationException("No depth test defined for type ["+this.depthTest+"]."); + } + this.blazePipelineBuilder.withDepthStencilState(new DepthStencilState(compareOp, this.writeDepth)); + + this.blazePipelineBuilder.withColorTargetState( + new ColorTargetState( + Optional.ofNullable(this.blendFunction), + this.writeColor ? ColorTargetState.WRITE_ALL : ColorTargetState.WRITE_NONE + ) + ); + + #endif + } + + + // vertex format + { + VertexFormat.Mode blazeVertexMode; + switch (this.vertexMode) + { + case TRIANGLES: + blazeVertexMode = VertexFormat.Mode.TRIANGLES; + break; + case TRIANGLE_FAN: + blazeVertexMode = VertexFormat.Mode.TRIANGLE_FAN; + break; + case LINES: + blazeVertexMode = VertexFormat.Mode.DEBUG_LINES; + break; + + default: + throw new UnsupportedOperationException("No vertex mode defined for type ["+this.vertexMode+"]."); + } + + this.blazePipelineBuilder.withVertexFormat(vertexFormat, blazeVertexMode); + } + + return this.blazePipelineBuilder.build(); + } + + //endregion + + + + //================// + // helper methods // + //================// + //region + + private static boolean isValidIdentifier(String identifier) + { + for (int i = 0; i < identifier.length(); i++) + { + char ch = identifier.charAt(i); + if (!isValidNamespaceChar(ch)) + { + return false; + } + } + + return true; + } + private static boolean isValidNamespaceChar(final char ch) + { + return ch == '_' + || ch == '-' + // only lower case characters + || (ch >= 'a' && ch <= 'z') + || (ch >= '0' && ch <= '9') + || ch == '.'; + } + + //endregion + + + + //================// + // helper classes // + //================// + //region + + public enum EDhPolygonMode + { + FILL, + WIREFRAME; + } + + public enum EDhVertexMode + { + TRIANGLES, + TRIANGLE_FAN, + LINES; + } + + public enum EDhDepthTest + { + NONE, + LESS; + } + + private enum EDhShaderType + { + FRAGMENT(".fsh"), + VERTEX(".vsh"); + + + public final String fileExtension; + + EDhShaderType(String fileExtension) + { + this.fileExtension = fileExtension; + } + } + + + //endregion + + + +} +#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/buffer/BlazeVertexBufferWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/buffer/BlazeVertexBufferWrapper.java index 71761481a..0898b1f54 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/buffer/BlazeVertexBufferWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/buffer/BlazeVertexBufferWrapper.java @@ -10,39 +10,86 @@ import com.mojang.blaze3d.buffers.GpuBufferSlice; import com.mojang.blaze3d.systems.CommandEncoder; import com.mojang.blaze3d.systems.GpuDevice; import com.mojang.blaze3d.systems.RenderSystem; -import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GlQuadIndexBuffer; -import com.seibel.distanthorizons.common.render.openGl.glObject.enums.GLEnums; +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.IndexBufferBuilder; import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodQuadBuilder; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition; import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper; -import org.lwjgl.opengl.GL32; import org.lwjgl.system.MemoryUtil; import java.nio.ByteBuffer; -import java.nio.ByteOrder; +import java.util.concurrent.atomic.AtomicInteger; public class BlazeVertexBufferWrapper implements IVertexBufferWrapper { + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + private static final AbstractDhRenderApiDefinition RENDER_DEF = SingletonInjector.INSTANCE.get(AbstractDhRenderApiDefinition.class); + private static final GpuDevice GPU_DEVICE = RenderSystem.getDevice(); private static final CommandEncoder COMMAND_ENCODER = GPU_DEVICE.createCommandEncoder(); + private static final AtomicInteger BUFFER_COUNT_REF = new AtomicInteger(0); + public final String name; public String getName() { return this.name; } - public GpuBuffer vboGpuBuffer = null; - public GpuBuffer indexBuffer = null; + public GpuBuffer vertexGpuBuffer = null; public int vertexCount = -1; public int indexCount = -1; public boolean uploaded = false; + private GpuBuffer indexGpuBuffer = null; + private static GpuBuffer GLOBAL_INDEX_GPU_BUFFER = null; + public GpuBuffer getIndexGpuBuffer() + { + if (RENDER_DEF.useSingleIbo()) + { + return GLOBAL_INDEX_GPU_BUFFER; + } + else + { + return this.indexGpuBuffer; + } + } + + //=============// // constructor // //=============// //region + static + { + if (RENDER_DEF.useSingleIbo()) + { + RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("Global IBO Creation", () -> + { + int maxSize = LodQuadBuilder.getMaxBufferByteSize(); + int maxVertexCount = maxSize / LodQuadBuilder.BYTES_PER_VERTEX; + int maxQuadCount = (maxVertexCount / 4); + ByteBuffer indexBuffer = IndexBufferBuilder.createBuffer(maxQuadCount); + + int usage = GpuBuffer.USAGE_COPY_DST + | GpuBuffer.USAGE_INDEX; + GLOBAL_INDEX_GPU_BUFFER = GPU_DEVICE.createBuffer(BlazeVertexBufferWrapper::getIndexBufferName, usage, indexBuffer.capacity()); + + GpuBufferSlice bufferSlice = new GpuBufferSlice(GLOBAL_INDEX_GPU_BUFFER, /*offset*/ 0, indexBuffer.capacity()); + COMMAND_ENCODER.writeToBuffer(bufferSlice, indexBuffer); + + MemoryUtil.memFree(indexBuffer); + + }); + } + } + public BlazeVertexBufferWrapper(String name) { this.name = name; } //endregion @@ -55,7 +102,7 @@ public class BlazeVertexBufferWrapper implements IVertexBufferWrapper //region @Override - public void upload(ByteBuffer vertexBuffer, int vertexCount) + public void uploadVertexBuffer(ByteBuffer vertexBuffer, int vertexCount) { int oldVertexCount = this.vertexCount; @@ -65,50 +112,72 @@ public class BlazeVertexBufferWrapper implements IVertexBufferWrapper this.uploaded = true; - if (this.vboGpuBuffer == null + + if (this.vertexGpuBuffer == null // recreating if the size changes is always necessary (even if we only need a smaller amount) // due to a bug on Mac where it will attempt to render anything allocated in the buffer || oldVertexCount != vertexCount) { - if (this.vboGpuBuffer != null) + if (this.vertexGpuBuffer == null) { - this.vboGpuBuffer.close(); + BUFFER_COUNT_REF.incrementAndGet(); + //LOGGER.info("Create, count: ["+BUFFER_COUNT_REF.get()+"]"); + } + + if (this.vertexGpuBuffer != null) + { + this.vertexGpuBuffer.close(); } int usage = GpuBuffer.USAGE_COPY_DST | GpuBuffer.USAGE_VERTEX; int byteSize = (vertexBuffer.limit() - vertexBuffer.position()); - this.vboGpuBuffer = GPU_DEVICE.createBuffer(this::getName, usage, byteSize); + this.vertexGpuBuffer = GPU_DEVICE.createBuffer(this::getName, usage, byteSize); - GpuBufferSlice bufferSlice = new GpuBufferSlice(this.vboGpuBuffer, /*offset*/0, byteSize); + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.vertexGpuBuffer, /*offset*/0, byteSize); COMMAND_ENCODER.writeToBuffer(bufferSlice, vertexBuffer); } + } + + @Override + public void uploadIndexBuffer(ByteBuffer indexBuffer, int vertexCount) + { + int oldIndexCount = this.indexCount; + // 4 vertices per face, but 6 indices (IE 2 triangles) per face, aka need to multiply by 1.5 + this.indexCount = (int)(vertexCount * 1.5); + + if (RENDER_DEF.useSingleIbo()) + { + // ignore index uploading when running a single IBO + return; + } - if (this.indexBuffer == null - // recreating if the size changes is always necessary (even if we only need a smaller amount) - // due to a bug on Mac where it will attempt to render anything allocated in the buffer - || oldVertexCount != vertexCount) + + // recreating if the size changes is always necessary (even if we only need a smaller amount) + // due to a bug on Mac where it will attempt to render anything allocated in the buffer + if (this.indexGpuBuffer == null + || oldIndexCount != this.indexCount) { - if (this.indexBuffer != null) + if (this.indexGpuBuffer == null) { - this.indexBuffer.close(); + BUFFER_COUNT_REF.incrementAndGet(); + } + + if (this.indexGpuBuffer != null) + { + this.indexGpuBuffer.close(); } - - int quadCount = (this.vertexCount / 4); - ByteBuffer indexBuffer = MemoryUtil.memAlloc(quadCount * 6 * GLEnums.getTypeSize(GL32.GL_UNSIGNED_INT)); - indexBuffer.order(ByteOrder.nativeOrder()); - GlQuadIndexBuffer.buildBuffer(quadCount, indexBuffer, GL32.GL_UNSIGNED_INT); int usage = GpuBuffer.USAGE_COPY_DST | GpuBuffer.USAGE_INDEX; - this.indexBuffer = GPU_DEVICE.createBuffer(this::getIndexBufferName, usage, indexBuffer.capacity()); + this.indexGpuBuffer = GPU_DEVICE.createBuffer(BlazeVertexBufferWrapper::getIndexBufferName, usage, indexBuffer.capacity()); - GpuBufferSlice bufferSlice = new GpuBufferSlice(this.indexBuffer, /*offset*/ 0, indexBuffer.capacity()); + GpuBufferSlice bufferSlice = new GpuBufferSlice(this.indexGpuBuffer, /*offset*/ 0, indexBuffer.capacity()); COMMAND_ENCODER.writeToBuffer(bufferSlice, indexBuffer); } } - private String getIndexBufferName() { return "distantHorizons:LodIndexBuffer"; } + private static String getIndexBufferName() { return "distantHorizons:LodIndexBuffer"; } //endregion @@ -122,10 +191,19 @@ public class BlazeVertexBufferWrapper implements IVertexBufferWrapper @Override public void close() { - if (this.vboGpuBuffer != null) + if (this.vertexGpuBuffer != null) { - this.vboGpuBuffer.close(); + BUFFER_COUNT_REF.decrementAndGet(); + this.vertexGpuBuffer.close(); } + + if (this.indexGpuBuffer != null) + { + BUFFER_COUNT_REF.decrementAndGet(); + this.indexGpuBuffer.close(); + } + + //LOGGER.info("Close, count: ["+BUFFER_COUNT_REF.get()+"]"); } //endregion diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/texture/BlazeTextureWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/texture/BlazeTextureWrapper.java index fe1a3a8f2..30b09744f 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/texture/BlazeTextureWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/blaze/wrappers/texture/BlazeTextureWrapper.java @@ -13,7 +13,7 @@ import com.mojang.blaze3d.textures.*; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.util.ColorUtil; +import com.seibel.distanthorizons.coreapi.util.ColorUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import java.util.OptionalDouble; @@ -79,42 +79,52 @@ public class BlazeTextureWrapper //=======// //region - /** does nothing if the texture is already created and the correct size */ - public void tryCreateOrResize() + /** + * does nothing if the texture is already created and the correct size + * @return true if the texture was (re)created + */ + public boolean tryCreateOrResize() { - this.tryCreateTexture(); + boolean textureChanged = this.tryCreateTexture(); this.tryCreateSampler(); + return textureChanged; } - private void tryCreateTexture() + private boolean tryCreateTexture() { int viewWidth = MC_RENDER.getTargetFramebufferViewportWidth(); int viewHeight = MC_RENDER.getTargetFramebufferViewportHeight(); - if (this.texture == null - || this.width != viewWidth - || this.height != viewHeight) + if (this.texture != null + && this.width == viewWidth + && this.height == viewHeight) { - if (this.texture != null) - { - this.texture.close(); - this.textureView.close(); - } - - this.width = viewWidth; - this.height = viewHeight; - - int usage = GpuBuffer.USAGE_HINT_CLIENT_STORAGE - | GpuBuffer.USAGE_COPY_DST - | GpuBuffer.USAGE_VERTEX - | GpuBuffer.USAGE_UNIFORM; - this.texture = GPU_DEVICE.createTexture(this.name, - usage, - this.textureFormat, - viewWidth, viewHeight, - /*depthOrLayers*/ 1, /*mipLevels*/ 1 - ); - this.textureView = GPU_DEVICE.createTextureView(this.texture); + // no changes needed + return false; } + + + if (this.texture != null) + { + this.texture.close(); + this.textureView.close(); + } + + this.width = viewWidth; + this.height = viewHeight; + + int usage = GpuTexture.USAGE_COPY_DST + | GpuTexture.USAGE_TEXTURE_BINDING + | GpuTexture.USAGE_COPY_SRC + | GpuTexture.USAGE_RENDER_ATTACHMENT; + this.texture = GPU_DEVICE.createTexture(this.name, + usage, + this.textureFormat, + viewWidth, viewHeight, + /*depthOrLayers*/ 1, /*mipLevels*/ 1 + ); + this.textureView = GPU_DEVICE.createTextureView(this.texture); + + return true; } private void tryCreateSampler() { diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhMetaRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhMetaRenderer.java index 8450849de..ebc78b50e 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhMetaRenderer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhMetaRenderer.java @@ -8,13 +8,12 @@ import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhAp import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiTextureCreatedParam; import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy; import com.seibel.distanthorizons.common.render.openGl.glObject.GlDhFramebuffer; -import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GlQuadIndexBuffer; import com.seibel.distanthorizons.common.render.openGl.glObject.texture.*; import com.seibel.distanthorizons.common.render.openGl.postProcessing.apply.GlDhApplyShader; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper; import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodQuadBuilder; +import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; @@ -22,7 +21,7 @@ import com.seibel.distanthorizons.core.render.DhApiRenderProxy; import com.seibel.distanthorizons.core.render.RenderParams; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor; +import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhMetaRenderer; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import com.seibel.distanthorizons.coreapi.DependencyInjection.OverrideInjector; @@ -46,6 +45,8 @@ public class GlDhMetaRenderer implements IDhMetaRenderer private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + private static final IOptifineAccessor OPTIFINE_ACCESSOR = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class); + private int activeFramebufferId = -1; private int activeColorTextureId = -1; @@ -96,7 +97,7 @@ public class GlDhMetaRenderer implements IDhMetaRenderer this.renderObjectsCreated = true; } - this.shaderProgramForThisFrame = GlDhTerrainShaderProgram.INSTANCE; + this.shaderProgramForThisFrame = GlDhTerrainRenderer.INSTANCE.getTerrainShaderProgram(); IDhApiShaderProgram lodShaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiShaderProgram.class); if (lodShaderProgramOverride != null && this.shaderProgramForThisFrame.overrideThisFrame()) { @@ -243,7 +244,7 @@ public class GlDhMetaRenderer implements IDhMetaRenderer // create or get the frame buffer - if (AbstractOptifineAccessor.optifinePresent()) + if (OPTIFINE_ACCESSOR != null) { // use MC/Optifine's default Framebuffer so shaders won't remove the LODs int currentFramebufferId = MC_RENDER.getTargetFramebuffer(); @@ -384,31 +385,26 @@ public class GlDhMetaRenderer implements IDhMetaRenderer - // needs to be fired after all the textures have been created/bound - boolean clearTextures = !ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeTextureClearEvent.class, renderParams); - if (clearTextures) + GL32.glClearDepth(1.0); + + float[] clearColorValues = new float[4]; + GL32.glGetFloatv(GL32.GL_COLOR_CLEAR_VALUE, clearColorValues); + GL32.glClearColor(clearColorValues[0], clearColorValues[1], clearColorValues[2], 1.0f); + + if (this.usingMcFramebuffer + && framebufferOverride == null) { - GL32.glClearDepth(1.0); + //// Due to using MC/Optifine's framebuffer we need to re-bind the depth texture, + //// otherwise we'll be writing to MC/Optifine's depth texture which causes rendering issues + //this.framebuffer.addDepthAttachment(this.depthTexture.getTextureId(), EDhDepthBufferFormat.DEPTH32F.isCombinedStencil()); - float[] clearColorValues = new float[4]; - GL32.glGetFloatv(GL32.GL_COLOR_CLEAR_VALUE, clearColorValues); - GL32.glClearColor(clearColorValues[0], clearColorValues[1], clearColorValues[2], 1.0f); - if (this.usingMcFramebuffer - && framebufferOverride == null) - { - //// Due to using MC/Optifine's framebuffer we need to re-bind the depth texture, - //// otherwise we'll be writing to MC/Optifine's depth texture which causes rendering issues - //this.framebuffer.addDepthAttachment(this.depthTexture.getTextureId(), EDhDepthBufferFormat.DEPTH32F.isCombinedStencil()); - - - // don't clear the color texture, that removes the sky - GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT); - } - else if (firstPass) - { - GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT); - } + // don't clear the color texture, that removes the sky + GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT); + } + else if (firstPass) + { + GL32.glClear(GL32.GL_COLOR_BUFFER_BIT | GL32.GL_DEPTH_BUFFER_BIT); } } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhRenderApiDefinition.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhRenderApiDefinition.java index 26f604c10..4ef567874 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhRenderApiDefinition.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhRenderApiDefinition.java @@ -2,13 +2,13 @@ package com.seibel.distanthorizons.common.render.openGl; import com.seibel.distanthorizons.common.render.openGl.generic.GlGenericObjectRenderer; import com.seibel.distanthorizons.common.render.openGl.generic.GlGenericObjectVertexContainer; -import com.seibel.distanthorizons.common.render.openGl.glObject.GLState; import com.seibel.distanthorizons.common.render.openGl.glObject.GlDummyUniformData; import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLVertexBuffer; import com.seibel.distanthorizons.common.render.openGl.postProcessing.fade.GlDhFarFadeRenderer; import com.seibel.distanthorizons.common.render.openGl.postProcessing.fade.GlVanillaFadeRenderer; import com.seibel.distanthorizons.common.render.openGl.postProcessing.fog.GlDhFogRenderer; import com.seibel.distanthorizons.common.render.openGl.postProcessing.ssao.GlDhSSAORenderer; +import com.seibel.distanthorizons.common.render.openGl.terrain.GlDhTerrainShaderProgram; import com.seibel.distanthorizons.common.render.openGl.test.GlTestTriangleRenderer; import com.seibel.distanthorizons.core.render.renderer.AbstractDebugWireframeRenderer; import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition; @@ -36,7 +36,7 @@ public class GlDhRenderApiDefinition extends AbstractDhRenderApiDefinition //region @Override public IDhMetaRenderer getMetaRenderer() { return GlDhMetaRenderer.INSTANCE; } - @Override public IDhTerrainRenderer getTerrainRenderer() { return GlDhTerrainShaderProgram.INSTANCE; } + @Override public IDhTerrainRenderer getTerrainRenderer() { return GlDhTerrainRenderer.INSTANCE; } @Override public IDhSsaoRenderer getSsaoRenderer() { return GlDhSSAORenderer.INSTANCE; } @Override public IDhFogRenderer getFogRenderer() { return GlDhFogRenderer.INSTANCE; } @Override public IDhFarFadeRenderer getFarFadeRenderer() { return GlDhFarFadeRenderer.INSTANCE; } @@ -45,15 +45,6 @@ public class GlDhRenderApiDefinition extends AbstractDhRenderApiDefinition @Override public IDhVanillaFadeRenderer getVanillaFadeRenderer() { return GlVanillaFadeRenderer.INSTANCE; } @Override public IDhTestTriangleRenderer getTestTriangleRenderer() { return GlTestTriangleRenderer.INSTANCE; } - @Override - public void bindRenderers() - { - try (GLState state = new GLState()) - { - super.bindRenderers(); - } - } - //endregion @@ -63,7 +54,7 @@ public class GlDhRenderApiDefinition extends AbstractDhRenderApiDefinition //===========// //region - @Override public IDhGenericRenderer createGenericRenderer() { return GlGenericObjectRenderer.INSTANCE; } + @Override public IDhGenericRenderer createGenericRenderer() { return new GlGenericObjectRenderer(); } @Override public IVertexBufferWrapper createVboWrapper(String name) { return new GLVertexBuffer(); } @Override public ILodContainerUniformBufferWrapper createLodContainerUniformWrapper() { return new GlDummyUniformData(); } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhTerrainRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhTerrainRenderer.java new file mode 100644 index 000000000..60afe3356 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhTerrainRenderer.java @@ -0,0 +1,67 @@ +package com.seibel.distanthorizons.common.render.openGl; + +import com.seibel.distanthorizons.common.render.openGl.terrain.GlDhTerrainShaderProgram; +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodBufferContainer; +import com.seibel.distanthorizons.core.render.RenderParams; +import com.seibel.distanthorizons.core.util.objects.SortedArraySet; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTerrainRenderer; + +public class GlDhTerrainRenderer implements IDhTerrainRenderer +{ + public static final GlDhTerrainRenderer INSTANCE = new GlDhTerrainRenderer(); + + private GlDhTerrainShaderProgram terrainShaderProgram = null; + + + + //=============// + // constructor // + //=============// + //region + + private GlDhTerrainRenderer() {} + + //endregion + + + //=========// + // getters // + //=========// + //region + + /** must be called on the render thread the first time so GL can run it's setup */ + public GlDhTerrainShaderProgram getTerrainShaderProgram() + { + if (this.terrainShaderProgram == null) + { + this.terrainShaderProgram = new GlDhTerrainShaderProgram(); + } + + return this.terrainShaderProgram; + } + + + //endregion + + + + //========// + // render // + //========// + //region + + @Override + public void render(RenderParams renderEventParam, boolean opaquePass, SortedArraySet bufferContainers, IProfilerWrapper profiler) + { + this.getTerrainShaderProgram(); + + this.terrainShaderProgram.tryInit(); + this.terrainShaderProgram.render(renderEventParam, opaquePass, bufferContainers, profiler); + } + + //endregion + + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/generic/GlGenericObjectRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/generic/GlGenericObjectRenderer.java index cc2c34530..5670cf08e 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/generic/GlGenericObjectRenderer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/generic/GlGenericObjectRenderer.java @@ -76,8 +76,6 @@ public class GlGenericObjectRenderer implements IDhGenericRenderer private static final DhApiRenderableBoxGroupShading DEFAULT_SHADING = DhApiRenderableBoxGroupShading.getUnshaded(); - public static final GlGenericObjectRenderer INSTANCE = new GlGenericObjectRenderer(); - /** * Can be used to troubleshoot the renderer. * If enabled several debug objects will render around (0,150,0). @@ -176,7 +174,7 @@ public class GlGenericObjectRenderer implements IDhGenericRenderer //=============// //region - private GlGenericObjectRenderer() { } + public GlGenericObjectRenderer() { } public void init() { @@ -415,136 +413,133 @@ public class GlGenericObjectRenderer implements IDhGenericRenderer // render setup // - profiler.push("setup"); - - this.init(); - - boolean useInstancedRendering = this.instancedRenderingAvailable - && Config.Client.Advanced.Graphics.GenericRendering.enableInstancedRendering.get(); - - ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderSetupEvent.class, renderEventParam); - - - boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get(); - if (renderWireframe) + try (IProfilerWrapper.IProfileBlock setup_profile = profiler.push("setup")) { - GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE); - GLMC.disableFaceCulling(); - } - else - { - GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); - GLMC.enableFaceCulling(); - } - - GLMC.enableBlend(); - GL32.glBlendEquation(GL32.GL_FUNC_ADD); - GLMC.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA); - - IDhApiGenericObjectShaderProgram shaderProgram = useInstancedRendering ? this.instancedShaderProgram : this.directShaderProgram; - IDhApiGenericObjectShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiGenericObjectShaderProgram.class); - if (shaderProgramOverride != null && shaderProgram.overrideThisFrame()) - { - shaderProgram = shaderProgramOverride; - } - - shaderProgram.bind(renderEventParam); - shaderProgram.bindVertexBuffer(this.boxVertexBuffer.getId()); - - this.boxIndexBuffer.bind(); - - Vec3d camPos = MC_RENDER.getCameraExactPosition(); - - - - // rendering // - - Collection boxList = this.boxGroupById.values(); - for (RenderableBoxGroup boxGroup : boxList) - { - // validation // - // shouldn't happen, but just in case - if (boxGroup == null) + this.init(); + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderSetupEvent.class, renderEventParam); + + + boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get(); + if (renderWireframe) { - continue; - } - - // skip boxes that shouldn't render this pass - if (boxGroup.ssaoEnabled != renderingWithSsao) - { - continue; - } - - profiler.popPush("render prep"); - boxGroup.preRender(renderEventParam); // called even if the group is inactive, so the group can be activate if desired - - // ignore inactive groups - if (!boxGroup.active) - { - continue; - } - - // allow API users to cancel this object's rendering - boolean cancelRendering = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericObjectRenderEvent.class, new DhApiBeforeGenericObjectRenderEvent.EventParam(renderEventParam, boxGroup)); - if (cancelRendering) - { - continue; - } - - // update instanced data if needed - if (useInstancedRendering) - { - boxGroup.tryUpdateInstancedDataAsync(); - - // skip groups that haven't been uploaded yet - if (boxGroup.vertexBufferContainer.getState() != GlGenericObjectVertexContainer.EState.RENDER) - { - continue; - } - } - - - - // render // - - profiler.popPush("rendering"); - profiler.push(boxGroup.getResourceLocationNamespace()); - profiler.push(boxGroup.getResourceLocationPath()); - if (useInstancedRendering) - { - this.renderBoxGroupInstanced(shaderProgram, renderEventParam, boxGroup, camPos, profiler); + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE); + GLMC.disableFaceCulling(); } else { - this.renderBoxGroupDirect(shaderProgram, renderEventParam, boxGroup, camPos, profiler); + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); + GLMC.enableFaceCulling(); } - profiler.pop(); // resource path - profiler.pop(); // resource namespace - boxGroup.postRender(renderEventParam); + GLMC.enableBlend(); + GL32.glBlendEquation(GL32.GL_FUNC_ADD); + GLMC.glBlendFuncSeparate(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA, GL32.GL_ONE, GL32.GL_ONE_MINUS_SRC_ALPHA); + + IDhApiGenericObjectShaderProgram shaderProgram = this.instancedRenderingAvailable ? this.instancedShaderProgram : this.directShaderProgram; + IDhApiGenericObjectShaderProgram shaderProgramOverride = OverrideInjector.INSTANCE.get(IDhApiGenericObjectShaderProgram.class); + if (shaderProgramOverride != null && shaderProgram.overrideThisFrame()) + { + shaderProgram = shaderProgramOverride; + } + + shaderProgram.bind(renderEventParam); + shaderProgram.bindVertexBuffer(this.boxVertexBuffer.getId()); + + this.boxIndexBuffer.bind(); + + Vec3d camPos = MC_RENDER.getCameraExactPosition(); + + + + // rendering // + + Collection boxList = this.boxGroupById.values(); + for (RenderableBoxGroup boxGroup : boxList) + { + // validation // + + // shouldn't happen, but just in case + if (boxGroup == null) + { + continue; + } + + // skip boxes that shouldn't render this pass + if (boxGroup.ssaoEnabled != renderingWithSsao) + { + continue; + } + + profiler.popPush("render prep"); + boxGroup.preRender(renderEventParam); // called even if the group is inactive, so the group can be activate if desired + + // ignore inactive groups + if (!boxGroup.active) + { + continue; + } + + // allow API users to cancel this object's rendering + boolean cancelRendering = ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericObjectRenderEvent.class, new DhApiBeforeGenericObjectRenderEvent.EventParam(renderEventParam, boxGroup)); + if (cancelRendering) + { + continue; + } + + // update instanced data if needed + if (this.instancedRenderingAvailable) + { + boxGroup.tryUpdateInstancedDataAsync(); + + // skip groups that haven't been uploaded yet + if (boxGroup.vertexBufferContainer.getState() != GlGenericObjectVertexContainer.EState.RENDER) + { + continue; + } + } + + + + // render // + + profiler.popPush("rendering"); + try (IProfilerWrapper.IProfileBlock namespace_profile = profiler.push(boxGroup.getResourceLocationNamespace()); + IProfilerWrapper.IProfileBlock location_profile = profiler.push(boxGroup.getResourceLocationPath())) + { + if (this.instancedRenderingAvailable) + { + this.renderBoxGroupInstanced(shaderProgram, renderEventParam, boxGroup, camPos, profiler); + } + else + { + this.renderBoxGroupDirect(shaderProgram, renderEventParam, boxGroup, camPos, profiler); + } + } + + boxGroup.postRender(renderEventParam); + } + + + + //==========// + // clean up // + //==========// + + profiler.popPush("cleanup"); + + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderCleanupEvent.class, renderEventParam); + + if (renderWireframe) + { + // default back to GL_FILL since all other rendering uses it + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); + GLMC.enableFaceCulling(); + } + + shaderProgram.unbind(); } - - - - //==========// - // clean up // - //==========// - - profiler.popPush("cleanup"); - - ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeGenericRenderCleanupEvent.class, renderEventParam); - - if (renderWireframe) - { - // default back to GL_FILL since all other rendering uses it - GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); - GLMC.enableFaceCulling(); - } - - shaderProgram.unbind(); - - profiler.pop(); } //endregion @@ -561,72 +556,71 @@ public class GlGenericObjectRenderer implements IDhGenericRenderer RenderableBoxGroup boxGroup, Vec3d camPos, IProfilerWrapper profiler) { - // update instance data // - - profiler.push("vertex setup"); - - DhApiRenderableBoxGroupShading shading = boxGroup.shading; - if (shading == null) + try (IProfilerWrapper.IProfileBlock render_profile = profiler.push("vertex setup")) { - shading = DEFAULT_SHADING; - } - - shaderProgram.fillIndirectUniformData( + + // update instance data // + DhApiRenderableBoxGroupShading shading = boxGroup.shading; + if (shading == null) + { + shading = DEFAULT_SHADING; + } + + shaderProgram.fillIndirectUniformData( renderEventParam, shading, boxGroup, camPos); - - - - // Bind instance data // - profiler.popPush("binding"); - - GlGenericObjectVertexContainer container = (GlGenericObjectVertexContainer)(boxGroup.vertexBufferContainer); - - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.color); - GL32.glEnableVertexAttribArray(1); - GL32.glVertexAttribPointer(1, 4, GL32.GL_FLOAT, false, 4 * Float.BYTES, 0); - this.vertexAttribDivisor(1, 1); - - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.scale); - GL32.glEnableVertexAttribArray(2); - this.vertexAttribDivisor(2, 1); - GL32.glVertexAttribPointer(2, 3, GL32.GL_FLOAT, false, 3 * Float.BYTES, 0); - - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.chunkPos); - GL32.glEnableVertexAttribArray(3); - this.vertexAttribDivisor(3, 1); - GL32.glVertexAttribIPointer(3, 3, GL32.GL_INT, 3 * Integer.BYTES, 0); - - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.subChunkPos); - GL32.glEnableVertexAttribArray(4); - this.vertexAttribDivisor(4, 1); - GL32.glVertexAttribPointer(4, 3, GL32.GL_FLOAT, false, 3 * Float.BYTES, 0); - - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.material); - GL32.glEnableVertexAttribArray(5); - this.vertexAttribDivisor(5, 1); - GL32.glVertexAttribIPointer(5, 1, GL32.GL_BYTE, Byte.BYTES, 0); - - - // Draw instanced - profiler.popPush("render"); - if (container.uploadedBoxCount > 0) - { - GL32.glDrawElementsInstanced(GL32.GL_TRIANGLES, BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0, container.uploadedBoxCount); + + + + // Bind instance data // + profiler.popPush("binding"); + + GlGenericObjectVertexContainer container = (GlGenericObjectVertexContainer) (boxGroup.vertexBufferContainer); + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.color); + GL32.glEnableVertexAttribArray(1); + GL32.glVertexAttribPointer(1, 4, GL32.GL_FLOAT, false, 4 * Float.BYTES, 0); + this.vertexAttribDivisor(1, 1); + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.scale); + GL32.glEnableVertexAttribArray(2); + this.vertexAttribDivisor(2, 1); + GL32.glVertexAttribPointer(2, 3, GL32.GL_FLOAT, false, 3 * Float.BYTES, 0); + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.chunkPos); + GL32.glEnableVertexAttribArray(3); + this.vertexAttribDivisor(3, 1); + GL32.glVertexAttribIPointer(3, 3, GL32.GL_INT, 3 * Integer.BYTES, 0); + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.subChunkPos); + GL32.glEnableVertexAttribArray(4); + this.vertexAttribDivisor(4, 1); + GL32.glVertexAttribPointer(4, 3, GL32.GL_FLOAT, false, 3 * Float.BYTES, 0); + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, container.material); + GL32.glEnableVertexAttribArray(5); + this.vertexAttribDivisor(5, 1); + GL32.glVertexAttribIPointer(5, 1, GL32.GL_BYTE, Byte.BYTES, 0); + + + // Draw instanced + profiler.popPush("render"); + if (container.uploadedBoxCount > 0) + { + GL32.glDrawElementsInstanced(GL32.GL_TRIANGLES, BOX_INDICES.length, GL32.GL_UNSIGNED_INT, 0, container.uploadedBoxCount); + } + + + // Clean up + profiler.popPush("cleanup"); + + GL32.glDisableVertexAttribArray(1); + GL32.glDisableVertexAttribArray(2); + GL32.glDisableVertexAttribArray(3); + GL32.glDisableVertexAttribArray(4); + GL32.glDisableVertexAttribArray(5); } - - - // Clean up - profiler.popPush("cleanup"); - - GL32.glDisableVertexAttribArray(1); - GL32.glDisableVertexAttribArray(2); - GL32.glDisableVertexAttribArray(3); - GL32.glDisableVertexAttribArray(4); - GL32.glDisableVertexAttribArray(5); - - profiler.pop(); } /** * Clean way to handle both {@link GL33#glVertexAttribDivisor} and {@link ARBInstancedArrays#glVertexAttribDivisorARB} @@ -694,8 +688,6 @@ public class GlGenericObjectRenderer implements IDhGenericRenderer break; } } - - profiler.pop(); } //endregion @@ -756,4 +748,27 @@ public class GlGenericObjectRenderer implements IDhGenericRenderer + //================// + // base overrides // + //================// + //region + + @Override + public void close() + { + if (this.boxVertexBuffer != null) + { + this.boxVertexBuffer.close(); + } + + if (this.boxIndexBuffer != null) + { + this.boxIndexBuffer.close(); + } + } + + //endregion + + + } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/GLProxy.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/GLProxy.java index 5a3b2af90..49c577f8e 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/GLProxy.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/GLProxy.java @@ -21,13 +21,17 @@ package com.seibel.distanthorizons.common.render.openGl.glObject; import com.seibel.distanthorizons.api.enums.config.EDhApiGLErrorHandlingMode; import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; +import com.seibel.distanthorizons.api.enums.config.EDhApiLoggerLevel; import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.config.types.ConfigEntry; +import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.jar.EPlatform; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.util.objects.GLMessages.*; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor; import com.seibel.distanthorizons.coreapi.ModInfo; import org.lwjgl.glfw.GLFW; import org.lwjgl.opengl.GL; @@ -46,10 +50,30 @@ import java.util.concurrent.ConcurrentHashMap; */ public class GLProxy { - public static final DhLogger LOGGER = new DhLoggerBuilder() - .fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile) - .chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat) - .build(); + private static final IIrisAccessor IRIS_ACCESSOR = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class); + + public static final DhLogger LOGGER; + static + { + DhLoggerBuilder loggerBuilder = new DhLoggerBuilder(); + loggerBuilder.fileLevelConfig(Config.Common.Logging.logRendererGLEventToFile); + + // don't send chat messages if Iris is present since + // Iris is known to cause (harmless) GL errors + // and this can confuse users + boolean irisPresent = (IRIS_ACCESSOR != null); + if (!irisPresent) + { + loggerBuilder.chatLevelConfig(Config.Common.Logging.logRendererGLEventToChat); + } + + LOGGER = loggerBuilder.build(); + + if (irisPresent) + { + LOGGER.info("Iris detected, Distant Horizons OpenGL error logging won't be sent in the chat due to Iris throwing known (harmless) OpenGL errors. This is a bug with Iris, not Distant Horizons."); + } + } public static final Set LOGGED_GL_MESSAGES = Collections.newSetFromMap(new ConcurrentHashMap()); @@ -220,16 +244,7 @@ public class GLProxy return instance; } - public EDhApiGpuUploadMethod getGpuUploadMethod() - { - EDhApiGpuUploadMethod uploadOverride = Config.Client.Advanced.Debugging.OpenGl.glUploadMode.get(); - if (uploadOverride == EDhApiGpuUploadMethod.AUTO) - { - return this.preferredUploadMethod; - } - - return uploadOverride; - } + public EDhApiGpuUploadMethod getGpuUploadMethod() { return this.preferredUploadMethod; } public static boolean runningOnRenderThread() { @@ -247,7 +262,7 @@ public class GLProxy //region /** this method is called on the render thread at the point of the GL Error */ - private static void logMessage(GLMessage msg) + private static void logMessage(GLMessage glMessage) { EDhApiGLErrorHandlingMode errorHandlingMode = Config.Client.Advanced.Debugging.OpenGl.glErrorHandlingMode.get(); if (errorHandlingMode == EDhApiGLErrorHandlingMode.IGNORE) @@ -258,19 +273,26 @@ public class GLProxy boolean onlyLogOnce = Config.Client.Advanced.Debugging.OpenGl.onlyLogGlErrorsOnce.get(); - String errorMessage = "GL ERROR [" + msg.id + "] from [" + msg.source + "]: [" + msg.message + "]"+(onlyLogOnce ? " this message will only be logged once" : "")+"."; if (onlyLogOnce - && !LOGGED_GL_MESSAGES.add(errorMessage)) + && !LOGGED_GL_MESSAGES.add(glMessage.message)) { // this message has already been logged return; } + String errorMessage = "GL ERROR [" + glMessage.id + "] from [" + glMessage.source + "]: [" + glMessage.message + "]."; + if (onlyLogOnce) + { + errorMessage += " This message will only be logged once."; + errorMessage += " Note: Distant Horizons will catch and log OpenGL errors from other mods, not just DH itself; if everything is rendering correctly these errors can probably be ignored."; + } + + // create an exception so we get a stacktrace of where the message was triggered from RuntimeException exception = new RuntimeException(errorMessage); - if (msg.type == EGLMessageType.ERROR || msg.type == EGLMessageType.UNDEFINED_BEHAVIOR) + if (glMessage.type == EGLMessageType.ERROR || glMessage.type == EGLMessageType.UNDEFINED_BEHAVIOR) { // critical error @@ -287,7 +309,7 @@ public class GLProxy { // non-critical log - EGLMessageSeverity severity = msg.severity; + EGLMessageSeverity severity = glMessage.severity; if (severity == null) { // just in case the message was malformed diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLBuffer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLBuffer.java index d556402a9..27aad3dc0 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLBuffer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLBuffer.java @@ -21,14 +21,19 @@ package com.seibel.distanthorizons.common.render.openGl.glObject.buffer; import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy; -import com.seibel.distanthorizons.common.render.openGl.glObject.GLState; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper; import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.jar.EPlatform; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.ThreadUtil; +import com.seibel.distanthorizons.core.util.objects.Pair; +import com.seibel.distanthorizons.core.util.objects.pooling.PhantomLoggingHelper; +import com.seibel.distanthorizons.coreapi.ModInfo; +import com.seibel.distanthorizons.coreapi.util.StringUtil; import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL44; @@ -36,9 +41,12 @@ import java.lang.ref.PhantomReference; import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.StampedLock; public class GLBuffer implements AutoCloseable { @@ -50,26 +58,57 @@ public class GLBuffer implements AutoCloseable private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; + // TODO move to a shared location that can be handled by both GL and Blaze3D buffers + /** if enabled the number of GC'ed buffers will be logged */ + private static final boolean LOG_PHANTOM_RECOVERY = ModInfo.IS_DEV_BUILD; + /** If enabled buffers allocation/upload stacks will be logged when GC'ed. */ + private static final boolean LOG_PHANTOM_ALLOCATION_STACKS = false; // disabled by default due to the increased GC load + public static final double BUFFER_EXPANSION_MULTIPLIER = 1.3; public static final double BUFFER_SHRINK_TRIGGER = BUFFER_EXPANSION_MULTIPLIER * BUFFER_EXPANSION_MULTIPLIER; + + /** + * On macOS the legacy OpenGL -> Metal bridge crashes with SIGBUS in + * {@code _platform_memmove} when {@code glBufferData} or {@code glBufferSubData} + * are called with a large ByteBuffer in one shot. To work around it, we split + * the upload into smaller sub-data calls that each fit comfortably inside the + * driver's internal staging path.
+ * 256 KiB tuned empirically against macOS 26.5 — a 1 MiB chunk still + * tripped the same {@code _platform_memmove} crash inside + * {@code glBufferSubData_Exec}, but 256 KiB consistently survives. + */ + public static final int MAC_UPLOAD_CHUNK_BYTES = 256 * 1024; + /** Threshold above which the chunked path kicks in on macOS. */ + public static final int MAC_UPLOAD_CHUNK_THRESHOLD = MAC_UPLOAD_CHUNK_BYTES; /** the number of active buffers, can be used for debugging */ public static AtomicInteger bufferCount = new AtomicInteger(0); private static final int PHANTOM_REF_CHECK_TIME_IN_MS = 5 * 1000; private static final ConcurrentHashMap, Integer> PHANTOM_TO_BUFFER_ID = new ConcurrentHashMap<>(); private static final ConcurrentHashMap> BUFFER_ID_TO_PHANTOM = new ConcurrentHashMap<>(); + private static final ConcurrentHashMap BUFFER_ID_TO_ALLOCATION_STRING = new ConcurrentHashMap<>(); private static final ReferenceQueue PHANTOM_REFERENCE_QUEUE = new ReferenceQueue<>(); private static final ThreadPoolExecutor CLEANUP_THREAD = ThreadUtil.makeSingleDaemonThreadPool("GLBuffer Cleanup"); - protected int id; + protected volatile int id = 0; public final int getId() { return this.id; } protected int size = 0; public int getSize() { return this.size; } protected boolean bufferStorage; - public final boolean isBufferStorage() { return this.bufferStorage; } protected boolean isMapped = false; + /** + * Locking on the render thread isn't great, but is needed due to an inconsistent + * race condition where VBOs can be marked as deleted outside the render thread.

+ * + * But, due to being a read-write lock the chance of freezing + * the render thread is very low + * and since this is a stamped lock, the optimistic read time is basically zero. + * (The optimistic lock time doesn't even appear in the profiler). + */ + public final StampedLock renderStampLock = new StampedLock(); + //==============// @@ -112,69 +151,119 @@ public class GLBuffer implements AutoCloseable LodUtil.assertNotReach("Thread ["+Thread.currentThread()+"] tried to create a GLBuffer outside the MC render thread."); } - // destroy the old buffer if one is present - if (this.id != 0) + + // lock to prevent the render thread from accessing the buffer's ID + // while we are removing it + long writeStamp = renderStampLock.writeLock(); + try { - destroyBufferIdNow(this.id); + final int oldId = this.id; + this.id = GLMC.glGenBuffers(); + //LOGGER.info("created [" + newId + "]."); + + // destroy the old buffer + // after the new one has been created + // to hopefully prevent a rare race conditions where the old ID + // is still used somewhere + if (oldId != 0) + { + // this ID doesn't need to be tracked anymore + tryRemoveBufferIdFromPhantom(oldId); + destroyBufferIdNow(oldId, "destroyOldAndCreate"); + } + + + this.bufferStorage = asBufferStorage; + bufferCount.getAndIncrement(); + + PhantomReference phantom = new PhantomReference<>(this, PHANTOM_REFERENCE_QUEUE); + PHANTOM_TO_BUFFER_ID.put(phantom, this.id); + BUFFER_ID_TO_PHANTOM.put(this.id, phantom); + + this.updateAllocationStackTrace(); + } + finally + { + renderStampLock.unlock(writeStamp); } - - this.id = GLMC.glGenBuffers(); - this.bufferStorage = asBufferStorage; - bufferCount.getAndIncrement(); - - PhantomReference phantom = new PhantomReference<>(this, PHANTOM_REFERENCE_QUEUE); - PHANTOM_TO_BUFFER_ID.put(phantom, this.id); - BUFFER_ID_TO_PHANTOM.put(this.id, phantom); - } protected void destroyAsync() { - if (this.id == 0) + // lock to prevent the render thread from accessing the buffer's ID + // while we are removing it + long writeStamp = renderStampLock.writeLock(); + try { - // the buffer has already been closed - return; + if (this.id == 0) + { + // the buffer has already been closed + return; + } + + final int idToDelete = this.id; // saving the ID to a separate variable is necessary so it can be captured by the lambda + + // remove the phantom tracking now so the phantom doesn't have the chance to + // get garbage collected before the render thread task runs + // (this can happen if MC is running at extremely low framerates like 1 fps via mods) + tryRemoveBufferIdFromPhantom(idToDelete); + + // mark the old data is invalid before deleting to prevent a rare race condition + // where the queued on render thread task runs before the ID is cleared + this.id = 0; + this.size = 0; + + //LOGGER.info("async destroy [" + idToDelete + "]."); + RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer destroyAsync", () -> { destroyBufferIdNow(idToDelete, "destroyAsync"); }); + } + finally + { + renderStampLock.unlock(writeStamp); } - - RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer destroyAsync", () -> { destroyBufferIdNow(this.id); }); - - this.id = 0; - this.size = 0; } - private static void destroyBufferIdNow(int id) + + private static void destroyBufferIdNow(int id, String cause) { // only delete valid buffers if (id == 0) { + LOGGER.warn("Attempted to destroy a buffer with ID 0, VRAM memory leaks may occur, cause: ["+cause+"]."); return; } - // remove and clear the phantom reference if present - if (BUFFER_ID_TO_PHANTOM.containsKey(id)) + bufferCount.decrementAndGet(); + GLMC.glDeleteBuffers(id); + + if (Config.Client.Advanced.Debugging.logBufferGarbageCollection.get()) + { + LOGGER.info("destroyed buffer [" + id + "], remaining: [" + BUFFER_ID_TO_PHANTOM.size() + "], cause: ["+cause+"]."); + } + } + + /** should be called before {@link GLBuffer#destroyBufferIdNow} */ + private static void tryRemoveBufferIdFromPhantom(int id) + { + // will contain nothing if stack tracking isn't enabled + BUFFER_ID_TO_ALLOCATION_STRING.remove(id); + + PhantomReference phantom = BUFFER_ID_TO_PHANTOM.remove(id); + if (phantom != null) { - Reference phantom = BUFFER_ID_TO_PHANTOM.get(id); - // if we are manually closing this buffer, we don't want the phantom reference to accidentally close it again // this can cause a race condition were we accidentally delete an in-use buffer and cause NVIDIA // to throw an EXCEPTION_ACCESS_VIOLATION when we attempt to render it phantom.clear(); - PHANTOM_TO_BUFFER_ID.remove(phantom); - BUFFER_ID_TO_PHANTOM.remove(id); - } - - // destroy the buffer if it exists, - // the buffer may not exist if the destroy method is called twice - if (GL32.glIsBuffer(id)) - { - GLMC.glDeleteBuffers(id); - bufferCount.decrementAndGet(); - - if (Config.Client.Advanced.Debugging.logBufferGarbageCollection.get()) + Integer phantomId = PHANTOM_TO_BUFFER_ID.remove(phantom); + if (phantomId == null) { - LOGGER.info("destroyed buffer [" + id + "], remaining: [" + BUFFER_ID_TO_PHANTOM.size() + "]"); + LOGGER.warn("No Phantom->ID binding stored for ID ["+id+"]"); } } + else + { + LOGGER.warn("Unable to remove phantom GLBuffer with ID ["+id+"], buffer may have already been deleted."); + } } //endregion @@ -207,10 +296,14 @@ public class GLBuffer implements AutoCloseable - // GLState to prevent issues with corrupting the global GL state. - // This can happen especially on legacy GL for MC 1.16.5, creating a black screen. - // Maybe this will also fix random crashing on Mac? - try(GLState state = new GLState()) + // re-binding the old buffers is necessary for old MC versions for the following reasons: + // for 16.5 and older the screen may be black when on the home menu + // and for 1.19.2 - 1.21.4 the inventory/UI will render without a background + int vao = GL32.glGetInteger(GL32.GL_VERTEX_ARRAY_BINDING); + int vbo = GL32.glGetInteger(GL32.GL_ARRAY_BUFFER_BINDING); + int ebo = GL32.glGetInteger(GL32.GL_ELEMENT_ARRAY_BUFFER_BINDING); + + try { // make sure the buffer is ready for uploading this.createOrChangeBufferTypeForUpload(uploadMethod); @@ -232,6 +325,12 @@ public class GLBuffer implements AutoCloseable LodUtil.assertNotReach("Unknown GpuUploadMethod!"); } } + finally + { + GL32.glBindVertexArray(GL32.glIsVertexArray(vao) ? vao : 0); + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, GL32.glIsBuffer(vbo) ? vbo : 0); + GL32.glBindBuffer(GL32.GL_ELEMENT_ARRAY_BUFFER, GL32.glIsBuffer(ebo) ? ebo: 0); + } } /** Requires the buffer to be bound */ protected void uploadBufferStorage(ByteBuffer bb) @@ -250,8 +349,25 @@ public class GLBuffer implements AutoCloseable LodUtil.assertTrue(!this.bufferStorage, "Buffer is bufferStorage but its trying to use bufferData upload method!"); int bbSize = bb.limit() - bb.position(); - GL32.glBufferData(this.getBufferBindingTarget(), bb, bufferDataHint); + int target = this.getBufferBindingTarget(); + + if (shouldUploadToGpuInChunks(bbSize)) + { + // Two-step path used on macOS to dodge the Apple OpenGL -> Metal + // memmove SIGBUS triggered by uploading a large ByteBuffer in one + // glBufferData call: + // 1) allocate-only with the size overload (no memcpy) + // 2) fill the buffer through chunked glBufferSubData calls + GL32.glBufferData(target, (long) bbSize, bufferDataHint); + subDataUploadInChunks(target, 0, bb, MAC_UPLOAD_CHUNK_BYTES); + } + else + { + GL32.glBufferData(target, bb, bufferDataHint); + } this.size = bbSize; + + this.updateAllocationStackTrace(); } /** Requires the buffer to be bound */ protected void uploadSubData(ByteBuffer bb, int maxExpansionSize, int bufferDataHint) @@ -259,14 +375,30 @@ public class GLBuffer implements AutoCloseable LodUtil.assertTrue(!this.bufferStorage, "Buffer is bufferStorage but its trying to use subData upload method!"); int bbSize = bb.limit() - bb.position(); + int target = this.getBufferBindingTarget(); if (this.size < bbSize || this.size > bbSize * BUFFER_SHRINK_TRIGGER) { int newSize = (int) (bbSize * BUFFER_EXPANSION_MULTIPLIER); - if (newSize > maxExpansionSize) newSize = maxExpansionSize; - GL32.glBufferData(this.getBufferBindingTarget(), newSize, bufferDataHint); + if (newSize > maxExpansionSize) + { + newSize = maxExpansionSize; + } + + // allocate-only — no memcpy, safe on macOS regardless of size + GL32.glBufferData(target, (long) newSize, bufferDataHint); this.size = newSize; } - GL32.glBufferSubData(this.getBufferBindingTarget(), 0, bb); + + if (shouldUploadToGpuInChunks(bbSize)) + { + subDataUploadInChunks(target, 0, bb, MAC_UPLOAD_CHUNK_BYTES); + } + else + { + GL32.glBufferSubData(target, 0, bb); + } + + this.updateAllocationStackTrace(); } //endregion @@ -308,7 +440,6 @@ public class GLBuffer implements AutoCloseable { // recreate if the buffer storage type changed this.bind(); - destroyBufferIdNow(this.id); this.destroyOldAndCreate(uploadMethod.useBufferStorage); this.bind(); } @@ -325,6 +456,91 @@ public class GLBuffer implements AutoCloseable } } + /** + * macOS-only mitigation for the SIGBUS in + * {@code libsystem_platform.dylib _platform_memmove} that happens when the + * Apple OpenGL -> Metal translation layer copies a single large ByteBuffer + * out of LWJGL into driver memory. Splitting the copy into + * {@link #MAC_UPLOAD_CHUNK_BYTES} slices keeps every memmove inside a size + * the bridge handles reliably. + */ + private static boolean shouldUploadToGpuInChunks(int byteCount) + { + return EPlatform.get() == EPlatform.MACOS + && byteCount > MAC_UPLOAD_CHUNK_THRESHOLD; + } + + /** + * Uploads {@code bb} into the currently bound buffer at {@code baseOffset} + * using a sequence of {@link GL32#glBufferSubData(int, long, ByteBuffer)} + * calls of at most {@code chunkBytes} each. The buffer's position/limit are + * restored before returning. + */ + private static void subDataUploadInChunks(int target, int baseOffset, ByteBuffer bb, int chunkBytes) + { + final int origPos = bb.position(); + final int origLimit = bb.limit(); + try + { + final int total = origLimit - origPos; + int uploaded = 0; + while (uploaded < total) + { + int chunk = Math.min(chunkBytes, total - uploaded); + bb.position(origPos + uploaded); + bb.limit(origPos + uploaded + chunk); + GL32.glBufferSubData(target, (long) (baseOffset + uploaded), bb); + uploaded += chunk; + // Force the driver to drain its command queue between chunks + // so the OpenGL -> Metal bridge processes (and frees) each + // staging copy before the next sub-data call piles another + // memmove on top of it. + if (uploaded < total) + { + GL32.glFlush(); + } + } + } + finally + { + bb.limit(origLimit); + bb.position(origPos); + } + } + + /** + * used to help track down leaks where the buffer isn't properly closed + * Note: this probably needs extending to accept a stack trace from outside where it's being called + * since it's often called on the render thread in an un-helpful location. + */ + public void updateAllocationStackTrace() + { + if (LOG_PHANTOM_ALLOCATION_STACKS) + { + String stack; + + RenderThreadTaskHandler.QueuedRunnable parentQueuedRunnable; + // if this is running on the render thread, try getting the render task's stack trace instead + // since it's a lot more helpful than wherever the render thread tasks themselves are being run from + if (RenderThreadTaskHandler.INSTANCE.isCurrentThread() + && (parentQueuedRunnable = RenderThreadTaskHandler.INSTANCE.getCurrentlyRunningTask()) != null + && parentQueuedRunnable.stackTrace != null) + { + // trim off the getStacktrace() and queueRunningOnRenderThread() methods + StackTraceElement[] trimmedElements = Arrays.copyOfRange(parentQueuedRunnable.stackTrace, 2, parentQueuedRunnable.stackTrace.length); + stack = StringUtil.join("\n", trimmedElements).intern(); + } + else + { + // not running on the render thread, use the normal stack trace + StackTraceElement[] stackTraceElements = Thread.currentThread().getStackTrace(); + stack = StringUtil.join("\n", stackTraceElements).intern(); + } + + BUFFER_ID_TO_ALLOCATION_STRING.put(this.id, stack); + } + } + //endregion @@ -336,8 +552,13 @@ public class GLBuffer implements AutoCloseable private static void runPhantomReferenceCleanupLoop() { + // these arrays are stored here so they don't have to be re-allocated each loop + ArrayList> allocationStackTraceCountPairList = new ArrayList<>(); + while (true) { + allocationStackTraceCountPairList.clear(); + try { try @@ -346,20 +567,53 @@ public class GLBuffer implements AutoCloseable } catch (InterruptedException ignore) { } + int collectedCount = 0; Reference phantomRef = PHANTOM_REFERENCE_QUEUE.poll(); while (phantomRef != null) { // destroy the buffer if it hasn't been cleared yet - if (PHANTOM_TO_BUFFER_ID.containsKey(phantomRef)) + Integer idRef = PHANTOM_TO_BUFFER_ID.remove((PhantomReference)phantomRef); // cast to make IntelliJ happy + if (idRef != null) { - int id = PHANTOM_TO_BUFFER_ID.get(phantomRef); - RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer phantom destroy", () -> { destroyBufferIdNow(id); }); - //LOGGER.warn("Buffer Phantom collected, ID: ["+id+"]"); + BUFFER_ID_TO_PHANTOM.remove(idRef); + final int id = idRef; + RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("GLBuffer phantom destroy", () -> { destroyBufferIdNow(id, "runPhantomReferenceCleanupLoop"); }); + //LOGGER.info("Buffer Phantom collected, ID: ["+id+"]"); + + if (LOG_PHANTOM_ALLOCATION_STACKS) // stack trace shouldn't be null, but just in case + { + String stack = BUFFER_ID_TO_ALLOCATION_STRING.get(idRef); + PhantomLoggingHelper.putAndIncrementTrackingString(stack, allocationStackTraceCountPairList); + } + } + else + { + LOGGER.warn("Failed to find Buffer ID for phantom reference: ["+phantomRef+"]"); } + + collectedCount++; phantomRef = PHANTOM_REFERENCE_QUEUE.poll(); } + + + + if (LOG_PHANTOM_RECOVERY) + { + // we only want to log when something has been returned + if (collectedCount != 0) + { + LOGGER.warn("GLBuffer phantom recovered: ["+ F3Screen.NUMBER_FORMAT.format(collectedCount)+"]."); + + // log stack traces if present + if (LOG_PHANTOM_ALLOCATION_STACKS) + { + PhantomLoggingHelper.LogAllocationStackTracePairCounts(LOGGER, allocationStackTraceCountPairList); + } + } + } + } catch (Exception e) { diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLIndexBuffer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLIndexBuffer.java index d7ca1b74b..0a8db0883 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLIndexBuffer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLIndexBuffer.java @@ -34,14 +34,14 @@ public class GLIndexBuffer extends GLBuffer * bigger than the upload payload */ protected int indicesCount = 0; - public int getIndicesCount() { return this.indicesCount; } - protected int type = GL32.GL_UNSIGNED_INT; - public int getType() { return type; } + protected int glType = GL32.GL_UNSIGNED_INT; + public int getGlType() { return this.glType; } + + + + public GLIndexBuffer(boolean isBufferStorage) { super(isBufferStorage); } + - public GLIndexBuffer(boolean isBufferStorage) - { - super(isBufferStorage); - } @Override public void destroyAsync() diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLVertexBuffer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLVertexBuffer.java index 4889c9a6d..13e4ceb6d 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLVertexBuffer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GLVertexBuffer.java @@ -20,12 +20,13 @@ package com.seibel.distanthorizons.common.render.openGl.glObject.buffer; import java.nio.ByteBuffer; -import java.nio.ByteOrder; -import com.seibel.distanthorizons.common.render.openGl.GlDhTerrainShaderProgram; import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy; -import com.seibel.distanthorizons.common.render.openGl.glObject.enums.GLEnums; +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.IndexBufferBuilder; import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.LodQuadBuilder; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler; +import com.seibel.distanthorizons.core.wrapperInterfaces.render.AbstractDhRenderApiDefinition; import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper; import org.lwjgl.opengl.GL32; @@ -41,6 +42,8 @@ import org.lwjgl.system.MemoryUtil; */ public class GLVertexBuffer extends GLBuffer implements IVertexBufferWrapper { + private static final AbstractDhRenderApiDefinition RENDER_DEF = SingletonInjector.INSTANCE.get(AbstractDhRenderApiDefinition.class); + /** * When uploading to a buffer that is too small, recreate it this many times * bigger than the upload payload @@ -48,7 +51,20 @@ public class GLVertexBuffer extends GLBuffer implements IVertexBufferWrapper protected int vertexCount = 0; public int getVertexCount() { return this.vertexCount; } - public GlQuadIndexBuffer quadIBO = null; + + private GlQuadIndexBuffer quadIBO = null; + private static GlQuadIndexBuffer GLOBAL_QUAD_IBO = null; + public GlQuadIndexBuffer getQuadIBO() + { + if (RENDER_DEF.useSingleIbo()) + { + return GLOBAL_QUAD_IBO; + } + else + { + return this.quadIBO; + } + } @@ -57,6 +73,25 @@ public class GLVertexBuffer extends GLBuffer implements IVertexBufferWrapper //=============// //region + static + { + if (RENDER_DEF.useSingleIbo()) + { + RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("Global IBO Creation", () -> + { + GLOBAL_QUAD_IBO = new GlQuadIndexBuffer(); + + int maxSize = LodQuadBuilder.getMaxBufferByteSize(); + int maxVertexCount = maxSize / LodQuadBuilder.BYTES_PER_VERTEX; + int maxQuadCount = (maxVertexCount / 4); + + ByteBuffer buffer = IndexBufferBuilder.createBuffer(maxQuadCount); + GLOBAL_QUAD_IBO.upload(buffer, maxQuadCount); + MemoryUtil.memFree(buffer); + }); + } + } + public GLVertexBuffer() { this(GLProxy.getInstance().getGpuUploadMethod() == EDhApiGpuUploadMethod.BUFFER_STORAGE); } public GLVertexBuffer(boolean isBufferStorage) { super(isBufferStorage); } @@ -73,7 +108,7 @@ public class GLVertexBuffer extends GLBuffer implements IVertexBufferWrapper public int getBufferBindingTarget() { return GL32.GL_ARRAY_BUFFER; } @Override - public void upload(ByteBuffer buffer, int vertexCount) + public void uploadVertexBuffer(ByteBuffer buffer, int vertexCount) { EDhApiGpuUploadMethod uploadMethod = GLProxy.getInstance().getGpuUploadMethod(); int maxBufferSize = LodQuadBuilder.getMaxBufferByteSize(); @@ -95,28 +130,41 @@ public class GLVertexBuffer extends GLBuffer implements IVertexBufferWrapper // If size is zero, just ignore it. if (byteBuffer.limit() - byteBuffer.position() != 0) { - // vertex data - { - super.uploadBuffer(byteBuffer, uploadMethod, maxExpansionSize, uploadMethod.useBufferStorage ? 0 : GL32.GL_STATIC_DRAW); - } - - // index data - { - if (this.quadIBO != null) - { - this.quadIBO.close(); - } - - int quadCount = (vertexCount / 4); - this.quadIBO = new GlQuadIndexBuffer(); - this.quadIBO.reserve(quadCount); - } - + super.uploadBuffer(byteBuffer, uploadMethod, maxExpansionSize, uploadMethod.useBufferStorage ? 0 : GL32.GL_STATIC_DRAW); } this.vertexCount = vertexCount; } + + @Override + public void uploadIndexBuffer(ByteBuffer buffer, int vertexCount) + { + if (RENDER_DEF.useSingleIbo()) + { + // ignore index uploading when running a single IBO + return; + } + + // If size is zero, just ignore it. + if (vertexCount == 0) + { + return; + } + + + + if (this.quadIBO != null) + { + this.quadIBO.close(); + } + + this.quadIBO = new GlQuadIndexBuffer(); + + int quadCount = (vertexCount / 4); + this.quadIBO.upload(buffer, quadCount); + } + //endregion @@ -132,7 +180,10 @@ public class GLVertexBuffer extends GLBuffer implements IVertexBufferWrapper public void destroyAsync() { super.destroyAsync(); - this.quadIBO.destroyAsync(); + if (this.quadIBO != null) + { + this.quadIBO.destroyAsync(); + } this.vertexCount = 0; } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GlQuadIndexBuffer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GlQuadIndexBuffer.java index bf50628bf..8944b5107 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GlQuadIndexBuffer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/glObject/buffer/GlQuadIndexBuffer.java @@ -21,6 +21,7 @@ package com.seibel.distanthorizons.common.render.openGl.glObject.buffer; import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; import com.seibel.distanthorizons.common.render.openGl.glObject.enums.GLEnums; +import com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding.IndexBufferBuilder; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import org.lwjgl.opengl.GL32; @@ -42,7 +43,7 @@ public class GlQuadIndexBuffer extends GLIndexBuffer public GlQuadIndexBuffer() { super(false); } - public void reserve(int quadCount) + public void upload(ByteBuffer buffer, int quadCount) { if (quadCount < 0) { @@ -60,30 +61,10 @@ public class GlQuadIndexBuffer extends GLIndexBuffer { return; } - int vertexCount = quadCount * 4; // 4 vertices per quad - if (vertexCount < 255) - { - // Reserve 1 for the reset index - this.type = GL32.GL_UNSIGNED_BYTE; - } - else if (vertexCount < 65535) - { - // Reserve 1 for the reset index - this.type = GL32.GL_UNSIGNED_SHORT; - } - else - { - this.type = GL32.GL_UNSIGNED_INT; - } - - ByteBuffer buffer = MemoryUtil.memAlloc(this.indicesCount * GLEnums.getTypeSize(this.type)); - buildBuffer(quadCount, buffer, this.type); - this.bind(); + this.glType = GL32.GL_UNSIGNED_INT; super.uploadBuffer(buffer, EDhApiGpuUploadMethod.DATA, - this.indicesCount * GLEnums.getTypeSize(this.type), GL32.GL_STATIC_DRAW); - - MemoryUtil.memFree(buffer); + this.indicesCount * GLEnums.getTypeSize(this.glType), GL32.GL_STATIC_DRAW); } //endregion @@ -95,95 +76,7 @@ public class GlQuadIndexBuffer extends GLIndexBuffer //=========// //region - public int getCapacity() { return super.getSize() / GLEnums.getTypeSize(this.getType()); } - - //endregion - - - - //==========// - // building // - //==========// - //region - - public static void buildBuffer(int quadCount, ByteBuffer buffer, int type) - { - switch (type) - { - case GL32.GL_UNSIGNED_BYTE: - buildBufferByte(quadCount, buffer); - break; - case GL32.GL_UNSIGNED_SHORT: - buildBufferShort(quadCount, buffer); - break; - case GL32.GL_UNSIGNED_INT: - buildBufferInt(quadCount, buffer); - break; - default: - throw new IllegalStateException("Unknown buffer type: [" + type + "]."); - } - } - - private static void buildBufferByte(int quadCount, ByteBuffer buffer) - { - for (int i = 0; i < quadCount; i++) - { - int vIndex = i * 4; - // First triangle - buffer.put((byte) (vIndex)); - buffer.put((byte) (vIndex + 1)); - buffer.put((byte) (vIndex + 2)); - // Second triangle - buffer.put((byte) (vIndex + 2)); - buffer.put((byte) (vIndex + 3)); - buffer.put((byte) (vIndex)); - } - if (buffer.hasRemaining()) - { - throw new IllegalStateException("QuadElementBuffer is not full somehow after building"); - } - buffer.rewind(); - } - private static void buildBufferShort(int quadCount, ByteBuffer buffer) - { - for (int i = 0; i < quadCount; i++) - { - int vIndex = i * 4; - // First triangle - buffer.putShort((short) (vIndex)); - buffer.putShort((short) (vIndex + 1)); - buffer.putShort((short) (vIndex + 2)); - // Second triangle - buffer.putShort((short) (vIndex + 2)); - buffer.putShort((short) (vIndex + 3)); - buffer.putShort((short) (vIndex)); - } - if (buffer.hasRemaining()) - { - throw new IllegalStateException("QuadElementBuffer is not full somehow after building"); - } - buffer.rewind(); - } - private static void buildBufferInt(int quadCount, ByteBuffer buffer) - { - for (int i = 0; i < quadCount; i++) - { - int vIndex = i * 4; - // First triangle - buffer.putInt(vIndex); - buffer.putInt(vIndex + 1); - buffer.putInt(vIndex + 2); - // Second triangle - buffer.putInt(vIndex + 2); - buffer.putInt(vIndex + 3); - buffer.putInt(vIndex); - } - if (buffer.hasRemaining()) - { - throw new IllegalStateException("QuadElementBuffer is not full somehow after building"); - } - buffer.rewind(); - } + public int getCapacity() { return super.getSize() / GLEnums.getTypeSize(this.getGlType()); } //endregion diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlDhFarFadeRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlDhFarFadeRenderer.java index f9a7d7960..5fbd50b32 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlDhFarFadeRenderer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlDhFarFadeRenderer.java @@ -122,8 +122,6 @@ public class GlDhFarFadeRenderer implements IDhFarFadeRenderer { try { - //profiler.push("Fade Generate"); - this.init(); // resize the framebuffer if necessary @@ -141,8 +139,6 @@ public class GlDhFarFadeRenderer implements IDhFarFadeRenderer GlDhFarFadeShader.INSTANCE.setProjectionMatrix(renderParams.mcModelViewMatrix, renderParams.mcProjectionMatrix); GlDhFarFadeShader.INSTANCE.render(renderParams); - //profiler.popPush("Fade Apply"); - GlDhFarFadeApplyShader.INSTANCE.fadeTexture = this.fadeTexture; GlDhFarFadeApplyShader.INSTANCE.readFramebuffer = GlDhFarFadeShader.INSTANCE.frameBuffer; GlDhFarFadeApplyShader.INSTANCE.drawFramebuffer = GlDhMetaRenderer.INSTANCE.getActiveFramebufferId(); @@ -152,10 +148,6 @@ public class GlDhFarFadeRenderer implements IDhFarFadeRenderer { LOGGER.error("Unexpected error during fade render, error: ["+e.getMessage()+"].", e); } - finally - { - //profiler.pop(); - } } //emdregion diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlVanillaFadeRenderer.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlVanillaFadeRenderer.java index 238f917ce..0592caa34 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlVanillaFadeRenderer.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/postProcessing/fade/GlVanillaFadeRenderer.java @@ -135,14 +135,9 @@ public class GlVanillaFadeRenderer implements IDhVanillaFadeRenderer IProfilerWrapper profiler = MC_CLIENT.getProfiler(); - profiler.pop(); // get out of "terrain" - profiler.push("DH-Vanilla Fade"); - - - try(GLState mcState = new GLState()) + try (IProfilerWrapper.IProfileBlock fade_profile = profiler.push("DH-Vanilla Fade"); + GLState mcState = new GLState()) { - profiler.push("Vanilla Fade Generate"); - this.init(); // resize the framebuffer if necessary @@ -165,19 +160,15 @@ public class GlVanillaFadeRenderer implements IDhVanillaFadeRenderer // otherwise we can directly render to their texture if (MC_RENDER.mcRendersToFrameBuffer()) { - profiler.popPush("Vanilla Fade Apply"); - GlDhFarFadeApplyShader.INSTANCE.fadeTexture = this.fadeTexture; GlDhFarFadeApplyShader.INSTANCE.readFramebuffer = GlDhVanillaFadeShader.INSTANCE.frameBuffer; GlDhFarFadeApplyShader.INSTANCE.drawFramebuffer = MC_RENDER.getTargetFramebuffer(); GlDhFarFadeApplyShader.INSTANCE.render(renderParams); } - - profiler.pop(); } catch (Exception e) { - LOGGER.error("Unexpected error during fade render, error: ["+e.getMessage()+"].", e); + LOGGER.error("Unexpected error during fade render, error: [" + e.getMessage() + "].", e); } } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhTerrainShaderProgram.java b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/terrain/GlDhTerrainShaderProgram.java similarity index 87% rename from common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhTerrainShaderProgram.java rename to common/src/main/java/com/seibel/distanthorizons/common/render/openGl/terrain/GlDhTerrainShaderProgram.java index a046781a6..2b156b11d 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/GlDhTerrainShaderProgram.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/render/openGl/terrain/GlDhTerrainShaderProgram.java @@ -1,12 +1,12 @@ -package com.seibel.distanthorizons.common.render.openGl; +package com.seibel.distanthorizons.common.render.openGl.terrain; import com.seibel.distanthorizons.api.interfaces.override.rendering.IDhApiShaderProgram; import com.seibel.distanthorizons.api.methods.events.abstractEvents.*; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; import com.seibel.distanthorizons.api.objects.math.DhApiVec3f; +import com.seibel.distanthorizons.common.render.openGl.GlDhMetaRenderer; import com.seibel.distanthorizons.common.render.openGl.glObject.GLProxy; import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GLVertexBuffer; -import com.seibel.distanthorizons.common.render.openGl.glObject.buffer.GlQuadIndexBuffer; import com.seibel.distanthorizons.common.render.openGl.glObject.shader.GlShaderProgram; import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlAbstractVertexAttribute; import com.seibel.distanthorizons.common.render.openGl.glObject.vertexAttribute.GlVertexAttributePostGL43; @@ -30,7 +30,6 @@ import com.seibel.distanthorizons.core.util.objects.SortedArraySet; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.render.objects.IVertexBufferWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.render.renderPass.IDhTerrainRenderer; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import org.lwjgl.opengl.GL32; @@ -38,14 +37,12 @@ import org.lwjgl.opengl.GL32; * Handles rendering the normal LOD terrain. * @see LodQuadBuilder */ -public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiShaderProgram, IDhTerrainRenderer +public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiShaderProgram { public static final DhLogger LOGGER = new DhLoggerBuilder() .fileLevelConfig(Config.Common.Logging.logRendererEventToFile) .build(); - public static final GlDhTerrainShaderProgram INSTANCE = new GlDhTerrainShaderProgram(); - private static final MinecraftGLWrapper GLMC = MinecraftGLWrapper.INSTANCE; private static final IIrisAccessor IRIS_ACCESSOR = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class); @@ -87,7 +84,7 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS //=============// //region - private GlDhTerrainShaderProgram() + public GlDhTerrainShaderProgram() { super( "assets/distanthorizons/shaders/shared/gl/standard.vert", @@ -96,7 +93,7 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS ); } - public void init() + public void tryInit() { if (this.init) { @@ -173,7 +170,7 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS @Override public void bind() { - this.init(); + this.tryInit(); super.bind(); this.vao.bind(); } @@ -262,7 +259,6 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS //===========// //region - @Override public void render(RenderParams renderEventParam, boolean opaquePass, SortedArraySet bufferContainers, IProfilerWrapper profiler) { //=======================// @@ -293,6 +289,9 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS GLMC.disableBlend(); } + // needs to be triggered after DH attempts to set the GL state so that Iris + // can override it as needed + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam); @@ -300,8 +299,6 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS // rendering // //===========// - ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeRenderPassEvent.class, renderEventParam); - if (IRIS_ACCESSOR != null) { // done to fix a bug with Iris where face culling isn't properly set or reverted in the MC state manager @@ -317,6 +314,12 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS for (int lodIndex = 0; lodIndex < bufferContainers.size(); lodIndex++) { LodBufferContainer bufferContainer = bufferContainers.get(lodIndex); + if (!bufferContainer.buffersUploaded) + { + // make sure we don't accidentally try + // rendering a buffer that is (or is going to be) freed + continue; + } // set uniforms and fire events { @@ -332,7 +335,7 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeBufferRenderEvent.class, new DhApiBeforeBufferRenderEvent.EventParam(renderEventParam, modelPos)); } - IVertexBufferWrapper[] vertexBuffers = (opaquePass ? bufferContainer.vbos : bufferContainer.vbosTransparent); + IVertexBufferWrapper[] vertexBuffers = (opaquePass ? bufferContainer.vboOpaqueWrappers : bufferContainer.vboTransparentWrappers); for (int vboIndex = 0; vboIndex < vertexBuffers.length; vboIndex++) { GLVertexBuffer vbo = (GLVertexBuffer) vertexBuffers[vboIndex]; @@ -341,25 +344,45 @@ public class GlDhTerrainShaderProgram extends GlShaderProgram implements IDhApiS continue; } - if (vbo.getVertexCount() == 0) + + // for lock information please view the lock's javadocs + long vboReadStamp = vbo.renderStampLock.readLock(); + long iboReadStamp = vbo.getQuadIBO().renderStampLock.readLock(); + try { - continue; + // don't render empty sections + if (vbo.getVertexCount() == 0) + { + continue; + } + + // don't render deleted VBOs (this will crash the driver/game) + if (vbo.getId() == 0 + || vbo.getQuadIBO().getId() == 0) + { + continue; + } + + // 4 vertices per face, but 6 indices (IE 2 triangles) per face, aka need to multiply by 1.5 + int indexCount = (int) (vbo.getVertexCount() * 1.5); + + vbo.bind(); + vbo.getQuadIBO().bind(); + + GlDhMetaRenderer.INSTANCE.shaderProgramForThisFrame.bindVertexBuffer(vbo.getId()); + GL32.glDrawElements( + GL32.GL_TRIANGLES, + indexCount, + vbo.getQuadIBO().getGlType(), 0); + + vbo.unbind(); + vbo.getQuadIBO().unbind(); + } + finally + { + vbo.renderStampLock.unlock(vboReadStamp); + vbo.getQuadIBO().renderStampLock.unlock(iboReadStamp); } - - // 4 vertices per face, but 6 indices (IE 2 triangles) per face, aka need to multiply by 1.5 - int indexCount = (int)(vbo.getVertexCount() * 1.5); - - vbo.bind(); - vbo.quadIBO.bind(); - - GlDhMetaRenderer.INSTANCE.shaderProgramForThisFrame.bindVertexBuffer(vbo.getId()); - GL32.glDrawElements( - GL32.GL_TRIANGLES, - indexCount, - vbo.quadIBO.getType(), 0); - - vbo.unbind(); - vbo.quadIBO.unbind(); } } } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/DependencySetup.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/DependencySetup.java index b0d03841a..0f8ffb7cd 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/DependencySetup.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/DependencySetup.java @@ -25,7 +25,7 @@ import com.seibel.distanthorizons.common.render.blaze.BlazeDhRenderApiDefinition import com.seibel.distanthorizons.common.render.openGl.GlDhRenderApiDefinition; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.render.renderer.GenericRenderObjectFactory; -import com.seibel.distanthorizons.common.wrappers.gui.ClassicConfigGUI; +import com.seibel.distanthorizons.common.wrappers.gui.classicConfig.ClassicConfigGUI; import com.seibel.distanthorizons.common.wrappers.gui.LangWrapper; import com.seibel.distanthorizons.common.wrappers.level.KeyedClientLevelManager; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper; @@ -80,8 +80,20 @@ public class DependencySetup SingletonInjector.INSTANCE.bind(IConfigGui.class, ClassicConfigGUI.CONFIG_CORE_INTERFACE); } - public static void setRenderingApiBindings() + private static boolean renderingApiBindingsSet = false; + /** will be called from a DH thread, not the render thread */ + public synchronized static void setRenderingApiBindings() { + // shouldn't happen, but there was a single report that this method was triggered twice + if (renderingApiBindingsSet) + { + LOGGER.warn("Rendering bindings already set, skipping. How did this happen?"); + return; + } + renderingApiBindingsSet = true; + + + EDhApiRenderApi renderingApiEnum = Config.Client.Advanced.Graphics.Experimental.renderingApi.get(); if (renderingApiEnum == EDhApiRenderApi.AUTO) { diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/McObjectConverter.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/McObjectConverter.java index 83930afec..b6e050174 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/McObjectConverter.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/McObjectConverter.java @@ -149,7 +149,16 @@ public class McObjectConverter } public static BlockPos Convert(DhBlockPos wrappedPos) { return new BlockPos(wrappedPos.getX(), wrappedPos.getY(), wrappedPos.getZ()); } + public static ChunkPos Convert(DhChunkPos wrappedPos) { return new ChunkPos(wrappedPos.getX(), wrappedPos.getZ()); } + public static DhChunkPos Convert(ChunkPos mcPos) + { + #if MC_VER <= MC_1_21_11 + return new DhChunkPos(mcPos.x, mcPos.z); + #else + return new DhChunkPos(mcPos.x(), mcPos.z()); + #endif + } public static #if MC_VER <= MC_1_12_2 EnumFacing #else Direction #endif Convert(EDhDirection lodDirection) { return directions[lodDirection.ordinal()]; } public static EDhDirection Convert(#if MC_VER <= MC_1_12_2 EnumFacing #else Direction #endif direction) { return lodDirections[direction.ordinal()]; } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/VersionConstants.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/VersionConstants.java index 930f4e1f7..efd96f845 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/VersionConstants.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/VersionConstants.java @@ -96,6 +96,8 @@ public class VersionConstants implements IVersionConstants return "1.21.10"; #elif MC_VER == MC_1_21_11 return "1.21.11"; + #elif MC_VER == MC_26_1_2 + return "26.1.2"; #else ERROR MC version constant missing #endif @@ -108,7 +110,7 @@ public class VersionConstants implements IVersionConstants #if MC_VER <= MC_1_21_11 return EDhApiRenderApi.OPEN_GL; #else - ERROR MC version constant missing + return EDhApiRenderApi.BLAZE_3D; #endif } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java index c314b5881..8af7cb4fd 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/WrapperFactory.java @@ -159,16 +159,19 @@ public class WrapperFactory implements IWrapperFactory public IBlockStateWrapper deserializeBlockStateWrapper(String str, ILevelWrapper levelWrapper) throws IOException { return BlockStateWrapper.deserialize(str, levelWrapper); } @Override public IBlockStateWrapper getAirBlockStateWrapper() { return BlockStateWrapper.AIR; } - + @Override + public IBlockStateWrapper getWaterBlockStateWrapper(ILevelWrapper levelWrapper) { return BlockStateWrapper.getWaterBlockStateWrapper(levelWrapper); } @Override public ObjectOpenHashSet getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredBlocks(levelWrapper); } @Override public ObjectOpenHashSet getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getRendererIgnoredCaveBlocks(levelWrapper); } + @Override + public ObjectOpenHashSet getWaterSubsurfaceReplacementBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getWaterSubsurfaceReplacementBlocks(levelWrapper); } + @Override + public ObjectOpenHashSet getWaterSurfaceReplacementBlocks(ILevelWrapper levelWrapper) { return BlockStateWrapper.getWaterSurfaceReplacementBlocks(levelWrapper); } @Override - public void resetRendererIgnoredCaveBlocks() { BlockStateWrapper.clearRendererIgnoredCaveBlocks(); } - @Override - public void resetRendererIgnoredBlocksSet() { BlockStateWrapper.clearRendererIgnoredBlocks(); } + public void resetCachedIgnoredBlocksSets() { BlockStateWrapper.clearCachedIgnoreBlocks(); } /** diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/AbstractDhTintGetter.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/AbstractDhTintGetter.java index 2bd83c088..d25b3e4f7 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/AbstractDhTintGetter.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/AbstractDhTintGetter.java @@ -6,18 +6,16 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPosMutable; -import com.seibel.distanthorizons.core.util.ColorUtil; +import com.seibel.distanthorizons.coreapi.util.ColorUtil; import com.seibel.distanthorizons.core.util.FullDataPointUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; -import it.unimi.dsi.fastutil.longs.LongArrayList; import net.minecraft.client.Minecraft; #if MC_VER <= MC_1_12_2 import net.minecraft.world.biome.Biome; #else import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.core.BlockPos; -import net.minecraft.world.level.BlockAndTintGetter; import net.minecraft.world.level.ColorResolver; import net.minecraft.world.level.biome.Biome; #endif @@ -34,6 +32,11 @@ import org.jetbrains.annotations.Nullable; import net.minecraft.core.Holder; #endif +#if MC_VER <= MC_1_21_11 +import net.minecraft.world.level.BlockAndTintGetter; +#else +import net.minecraft.client.renderer.block.BlockAndTintGetter; +#endif public abstract class AbstractDhTintGetter implements BlockAndTintGetter { @@ -47,7 +50,7 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter private static final ConcurrentHashMap COLOR_BY_BLOCK_BIOME_PAIR = new ConcurrentHashMap<>(); /** returned if the color cache is incomplete */ - public static final int INVALID_COLOR = Integer.MIN_VALUE; + public static final int INVALID_COLOR = -1; protected BiomeWrapper biomeWrapper; @@ -61,6 +64,7 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter //=============// // constructor // //=============// + //region public AbstractDhTintGetter() { } @@ -77,11 +81,14 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter this.smoothingRadiusInBlocks = Config.Client.Advanced.Graphics.Quality.lodBiomeBlending.get(); } + //endregion - //================// - // shared methods // - //================// + + //===============// + // color getters // + //===============// + //region /** Called by MC's tint getter */ @Override @@ -196,7 +203,7 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter BlockBiomeWrapperPair pair = BlockBiomeWrapperPair.get(this.blockStateWrapper, biomeWrapper); // use the cached color if possible - Integer cachedColor = COLOR_BY_BLOCK_BIOME_PAIR.get(pair); // explicit Integer return here reduces unnecessary allocations + Integer cachedColor = COLOR_BY_BLOCK_BIOME_PAIR.get(pair); if (cachedColor != null) { return cachedColor; @@ -336,6 +343,27 @@ public abstract class AbstractDhTintGetter implements BlockAndTintGetter }); } + //endregion + + + + //===========// + // set color // + //===========// + //region + + /** + * can be used in newer MC versions + * where the color getting logic is a bit more manual + */ + public static void setStaticColor(BlockStateWrapper blockStateWrapper, BiomeWrapper biomeWrapper, Integer colorInt) + { + BlockBiomeWrapperPair pair = BlockBiomeWrapperPair.get(blockStateWrapper, biomeWrapper); + COLOR_BY_BLOCK_BIOME_PAIR.put(pair, colorInt); + } + + //endregion + } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java index 89d9cd968..0d557f2df 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BiomeWrapper.java @@ -28,6 +28,7 @@ import java.util.concurrent.ConcurrentMap; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import net.minecraft.world.level.Level; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; @@ -39,28 +40,19 @@ import net.minecraft.core.Holder; import net.minecraft.core.Registry; import net.minecraft.core.RegistryAccess; import net.minecraft.data.BuiltinRegistries; -#elif MC_VER > MC_1_12_2 +#else import net.minecraft.core.Holder; import net.minecraft.core.registries.Registries; #endif -#if MC_VER > MC_1_12_2 -import net.minecraft.world.level.Level; -#endif - -#if MC_VER <= MC_1_12_2 -import net.minecraft.util.ResourceLocation; -#elif MC_VER <= MC_1_21_10 +#if MC_VER <= MC_1_21_10 import net.minecraft.resources.ResourceLocation; #else import net.minecraft.resources.Identifier; +import net.minecraft.core.component.DataComponentMap; #endif -#if MC_VER <= MC_1_12_2 -import net.minecraft.world.biome.Biome; -#else import net.minecraft.world.level.biome.Biome; -#endif #if MC_VER >= MC_1_18_2 import net.minecraft.world.level.biome.Biomes; @@ -227,33 +219,30 @@ public class BiomeWrapper implements IBiomeWrapper // generate the serial string // - #if MC_VER > MC_1_12_2 Level level = (Level)levelWrapper.getWrappedMcObject(); net.minecraft.core.RegistryAccess registryAccess = level.registryAccess(); - #endif - #if MC_VER < MC_1_21_11 + #if MC_VER <= MC_1_21_10 ResourceLocation resourceLocation; #else Identifier resourceLocation; #endif - #if MC_VER <= MC_1_12_2 - resourceLocation = this.biome.getRegistryName(); - #elif MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 + #if MC_VER <= MC_1_17_1 resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome); - #elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2 + #elif MC_VER <= MC_1_19_2 resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome.value()); - #elif MC_VER < MC_1_21_3 + #elif MC_VER <= MC_1_21_1 resourceLocation = registryAccess.registryOrThrow(Registries.BIOME).getKey(this.biome.value()); #else resourceLocation = registryAccess.lookupOrThrow(Registries.BIOME).getKey(this.biome.value()); #endif + if (resourceLocation == null) { String biomeName; - #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 || MC_VER == MC_1_12_2 + #if MC_VER <= MC_1_17_1 biomeName = this.biome.toString(); #else biomeName = this.biome.value().toString(); @@ -304,12 +293,10 @@ public class BiomeWrapper implements IBiomeWrapper { try { - #if MC_VER > MC_1_12_2 Level level = (Level) levelWrapper.getWrappedMcObject(); net.minecraft.core.RegistryAccess registryAccess = level.registryAccess(); - #endif - BiomeDeserializeResult deserializeResult = deserializeBiome(resourceLocationString #if MC_VER > MC_1_12_2, registryAccess #endif); + BiomeDeserializeResult deserializeResult = deserializeBiome(resourceLocationString, registryAccess); @@ -338,7 +325,7 @@ public class BiomeWrapper implements IBiomeWrapper } } - public static BiomeDeserializeResult deserializeBiome(String resourceLocationString #if MC_VER > MC_1_12_2, net.minecraft.core.RegistryAccess registryAccess #endif) throws IOException + public static BiomeDeserializeResult deserializeBiome(String resourceLocationString, net.minecraft.core.RegistryAccess registryAccess) throws IOException { // parse the resource location int separatorIndex = resourceLocationString.indexOf(":"); @@ -369,21 +356,18 @@ public class BiomeWrapper implements IBiomeWrapper boolean success; - #if MC_VER == MC_1_12_2 - Biome biome = Biome.REGISTRY.getObject(resourceLocation); - success = (biome != null); - #elif MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 + #if MC_VER <= MC_1_17_1 Biome biome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation); success = (biome != null); - #elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2 + #elif MC_VER <= MC_1_19_2 Biome unwrappedBiome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation); success = (unwrappedBiome != null); Holder biome = new Holder.Direct<>(unwrappedBiome); - #elif MC_VER < MC_1_21_3 + #elif MC_VER <= MC_1_21_1 Biome unwrappedBiome = registryAccess.registryOrThrow(Registries.BIOME).get(resourceLocation); success = (unwrappedBiome != null); Holder biome = new Holder.Direct<>(unwrappedBiome); - #else + #elif MC_VER <= MC_1_21_11 Holder biome; Optional> optionalBiomeHolder = registryAccess.lookupOrThrow(Registries.BIOME).get(resourceLocation); if (optionalBiomeHolder.isPresent()) @@ -397,6 +381,20 @@ public class BiomeWrapper implements IBiomeWrapper success = false; biome = null; } + #else + Holder biome; + Optional> optionalBiomeHolder = registryAccess.lookupOrThrow(Registries.BIOME).get(resourceLocation); + if (optionalBiomeHolder.isPresent()) + { + Biome unwrappedBiome = optionalBiomeHolder.get().value(); + success = (unwrappedBiome != null); + biome = new Holder.Direct<>(unwrappedBiome, DataComponentMap.EMPTY); + } + else + { + success = false; + biome = null; + } #endif return new BiomeDeserializeResult(success, biome); diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java index a1f06f2d3..f20398970 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/BlockStateWrapper.java @@ -20,33 +20,24 @@ package com.seibel.distanthorizons.common.wrappers.block; import com.seibel.distanthorizons.api.enums.rendering.EDhApiBlockMaterial; +import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBlockStateWrapperCreatedEvent; +import com.seibel.distanthorizons.common.wrappers.WrapperFactory; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.types.ConfigEntry; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.util.ColorUtil; +import com.seibel.distanthorizons.coreapi.util.ColorUtil; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet; -#if MC_VER <= MC_1_12_2 -import net.minecraft.block.Block; -import net.minecraft.block.BlockLeaves; -import net.minecraft.block.SoundType; -import net.minecraft.init.Blocks; -import net.minecraft.block.BlockLiquid; -import net.minecraft.block.state.IBlockState; -import net.minecraft.block.properties.IProperty; -import net.minecraftforge.fluids.IFluidBlock; -#else import net.minecraft.tags.BlockTags; import net.minecraft.world.level.block.BeaconBeamBlock; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.state.BlockState; -import net.minecraft.world.level.block.state.properties.Property; -#endif import com.seibel.distanthorizons.core.logging.DhLogger; import java.awt.*; @@ -56,13 +47,13 @@ import java.util.List; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Stream; -import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 import net.minecraft.core.Registry; import net.minecraft.core.BlockPos; import net.minecraft.world.level.EmptyBlockGetter; +import net.minecraft.world.level.Level; #elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2 import net.minecraft.tags.TagKey; import net.minecraft.client.Minecraft; @@ -70,7 +61,7 @@ import net.minecraft.world.level.Level; import net.minecraft.core.BlockPos; import net.minecraft.core.Registry; import net.minecraft.world.level.EmptyBlockGetter; -#elif MC_VER > MC_1_12_2 +#else import net.minecraft.tags.TagKey; import net.minecraft.world.level.Level; import net.minecraft.core.BlockPos; @@ -79,9 +70,7 @@ import net.minecraft.world.level.EmptyBlockGetter; import net.minecraft.core.Holder; #endif -#if MC_VER <= MC_1_12_2 -import net.minecraft.util.ResourceLocation; -#elif MC_VER <= MC_1_21_10 +#if MC_VER <= MC_1_21_10 import net.minecraft.resources.ResourceLocation; #else import net.minecraft.resources.Identifier; @@ -98,17 +87,20 @@ public class BlockStateWrapper implements IBlockStateWrapper // must be defined before AIR, otherwise a null pointer will be thrown private static final DhLogger LOGGER = new DhLoggerBuilder().build(); - public static final ConcurrentHashMap<#if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif, BlockStateWrapper> WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>(); - public static final ConcurrentHashMap WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>(); + public static final ConcurrentHashMap WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>(); + public static final ConcurrentHashMap WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>(); public static final String AIR_STRING = "AIR"; - public static final BlockStateWrapper AIR = new BlockStateWrapper(null, null); + public static final BlockStateWrapper AIR = new BlockStateWrapper(null, null, null); public static final String DIRT_RESOURCE_LOCATION_STRING = "minecraft:dirt"; public static final String WATER_RESOURCE_LOCATION_STRING = "minecraft:water"; public static ObjectOpenHashSet rendererIgnoredBlocks = null; public static ObjectOpenHashSet rendererIgnoredCaveBlocks = null; + public static ObjectOpenHashSet waterSubsurfaceReplacementBlocks = null; + public static ObjectOpenHashSet waterSurfaceReplacementBlocks = null; + public static IBlockStateWrapper waterBlock = null; /** keep track of broken blocks so we don't log every time */ #if MC_VER <= MC_1_21_10 @@ -122,7 +114,7 @@ public class BlockStateWrapper implements IBlockStateWrapper // properties // @Nullable - public final #if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif blockState; + public final BlockState blockState; /** technically final, but since it requires a method call to generate it can't be marked as such */ private String serialString; private final int hashCode; @@ -131,11 +123,14 @@ public class BlockStateWrapper implements IBlockStateWrapper /** used by the Iris shader mod to determine how each LOD should be rendered */ private byte blockMaterialId = 0; - private final boolean isBeaconBlock; + private final boolean isBeaconBlock; private final boolean isBeaconBaseBlock; private final boolean allowsBeaconBeamPassage; + private final boolean isSolid; + private final boolean isLiquid; + private final boolean allowApiColorOverride; /** null if this block can't tint beacons */ - private final Color beaconTintColor; + private final Color beaconTintColor; private final Color mapColor; @@ -145,41 +140,14 @@ public class BlockStateWrapper implements IBlockStateWrapper //==============// //region - public static BlockStateWrapper fromBlockState(#if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif blockState, ILevelWrapper levelWrapper) - { - if (blockState == null || #if MC_VER <= MC_1_12_2 blockState.getBlock() == Blocks.AIR #else blockState.isAir() #endif) - { - return AIR; - } - - - if (WRAPPER_BY_BLOCK_STATE.containsKey(blockState)) - { - return WRAPPER_BY_BLOCK_STATE.get(blockState); - } - else - { - BlockStateWrapper newWrapper = new BlockStateWrapper(blockState, levelWrapper); - WRAPPER_BY_BLOCK_STATE.put(blockState, newWrapper); - return newWrapper; - } - } - - #if MC_VER <= MC_1_12_2 /** - * Can be faster than {@link BlockStateWrapper#fromBlockState(IBlockState, ILevelWrapper)} + * Can be faster than {@link BlockStateWrapper#fromBlockState(BlockState, ILevelWrapper)} * in cases where the same block state is expected to be referenced multiple times. */ - #else - /** - * Can be faster than {@link BlockStateWrapper#fromBlockState(BlockState, ILevelWrapper)} - * in cases where the same block state is expected to be referenced multiple times. - */ - #endif - public static BlockStateWrapper fromBlockState(#if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif blockState, ILevelWrapper levelWrapper, IBlockStateWrapper guess) + public static BlockStateWrapper fromBlockState(BlockState blockState, ILevelWrapper levelWrapper, IBlockStateWrapper guess) { - #if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif guessBlockState = (guess == null || guess.isAir()) ? null : (#if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif) guess.getWrappedMcObject(); - #if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif inputBlockState = (blockState == null || #if MC_VER <= MC_1_12_2 blockState.getBlock() == Blocks.AIR #else blockState.isAir() #endif) ? null : blockState; + BlockState guessBlockState = (guess == null || guess.isAir()) ? null : (BlockState) guess.getWrappedMcObject(); + BlockState inputBlockState = (blockState == null || blockState.isAir()) ? null : blockState; if (guess instanceof BlockStateWrapper && guessBlockState == inputBlockState) @@ -191,144 +159,457 @@ public class BlockStateWrapper implements IBlockStateWrapper return fromBlockState(blockState, levelWrapper); } } - - private BlockStateWrapper(@Nullable #if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif blockState, ILevelWrapper levelWrapper) + public static BlockStateWrapper fromBlockState(@Nullable BlockState blockState, ILevelWrapper levelWrapper) { - this.blockState = blockState; - this.serialString = this.serialize(levelWrapper); - this.hashCode = Objects.hash(this.serialString); - this.blockMaterialId = this.calculateEDhApiBlockMaterialId().index; - this.opacity = this.calculateOpacity(); - - String lowercaseSerial = this.serialString.toLowerCase(); - - - - // beacon base blocks - #if MC_VER <= MC_1_18_2 - - // Used to handle older MC versions that don't have an simple way of getting the block's tags - List oldBeaconBaseBlockNameList = Arrays.asList( - "iron_block", - "gold_block", - "diamond_block", - "emerald_block", - "netherite_block" - ); - - // Older MC versions are harder to get block tags, so just use a static list to determine beacon blocks - boolean isBeaconBaseBlock = false; - for (int i = 0; i < oldBeaconBaseBlockNameList.size(); i++) + // air is a special case + if (isAir(blockState)) { - String baseBlockName = oldBeaconBaseBlockNameList.get(i); - if (lowercaseSerial.contains(baseBlockName)) + return AIR; + } + + // pooling wrappers significantly improves chunk->LOD processing speed + // and also reduces GC pressure + BlockStateWrapper existingWrapper = WRAPPER_BY_BLOCK_STATE.get(blockState); + if (existingWrapper != null) + { + return existingWrapper; + } + + + + // synchronized so the API event only fires once per block + synchronized (WRAPPER_BY_BLOCK_STATE) + { + // if another thread already finished this block, use that wrapper + existingWrapper = WRAPPER_BY_BLOCK_STATE.get(blockState); + if (existingWrapper != null) { - isBeaconBaseBlock = true; - break; + return existingWrapper; } - } - this.isBeaconBaseBlock = isBeaconBaseBlock; - #else - if (blockState != null) - { - // check if this block has any tags - Stream> tags = blockState.getTags(); - this.isBeaconBaseBlock = tags.anyMatch((TagKey tag) -> tag.location().getPath().toLowerCase().contains("beacon_base_blocks")); - } - else - { - this.isBeaconBaseBlock = false; - } - #endif - - // beacon block - this.isBeaconBlock = lowercaseSerial.contains("minecraft:beacon"); - - - // beacon tint color - Color beaconTintColor = null; - // 1.12.2 doesn't have block for beacon beam - #if MC_VER > MC_1_12_2 - if (this.blockState != null - // beacon blocks also show up here, but since they block the beacon beam we don't want their color - && !this.isBeaconBlock) - { - Block block = this.blockState.getBlock(); - if (block instanceof BeaconBeamBlock ) - { - int colorInt; - #if MC_VER <= MC_1_19_4 - colorInt = ((BeaconBeamBlock) block).getColor().getMaterialColor().col; - #else - colorInt = ((BeaconBeamBlock) block).getColor().getMapColor().col; - #endif - - beaconTintColor = ColorUtil.toColorObjRGB(colorInt); - } - } - #endif - this.beaconTintColor = beaconTintColor; - - - // allow/deny beacon beam passage - boolean allowsBeaconBeamPassage; - if (this.blockState != null) - { - // get block properties (defaults to the values used by air) - boolean canOcclude = this.getCanOcclude(); - boolean propagatesSkyLightDown = this.getPropagatesSkyLightDown(); - if (lowercaseSerial.contains("minecraft:bedrock")) + + // create a wrapper specifically for the API event to use + BlockStateWrapper apiWrapper = new BlockStateWrapper(blockState, levelWrapper, null); + DhApiBlockStateWrapperCreatedEvent.EventParam eventParam = new DhApiBlockStateWrapperCreatedEvent.EventParam(apiWrapper); + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBlockStateWrapperCreatedEvent.class, eventParam); + + if (!eventParam.getOverridesSet()) { - // bedrock is a special case fully opaque block that does allow beacons through - allowsBeaconBeamPassage = true; - } - else if (lowercaseSerial.contains("minecraft:tinted_glass")) - { - // tinted glass is a special case where it isn't fully opaque, - // but should block beacons - allowsBeaconBeamPassage = false; - } - else if (propagatesSkyLightDown || !canOcclude) - { - // stairs, cake, fences, etc. - allowsBeaconBeamPassage = true; + // no API changes needed, use the existing object + WRAPPER_BY_BLOCK_STATE.putIfAbsent(blockState, apiWrapper); + return apiWrapper; } else { - // non-opaque blocks (glass, mob spawners, etc.) - // all allow beacons through - allowsBeaconBeamPassage = (this.opacity != LodUtil.BLOCK_FULLY_OPAQUE); + // create a new wrapper using whatever overrides the API user set + BlockStateWrapper returnWrapper = new BlockStateWrapper(blockState, levelWrapper, eventParam); + WRAPPER_BY_BLOCK_STATE.putIfAbsent(blockState, returnWrapper); + return returnWrapper; } } - else - { - // air allows beacons through - allowsBeaconBeamPassage = true; - } - this.allowsBeaconBeamPassage = allowsBeaconBeamPassage; + } + private BlockStateWrapper( + @Nullable BlockState blockState, ILevelWrapper levelWrapper, + @Nullable DhApiBlockStateWrapperCreatedEvent.EventParam overrideEventParam) + { + this.blockState = blockState; + this.serialString = serialize(blockState, levelWrapper); + this.hashCode = Objects.hash(this.serialString); + String lowerCaseSerial = this.serialString.toLowerCase(); - int mcColor = 0; - if (this.blockState != null) + + // is liquid // { - #if MC_VER <= MC_1_12_2 - mcColor = this.blockState.getMaterial().getMaterialMapColor().colorValue; - #elif MC_VER < MC_1_20_1 - mcColor = this.blockState.getMaterial().getColor().col; + if (this.isAir() + || this.blockState == null) // == null isn't necessary since its handled in isAir() but is here to prevent intellij from complaining + { + this.isLiquid = false; + } + else + { + #if MC_VER < MC_1_20_1 + this.isLiquid = this.blockState.getMaterial().isLiquid() || !this.blockState.getFluidState().isEmpty(); #else - mcColor = this.blockState.getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).col; - #endif - this.mapColor = ColorUtil.toColorObjRGB(mcColor); - } - else - { - this.mapColor = new Color(0,0,0,0); + this.isLiquid = !this.blockState.getFluidState().isEmpty(); + #endif + } } - //LOGGER.trace("Created BlockStateWrapper ["+this.serialString+"] for ["+blockState+"] with material ID ["+this.EDhApiBlockMaterialId+"]"); + + // API overriding // + { + if (overrideEventParam != null + && overrideEventParam.getBlockMaterial() != null) + { + this.blockMaterialId = overrideEventParam.getBlockMaterial().index; + } + else + { + // no API override, use the base logic + this.blockMaterialId = calculateEDhApiBlockMaterialId(this.blockState, lowerCaseSerial, this.isLiquid).index; + } + + // allow overriding if present + if (overrideEventParam != null + && overrideEventParam.getOpacity() != null) + { + this.opacity = overrideEventParam.getOpacity(); + } + else + { + this.opacity = calculateOpacity(this.blockState, isAir(this.blockState), this.isLiquid); + } + + // allow overriding if present + if (overrideEventParam != null + && overrideEventParam.getAllowApiColorOverride() != null) + { + this.allowApiColorOverride = overrideEventParam.getAllowApiColorOverride(); + } + else + { + this.allowApiColorOverride = false; + } + } + + + // beacon handling // + { + + // beacon base blocks + #if MC_VER <= MC_1_18_2 + + // Used to handle older MC versions that don't have an simple way of getting the block's tags + List oldBeaconBaseBlockNameList = Arrays.asList( + "iron_block", + "gold_block", + "diamond_block", + "emerald_block", + "netherite_block" + ); + + // Older MC versions are harder to get block tags, so just use a static list to determine beacon blocks + boolean isBeaconBaseBlock = false; + for (int i = 0; i < oldBeaconBaseBlockNameList.size(); i++) + { + String baseBlockName = oldBeaconBaseBlockNameList.get(i); + if (lowerCaseSerial.contains(baseBlockName)) + { + isBeaconBaseBlock = true; + break; + } + } + this.isBeaconBaseBlock = isBeaconBaseBlock; + #else + if (blockState != null) + { + // check if this block has any tags + + Stream> tags; + #if MC_VER <= MC_1_21_11 + tags = blockState.getTags(); + #else + tags = blockState.tags(); + #endif + + this.isBeaconBaseBlock = tags.anyMatch((TagKey tag) -> tag.location().getPath().toLowerCase().contains("beacon_base_blocks")); + } + else + { + this.isBeaconBaseBlock = false; + } + #endif + + // beacon block + this.isBeaconBlock = lowerCaseSerial.contains("minecraft:beacon"); + + + // beacon tint color + Color beaconTintColor = null; + if (this.blockState != null + // beacon blocks also show up here, but since they block the beacon beam we don't want their color + && !this.isBeaconBlock) + { + Block block = this.blockState.getBlock(); + if (block instanceof BeaconBeamBlock) + { + int colorInt; + #if MC_VER <= MC_1_19_4 + colorInt = ((BeaconBeamBlock) block).getColor().getMaterialColor().col; + #else + colorInt = ((BeaconBeamBlock) block).getColor().getMapColor().col; + #endif + + beaconTintColor = ColorUtil.toColorObjRGB(colorInt); + } + } + this.beaconTintColor = beaconTintColor; + + + // allow/deny beacon beam passage + boolean allowsBeaconBeamPassage; + if (this.blockState != null) + { + // get block properties (defaults to the values used by air) + boolean canOcclude = getCanOcclude(this.blockState); + boolean propagatesSkyLightDown = getPropagatesSkyLightDown(this.blockState); + + if (lowerCaseSerial.contains("minecraft:bedrock")) + { + // bedrock is a special case fully opaque block that does allow beacons through + allowsBeaconBeamPassage = true; + } + else if (lowerCaseSerial.contains("minecraft:tinted_glass")) + { + // tinted glass is a special case where it isn't fully opaque, + // but should block beacons + allowsBeaconBeamPassage = false; + } + else if (propagatesSkyLightDown || !canOcclude) + { + // stairs, cake, fences, etc. + allowsBeaconBeamPassage = true; + } + else + { + // non-opaque blocks (glass, mob spawners, etc.) + // all allow beacons through + allowsBeaconBeamPassage = (this.opacity != LodUtil.BLOCK_FULLY_OPAQUE); + } + } + else + { + // air allows beacons through + allowsBeaconBeamPassage = true; + } + this.allowsBeaconBeamPassage = allowsBeaconBeamPassage; + } + + + // map color // + { + if (this.blockState != null) + { + int mcColor = 0; + + #if MC_VER < MC_1_20_1 + mcColor = this.blockState.getMaterial().getColor().col; + #else + mcColor = this.blockState.getMapColor(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).col; + #endif + + this.mapColor = ColorUtil.toColorObjRGB(mcColor); + } + else + { + this.mapColor = new Color(0, 0, 0, 0); + } + } + + + // is solid // + { + if (this.isAir() + || this.blockState == null) // "== null" isn't necessary since its handled in isAir() but is here to prevent IntelliJ from complaining + { + this.isSolid = false; + } + else + { + #if MC_VER < MC_1_20_1 + this.isSolid = this.blockState.getMaterial().isSolid(); + #else + this.isSolid = !this.blockState.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).isEmpty(); + #endif + } + } + + } + // static constructor helpers // + //region + + private static EDhApiBlockMaterial calculateEDhApiBlockMaterialId( + @Nullable BlockState blockState, + String lowercaseSerialString, + boolean isLiquid + ) + { + if (blockState == null) + { + return EDhApiBlockMaterial.AIR; + } + + + if (blockState.is(BlockTags.LEAVES) + || lowercaseSerialString.contains("bamboo") + || lowercaseSerialString.contains("cactus") + || lowercaseSerialString.contains("chorus_flower") + || lowercaseSerialString.contains("mushroom") + ) + { + return EDhApiBlockMaterial.LEAVES; + } + else if (blockState.is(Blocks.LAVA)) + { + return EDhApiBlockMaterial.LAVA; + } + else if (isLiquid + || blockState.is(Blocks.WATER)) + { + return EDhApiBlockMaterial.WATER; + } + else if (blockState.getSoundType() == SoundType.WOOD + || lowercaseSerialString.contains("root") + #if MC_VER >= MC_1_19_4 + || blockState.getSoundType() == SoundType.CHERRY_WOOD + #endif + ) + { + return EDhApiBlockMaterial.WOOD; + } + else if (blockState.getSoundType() == SoundType.METAL + #if MC_VER >= MC_1_19_2 + || blockState.getSoundType() == SoundType.COPPER + #endif + #if MC_VER >= MC_1_20_4 + || blockState.getSoundType() == SoundType.COPPER_BULB + || blockState.getSoundType() == SoundType.COPPER_GRATE + #endif + ) + { + return EDhApiBlockMaterial.METAL; + } + else if ( + lowercaseSerialString.contains("grass_block") + || lowercaseSerialString.contains("grass_slab") + ) + { + return EDhApiBlockMaterial.GRASS; + } + else if ( + lowercaseSerialString.contains("dirt") + || lowercaseSerialString.contains("gravel") + || lowercaseSerialString.contains("mud") + || lowercaseSerialString.contains("podzol") + || lowercaseSerialString.contains("mycelium") + ) + { + return EDhApiBlockMaterial.DIRT; + } + #if MC_VER >= MC_1_17_1 + else if (blockState.getSoundType() == SoundType.DEEPSLATE + || blockState.getSoundType() == SoundType.DEEPSLATE_BRICKS + || blockState.getSoundType() == SoundType.DEEPSLATE_TILES + || blockState.getSoundType() == SoundType.POLISHED_DEEPSLATE + || lowercaseSerialString.contains("deepslate") ) + { + return EDhApiBlockMaterial.DEEPSLATE; + } + #endif + else if (lowercaseSerialString.contains("snow")) + { + return EDhApiBlockMaterial.SNOW; + } + else if (lowercaseSerialString.contains("sand")) + { + return EDhApiBlockMaterial.SAND; + } + else if (lowercaseSerialString.contains("terracotta")) + { + return EDhApiBlockMaterial.TERRACOTTA; + } + else if (blockState.is(BlockTags.BASE_STONE_NETHER)) + { + return EDhApiBlockMaterial.NETHER_STONE; + } + else if (lowercaseSerialString.contains("stone") + || lowercaseSerialString.contains("ore")) + { + return EDhApiBlockMaterial.STONE; + } + else if (blockState.getLightEmission() > 0) + { + return EDhApiBlockMaterial.ILLUMINATED; + } + else + { + return EDhApiBlockMaterial.UNKNOWN; + } + } + + private static int calculateOpacity( + @Nullable BlockState blockState, + boolean isAir, boolean isLiquid + ) + { + // get block properties (defaults to the values used by air) + boolean canOcclude = getCanOcclude(blockState); + boolean propagatesSkyLightDown = getPropagatesSkyLightDown(blockState); + + + + // this method isn't perfect, but works well enough for our use case + int opacity; + if (isAir) + { + opacity = LodUtil.BLOCK_FULLY_TRANSPARENT; + } + else if (isLiquid && !canOcclude) + { + // probably not a waterlogged block (which should block light entirely) + + // +1 to indicate that the block is translucent (in between transparent and opaque) + opacity = LodUtil.BLOCK_FULLY_TRANSPARENT + 1; + } + else if (propagatesSkyLightDown && !canOcclude) + { + // probably glass or some other fully transparent block + + // !canOcclude is required to ignore stairs and slabs since + // propagateSkyLightDown is true for them, but they're solid and don't actually let light through + + opacity = LodUtil.BLOCK_FULLY_TRANSPARENT; + } + else + { + // default for all other blocks + opacity = LodUtil.BLOCK_FULLY_OPAQUE; + } + + + return opacity; + } + private static boolean getCanOcclude(@Nullable BlockState blockState) + { + // defaults to the value used by air + boolean canOcclude = false; + if (blockState != null) + { + canOcclude = blockState.canOcclude(); + } + + return canOcclude; + } + private static boolean getPropagatesSkyLightDown(@Nullable BlockState blockState) + { + // defaults to the value used by air + boolean propagatesSkyLightDown = true; + if (blockState != null) + { + #if MC_VER < MC_1_21_3 + propagatesSkyLightDown = blockState.propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO); + #else + propagatesSkyLightDown = blockState.propagatesSkylightDown(); + #endif + } + + return propagatesSkyLightDown; + } + + //endregion //endregion @@ -339,9 +620,11 @@ public class BlockStateWrapper implements IBlockStateWrapper //region /** - * Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one. + * Each of the following methods require + * a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one. * This way the method won't accidentally be called before the deserialization can be completed. */ + public static ObjectOpenHashSet getRendererIgnoredBlocks(ILevelWrapper levelWrapper) { // use the cached version if possible @@ -355,10 +638,6 @@ public class BlockStateWrapper implements IBlockStateWrapper rendererIgnoredBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderBlockCsv, baseIgnoredBlock, levelWrapper); return rendererIgnoredBlocks; } - /** - * Requires a {@link ILevelWrapper} since {@link BlockStateWrapper#deserialize(String,ILevelWrapper)} also requires one. - * This way the method won't accidentally be called before the deserialization can be completed. - */ public static ObjectOpenHashSet getRendererIgnoredCaveBlocks(ILevelWrapper levelWrapper) { // use the cached version if possible @@ -372,9 +651,41 @@ public class BlockStateWrapper implements IBlockStateWrapper rendererIgnoredCaveBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderCaveBlockCsv, baseIgnoredBlock, levelWrapper); return rendererIgnoredCaveBlocks; } - - public static void clearRendererIgnoredBlocks() { rendererIgnoredBlocks = null; } - public static void clearRendererIgnoredCaveBlocks() { rendererIgnoredCaveBlocks = null; } + public static ObjectOpenHashSet getWaterSurfaceReplacementBlocks(ILevelWrapper levelWrapper) + { + // use the cached version if possible + if (waterSurfaceReplacementBlocks != null) + { + return waterSurfaceReplacementBlocks; + } + + ObjectOpenHashSet baseIgnoredBlock = new ObjectOpenHashSet<>(); + waterSurfaceReplacementBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.waterSurfaceBlockReplacementCsv, baseIgnoredBlock, levelWrapper); + return waterSurfaceReplacementBlocks; + } + public static ObjectOpenHashSet getWaterSubsurfaceReplacementBlocks(ILevelWrapper levelWrapper) + { + // use the cached version if possible + if (waterSubsurfaceReplacementBlocks != null) + { + return waterSubsurfaceReplacementBlocks; + } + + ObjectOpenHashSet baseIgnoredBlock = new ObjectOpenHashSet<>(); + waterSubsurfaceReplacementBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.waterSubSurfaceBlockReplacementCsv, baseIgnoredBlock, levelWrapper); + return waterSubsurfaceReplacementBlocks; + } + public static IBlockStateWrapper getWaterBlockStateWrapper(ILevelWrapper levelWrapper) + { + // use the cached version if possible + if (waterBlock != null) + { + return waterBlock; + } + + waterBlock = WrapperFactory.INSTANCE.deserializeBlockStateWrapperOrGetDefault("minecraft:water", levelWrapper); + return waterBlock; + } //endregion @@ -391,7 +702,7 @@ public class BlockStateWrapper implements IBlockStateWrapper ObjectOpenHashSet blockStringList = new ObjectOpenHashSet<>(); if (baseResourceLocations != null) { - blockStringList.addAll(baseResourceLocations); + blockStringList.addAll(baseResourceLocations); } // get the config blocks @@ -429,14 +740,10 @@ public class BlockStateWrapper implements IBlockStateWrapper if (defaultBlockStateToIgnore != AIR) { // add all possible blockstates (to account for light blocks with different light values and such) - #if MC_VER <= MC_1_12_2 - List blockStatesToIgnore = defaultBlockStateToIgnore.blockState.getBlock().getBlockState().getValidStates(); - #else List blockStatesToIgnore = defaultBlockStateToIgnore.blockState.getBlock().getStateDefinition().getPossibleStates(); - #endif - for (#if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif blockState : blockStatesToIgnore) + for (BlockState blockState : blockStatesToIgnore) { - BlockStateWrapper newBlockToIgnore = BlockStateWrapper.fromBlockState(blockState, levelWrapper); + BlockStateWrapper newBlockToIgnore = fromBlockState(blockState, levelWrapper); blockStateWrappers.add(newBlockToIgnore); } } @@ -459,6 +766,15 @@ public class BlockStateWrapper implements IBlockStateWrapper return blockStateWrappers; } + public static void clearCachedIgnoreBlocks() + { + rendererIgnoredBlocks = null; + rendererIgnoredCaveBlocks = null; + waterSurfaceReplacementBlocks = null; + waterSubsurfaceReplacementBlocks = null; + waterBlock = null; + } + //endregion @@ -470,160 +786,24 @@ public class BlockStateWrapper implements IBlockStateWrapper @Override public int getOpacity() { return this.opacity; } - private int calculateOpacity() - { - // get block properties (defaults to the values used by air) - boolean canOcclude = this.getCanOcclude(); - boolean propagatesSkyLightDown = this.getPropagatesSkyLightDown(); - - - - // this method isn't perfect, but works well enough for our use case - int opacity; - if (this.isAir()) - { - opacity = LodUtil.BLOCK_FULLY_TRANSPARENT; - } - else if (this.isLiquid() && !canOcclude) - { - // probably not a waterlogged block (which should block light entirely) - - // +1 to indicate that the block is translucent (in between transparent and opaque) - opacity = LodUtil.BLOCK_FULLY_TRANSPARENT + 1; - } - else if (propagatesSkyLightDown && !canOcclude) - { - // probably glass or some other fully transparent block - - // !canOcclude is required to ignore stairs and slabs since - // propagateSkyLightDown is true for them, but they're solid and don't actually let light through - - opacity = LodUtil.BLOCK_FULLY_TRANSPARENT; - } - else - { - // default for all other blocks - opacity = LodUtil.BLOCK_FULLY_OPAQUE; - } - - - return opacity; - } - private boolean getCanOcclude() - { - // defaults to the value used by air - boolean canOcclude = false; - if (this.blockState != null) - { - #if MC_VER <= MC_1_12_2 - canOcclude = this.blockState.isOpaqueCube(); - #else - canOcclude = this.blockState.canOcclude(); - #endif - } - - return canOcclude; - } - private boolean getPropagatesSkyLightDown() - { - // defaults to the value used by air - boolean propagatesSkyLightDown = true; - if (this.blockState != null) - { - #if MC_VER <= MC_1_12_2 - propagatesSkyLightDown = !this.blockState.isOpaqueCube() && !(this.blockState.getBlock() instanceof BlockLiquid); - #elif MC_VER < MC_1_21_3 - propagatesSkyLightDown = this.blockState.propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO); - #else - propagatesSkyLightDown = this.blockState.propagatesSkylightDown(); - #endif - } - - return propagatesSkyLightDown; - } - - - @Override - public int getLightEmission() { return (this.blockState != null) ? #if MC_VER <= MC_1_12_2 this.blockState.getLightValue() #else this.blockState.getLightEmission() #endif : 0; } + public int getLightEmission() { return (this.blockState != null) ? this.blockState.getLightEmission() : 0; } @Override public String getSerialString() { return this.serialString; } - @Override - public boolean equals(Object obj) - { - if (this == obj) - { - return true; - } - - if (obj == null || this.getClass() != obj.getClass()) - { - return false; - } - - BlockStateWrapper that = (BlockStateWrapper) obj; - // the serialized value is used so we can test the contents instead of the references - return Objects.equals(this.getSerialString(), that.getSerialString()); - } - - @Override - public int hashCode() { return this.hashCode; } - - @Override public Object getWrappedMcObject() { return this.blockState; } @Override - public boolean isAir() { return this.isAir(this.blockState); } - public boolean isAir(#if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif blockState) { return blockState == null || #if MC_VER <= MC_1_12_2 blockState.getBlock() == Blocks.AIR #else blockState.isAir() #endif; } - - private Boolean blockIsSolid = null; - @Override - public boolean isSolid() - { - if (this.isAir() - || this.blockState == null) // == null isn't necessary since its handled in isAir() but is here to prevent intellij from complaining - { - return false; - } - - // cached since getCollisionShape() is a dictionary lookup that allocates objects - // and this call is used in a high traffic location - if (this.blockIsSolid != null) - { - return this.blockIsSolid; - } - - - #if MC_VER < MC_1_20_1 - this.blockIsSolid = this.blockState.getMaterial().isSolid(); - #else - this.blockIsSolid = !this.blockState.getCollisionShape(EmptyBlockGetter.INSTANCE, BlockPos.ZERO).isEmpty(); - #endif - return this.blockIsSolid; - } + public boolean isAir() { return isAir(this.blockState); } + public static boolean isAir(BlockState blockState) { return blockState == null || blockState.isAir(); } @Override - public boolean isLiquid() - { - if (this.isAir() - || this.blockState == null) // == null isn't necessary since its handled in isAir() but is here to prevent intellij from complaining - { - return false; - } - - #if MC_VER <= MC_1_12_2 - return this.blockState.getMaterial().isLiquid() || this.blockState.getBlock() instanceof IFluidBlock; - #elif MC_VER < MC_1_20_1 - return this.blockState.getMaterial().isLiquid() || !this.blockState.getFluidState().isEmpty(); - #else - return !this.blockState.getFluidState().isEmpty(); - #endif - } - + public boolean isSolid() { return this.isSolid; } + @Override + public boolean isLiquid() { return this.isLiquid; } @Override public boolean isBeaconBlock() { return this.isBeaconBlock; } @Override @@ -632,6 +812,8 @@ public class BlockStateWrapper implements IBlockStateWrapper public boolean isBeaconTintBlock() { return this.beaconTintColor != null; } @Override public boolean allowsBeaconBeamPassage() { return this.allowsBeaconBeamPassage; } + @Override + public boolean allowApiColorOverride() { return this.allowApiColorOverride; } @Override public Color getMapColor() { return this.mapColor; } @@ -641,9 +823,6 @@ public class BlockStateWrapper implements IBlockStateWrapper @Override public byte getMaterialId() { return this.blockMaterialId; } - @Override - public String toString() { return this.getSerialString(); } - //endregion @@ -653,9 +832,9 @@ public class BlockStateWrapper implements IBlockStateWrapper //=======================// //region - private String serialize(ILevelWrapper levelWrapper) + private static String serialize(BlockState blockState, ILevelWrapper levelWrapper) { - if (this.blockState == null) + if (blockState == null) { return AIR_STRING; } @@ -663,41 +842,39 @@ public class BlockStateWrapper implements IBlockStateWrapper // older versions of MC have a static registry - #if MC_VER > MC_1_17_1 + #if MC_VER <= MC_1_16_5 + #else Level level = (Level)levelWrapper.getWrappedMcObject(); net.minecraft.core.RegistryAccess registryAccess = level.registryAccess(); #endif - #if MC_VER < MC_1_21_11 + #if MC_VER <= MC_1_21_10 ResourceLocation resourceLocation; #else Identifier resourceLocation; #endif - #if MC_VER <= MC_1_12_2 - resourceLocation = this.blockState.getBlock().getRegistryName(); - #elif MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 - resourceLocation = Registry.BLOCK.getKey(this.blockState.getBlock()); - #elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2 - resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(this.blockState.getBlock()); - #elif MC_VER < MC_1_21_3 - resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock()); + #if MC_VER <= MC_1_17_1 + resourceLocation = Registry.BLOCK.getKey(blockState.getBlock()); + #elif MC_VER <= MC_1_19_2 + resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(blockState.getBlock()); + #elif MC_VER <= MC_1_21_1 + resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(blockState.getBlock()); #else - resourceLocation = registryAccess.lookupOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock()); + resourceLocation = registryAccess.lookupOrThrow(Registries.BLOCK).getKey(blockState.getBlock()); #endif if (resourceLocation == null) { - LOGGER.warn("No ResourceLocation found, unable to serialize: " + this.blockState); + LOGGER.warn("No ResourceLocation found, unable to serialize: " + blockState); return AIR_STRING; } - this.serialString = resourceLocation.getNamespace() + RESOURCE_LOCATION_SEPARATOR + resourceLocation.getPath() - + STATE_STRING_SEPARATOR + serializeBlockStateProperties(this.blockState); - - return this.serialString; + String serialString = resourceLocation.getNamespace() + RESOURCE_LOCATION_SEPARATOR + resourceLocation.getPath() + + STATE_STRING_SEPARATOR + serializeBlockStateProperties(blockState); + return serialString; } @@ -707,7 +884,7 @@ public class BlockStateWrapper implements IBlockStateWrapper // we need the final string for the concurrent hash map later final String finalResourceStateString = resourceStateString; - if (finalResourceStateString.equals(AIR_STRING) + if (finalResourceStateString.equals(AIR_STRING) || finalResourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case { return AIR; @@ -769,20 +946,19 @@ public class BlockStateWrapper implements IBlockStateWrapper try { - #if MC_VER > MC_1_17_1 + #if MC_VER <= MC_1_16_5 + #else LodUtil.assertTrue(levelWrapper != null && levelWrapper.getWrappedMcObject() != null); Level level = (Level)levelWrapper.getWrappedMcObject(); #endif Block block; - #if MC_VER <= MC_1_12_2 - block = Block.REGISTRY.getObject(resourceLocation); - #elif MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 + #if MC_VER <= MC_1_17_1 block = Registry.BLOCK.get(resourceLocation); - #elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2 + #elif MC_VER <= MC_1_19_2 net.minecraft.core.RegistryAccess registryAccess = level.registryAccess(); block = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).get(resourceLocation); - #elif MC_VER < MC_1_21_3 + #elif MC_VER <= MC_1_21_1 net.minecraft.core.RegistryAccess registryAccess = level.registryAccess(); block = registryAccess.registryOrThrow(Registries.BLOCK).get(resourceLocation); #else @@ -806,15 +982,11 @@ public class BlockStateWrapper implements IBlockStateWrapper // attempt to find the blockstate from all possibilities - #if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif foundState = null; + BlockState foundState = null; if (blockStatePropertiesString != null) { - #if MC_VER <= MC_1_12_2 - List possibleStateList = block.getBlockState().getValidStates(); - #else List possibleStateList = block.getStateDefinition().getPossibleStates(); - #endif - for (#if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif possibleState : possibleStateList) + for (BlockState possibleState : possibleStateList) { String possibleStatePropertiesString = serializeBlockStateProperties(possibleState); if (possibleStatePropertiesString.equals(blockStatePropertiesString)) @@ -838,10 +1010,10 @@ public class BlockStateWrapper implements IBlockStateWrapper } } - foundState = #if MC_VER <= MC_1_12_2 block.getDefaultState() #else block.defaultBlockState() #endif; + foundState = block.defaultBlockState(); } - foundWrapper = new BlockStateWrapper(foundState, levelWrapper); + foundWrapper = fromBlockState(foundState, levelWrapper); return foundWrapper; } catch (Exception e) @@ -854,40 +1026,35 @@ public class BlockStateWrapper implements IBlockStateWrapper // put if absent in case two threads deserialize at the same time // unfortunately we can't put everything in a computeIfAbsent() since we also throw exceptions WRAPPER_BY_RESOURCE_LOCATION.putIfAbsent(finalResourceStateString, foundWrapper); + + if (foundWrapper != AIR) + { + WRAPPER_BY_BLOCK_STATE.putIfAbsent(foundWrapper.blockState, foundWrapper); + } } } /** used to compare and save BlockStates based on their properties */ - private static String serializeBlockStateProperties(#if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif blockState) + private static String serializeBlockStateProperties(BlockState blockState) { // get the property list for this block (doesn't contain this block state's values, just the names and possible values) - #if MC_VER <= MC_1_12_2 - java.util.Collection> blockPropertyCollection = blockState.getPropertyKeys(); - #else - java.util.Collection> blockPropertyCollection = blockState.getProperties();; - #endif + java.util.Collection> blockPropertyCollection = blockState.getProperties(); // alphabetically sort the list so they are always in the same order - - List<#if MC_VER <= MC_1_12_2 IProperty #else Property #endif> sortedBlockPropteryList = new ArrayList<>(blockPropertyCollection); - + List> sortedBlockPropteryList = new ArrayList<>(blockPropertyCollection); sortedBlockPropteryList.sort((a, b) -> a.getName().compareTo(b.getName())); + StringBuilder stringBuilder = new StringBuilder(); - for (#if MC_VER <= MC_1_12_2 IProperty #else Property #endif property : sortedBlockPropteryList) + for (net.minecraft.world.level.block.state.properties.Property property : sortedBlockPropteryList) { String propertyName = property.getName(); String value = "NULL"; - - #if MC_VER <= MC_1_12_2 - value = blockState.getValue(property).toString(); - #else if (blockState.hasProperty(property)) { value = blockState.getValue(property).toString(); } - #endif stringBuilder.append("{"); stringBuilder.append(propertyName).append(RESOURCE_LOCATION_SEPARATOR).append(value); @@ -901,117 +1068,37 @@ public class BlockStateWrapper implements IBlockStateWrapper - //==============// - // Iris methods // - //==============// + //================// + // base overrides // + //================// //region - private EDhApiBlockMaterial calculateEDhApiBlockMaterialId() + @Override + public boolean equals(Object obj) { - if (this.blockState == null) + if (this == obj) { - return EDhApiBlockMaterial.AIR; + return true; } + if (obj == null || this.getClass() != obj.getClass()) + { + return false; + } - String serialString = this.getSerialString().toLowerCase(); - if (#if MC_VER <= MC_1_12_2 this.blockState.getBlock() instanceof BlockLeaves #else this.blockState.is(BlockTags.LEAVES) #endif - || serialString.contains("bamboo") - || serialString.contains("cactus") - || serialString.contains("chorus_flower") - || serialString.contains("mushroom") - ) - { - return EDhApiBlockMaterial.LEAVES; - } - else if (#if MC_VER <= MC_1_12_2 this.blockState.getBlock() == Blocks.LAVA || this.blockState.getBlock() == Blocks.FLOWING_LAVA #else this.blockState.is(Blocks.LAVA) #endif) - { - return EDhApiBlockMaterial.LAVA; - } - else if (this.isLiquid() || #if MC_VER <= MC_1_12_2 this.blockState.getBlock() == Blocks.WATER || this.blockState.getBlock() == Blocks.FLOWING_WATER #else this.blockState.is(Blocks.WATER) #endif) - { - return EDhApiBlockMaterial.WATER; - } - else if (#if MC_VER <= MC_1_12_2 this.blockState.getBlock().getSoundType() #else this.blockState.getSoundType() #endif == SoundType.WOOD - || serialString.contains("root") - #if MC_VER >= MC_1_19_4 - || this.blockState.getSoundType() == SoundType.CHERRY_WOOD - #endif - ) - { - return EDhApiBlockMaterial.WOOD; - } - else if (#if MC_VER <= MC_1_12_2 this.blockState.getBlock().getSoundType() #else this.blockState.getSoundType() #endif == SoundType.METAL - #if MC_VER >= MC_1_19_2 - || this.blockState.getSoundType() == SoundType.COPPER - #endif - #if MC_VER >= MC_1_20_4 - || this.blockState.getSoundType() == SoundType.COPPER_BULB - || this.blockState.getSoundType() == SoundType.COPPER_GRATE - #endif - ) - { - return EDhApiBlockMaterial.METAL; - } - else if ( - serialString.contains("grass_block") - || serialString.contains("grass_slab") - ) - { - return EDhApiBlockMaterial.GRASS; - } - else if ( - serialString.contains("dirt") - || serialString.contains("gravel") - || serialString.contains("mud") - || serialString.contains("podzol") - || serialString.contains("mycelium") - ) - { - return EDhApiBlockMaterial.DIRT; - } - #if MC_VER >= MC_1_17_1 - else if (this.blockState.getSoundType() == SoundType.DEEPSLATE - || this.blockState.getSoundType() == SoundType.DEEPSLATE_BRICKS - || this.blockState.getSoundType() == SoundType.DEEPSLATE_TILES - || this.blockState.getSoundType() == SoundType.POLISHED_DEEPSLATE - || serialString.contains("deepslate") ) - { - return EDhApiBlockMaterial.DEEPSLATE; - } - #endif - else if (this.serialString.contains("snow")) - { - return EDhApiBlockMaterial.SNOW; - } - else if (serialString.contains("sand")) - { - return EDhApiBlockMaterial.SAND; - } - else if (serialString.contains("terracotta")) - { - return EDhApiBlockMaterial.TERRACOTTA; - } - else if (#if MC_VER <= MC_1_12_2 this.blockState.getBlock() == Blocks.NETHERRACK #else this.blockState.is(BlockTags.BASE_STONE_NETHER) #endif) - { - return EDhApiBlockMaterial.NETHER_STONE; - } - else if (serialString.contains("stone") - || serialString.contains("ore")) - { - return EDhApiBlockMaterial.STONE; - } - else if (#if MC_VER <= MC_1_12_2 this.blockState.getLightValue() #else this.blockState.getLightEmission() #endif > 0) - { - return EDhApiBlockMaterial.ILLUMINATED; - } - else - { - return EDhApiBlockMaterial.UNKNOWN; - } + BlockStateWrapper that = (BlockStateWrapper) obj; + // the serialized value is used so we can test the contents instead of the references + return Objects.equals(this.getSerialString(), that.getSerialString()); } + @Override + public int hashCode() { return this.hashCode; } + + @Override + public String toString() { return this.getSerialString(); } + //endregion + } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/ClientBlockStateColorCache.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/ClientBlockStateColorCache.java index 9211811a0..20a42bb75 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/ClientBlockStateColorCache.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/ClientBlockStateColorCache.java @@ -19,62 +19,49 @@ package com.seibel.distanthorizons.common.wrappers.block; +import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper; +import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; +import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBlockColorOverrideEvent; import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPosMutable; -import com.seibel.distanthorizons.core.util.ColorUtil; +import com.seibel.distanthorizons.coreapi.util.ColorUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; - +import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.texture.TextureAtlasSprite; -import net.minecraft.client.renderer.block.model.BakedQuad; -#if MC_VER <= MC_1_12_2 -import net.minecraft.block.state.IBlockState; -import net.minecraft.init.Blocks; -import net.minecraft.util.EnumFacing; -import net.minecraft.block.BlockRotatedPillar; -import net.minecraft.block.*; -import net.minecraft.client.multiplayer.WorldClient; -import net.minecraft.client.renderer.color.BlockColors; -import net.minecraft.util.math.BlockPos; -#else import net.minecraft.core.Direction; import net.minecraft.world.level.block.*; import net.minecraft.world.level.block.state.BlockState; +import com.seibel.distanthorizons.core.logging.DhLogger; import net.minecraft.world.level.block.state.properties.SlabType; -#endif +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + #if MC_VER >= MC_1_19_2 import net.minecraft.util.RandomSource; #else import java.util.Random; #endif -import java.util.Collections; -import java.util.HashSet; -import java.util.List; -import java.util.ArrayList; - -import com.seibel.distanthorizons.core.logging.DhLogger; -import org.jetbrains.annotations.Nullable; - -import java.util.concurrent.locks.ReentrantLock; - #if MC_VER < MC_1_21_5 -#else +import net.minecraft.client.renderer.block.model.BakedQuad; +#elif MC_VER <= MC_1_21_11 import net.minecraft.client.renderer.block.model.BlockModelPart; +import net.minecraft.client.renderer.block.model.BakedQuad; +#else +import net.minecraft.client.renderer.block.dispatch.BlockStateModelPart; +import net.minecraft.client.resources.model.geometry.BakedQuad; +import net.minecraft.core.BlockPos; +import net.minecraft.client.color.block.BlockTintSource; #endif -#if MC_VER <= MC_1_12_2 -/** - * This stores and calculates the colors - * the given {@link IBlockState} should have based - * on the given {@link IClientLevelWrapper}. - * - * @see ColorUtil - */ -#else /** * This stores and calculates the colors * the given {@link BlockState} should have based @@ -82,15 +69,14 @@ import net.minecraft.client.renderer.block.model.BlockModelPart; * * @see ColorUtil */ -#endif public class ClientBlockStateColorCache { private static final DhLogger LOGGER = new DhLoggerBuilder().build(); - private static final Minecraft MC = Minecraft.#if MC_VER <= MC_1_12_2 getMinecraft() #else getInstance() #endif; + private static final Minecraft MC = Minecraft.getInstance(); - private static final HashSet<#if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>(); - private static final HashSet<#if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif> BROKEN_BLOCK_STATES = new HashSet<>(); + private static final HashSet BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>(); + private static final HashSet BROKEN_BLOCK_STATES = new HashSet<>(); /** * Methods using MC's "RandomSource" object aren't thread safe
@@ -104,15 +90,15 @@ public class ClientBlockStateColorCache /** This is the order each direction on a block is processed when attempting to get the texture/color */ - private static final @Nullable #if MC_VER <= MC_1_12_2 EnumFacing #else Direction #endif[] COLOR_RESOLUTION_DIRECTION_ORDER = - { - #if MC_VER <= MC_1_12_2 EnumFacing #else Direction #endif.UP, + private static final @Nullable Direction[] COLOR_RESOLUTION_DIRECTION_ORDER = + { + Direction.UP, null, // null represents "unculled" faces, IE the top of farmland - #if MC_VER <= MC_1_12_2 EnumFacing #else Direction #endif.NORTH, - #if MC_VER <= MC_1_12_2 EnumFacing #else Direction #endif.EAST, - #if MC_VER <= MC_1_12_2 EnumFacing #else Direction #endif.WEST, - #if MC_VER <= MC_1_12_2 EnumFacing #else Direction #endif.SOUTH, - #if MC_VER <= MC_1_12_2 EnumFacing #else Direction #endif.DOWN + Direction.NORTH, + Direction.EAST, + Direction.WEST, + Direction.SOUTH, + Direction.DOWN }; private static final int FLOWER_COLOR_SCALE = 5; @@ -127,7 +113,7 @@ public class ClientBlockStateColorCache #endif private final IClientLevelWrapper clientLevelWrapper; - private final #if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif blockState; + private final BlockState blockState; private final BlockStateWrapper blockStateWrapper; private boolean isColorResolved = false; @@ -140,6 +126,7 @@ public class ClientBlockStateColorCache //===========// // constants // //===========// + //region private static final int MIN_SRGB_BITS = 0x39000000; // 2^(-13) private static final int MAX_SRGB_BITS = 0x3f7fffff; // 1.0 - f32::EPSILON @@ -148,6 +135,7 @@ public class ClientBlockStateColorCache private static final int[] linearToSrgbTable = new int[] { + //region 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, @@ -161,9 +149,11 @@ public class ClientBlockStateColorCache 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, + //endregion }; private static final float[] srgbToLinearTable = new float[] + //region { 0.0f, 0.000303527f, 0.000607054f, 0.00091058103f, 0.001214108f, 0.001517635f, 0.0018211621f, 0.002124689f, 0.002428216f, 0.002731743f, 0.00303527f, 0.0033465356f, 0.003676507f, 0.004024717f, 0.004391442f, @@ -197,20 +187,24 @@ public class ClientBlockStateColorCache 0.78353804f, 0.79129815f, 0.79910296f, 0.8069525f, 0.8148468f, 0.822786f, 0.8307701f, 0.83879924f, 0.84687346f, 0.8549928f, 0.8631574f, 0.87136734f, 0.8796226f, 0.8879232f, 0.89626956f, 0.90466136f, 0.913099f, 0.92158204f, 0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f + //endregion }; - #if MC_VER > MC_1_12_2 - private static final ThreadLocal TintWithoutLevelOverrideGetter = ThreadLocal.withInitial(() -> new TintWithoutLevelOverrider()); - private static final ThreadLocal TintOverrideGetter = ThreadLocal.withInitial(() -> new TintGetterOverride()); - #endif + // these are threadlocals since AbstractDhTintGetter use local variables to handle color queries + private static final ThreadLocal TintWithoutLevelOverrideGetter = ThreadLocal.withInitial(TintWithoutLevelOverrider::new); + private static final ThreadLocal TintOverrideGetter = ThreadLocal.withInitial(TintGetterOverride::new); + private static final ThreadLocal ColorOverrideEventParamGetter = ThreadLocal.withInitial(DhApiBlockColorOverrideEvent.EventParam::new); + + //endregion //=============// // constructor // //=============// + //region - public ClientBlockStateColorCache(#if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif blockState, IClientLevelWrapper clientLevelWrapper) + public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper clientLevelWrapper) { this.blockState = blockState; this.blockStateWrapper = BlockStateWrapper.fromBlockState(blockState, clientLevelWrapper); @@ -219,6 +213,8 @@ public class ClientBlockStateColorCache this.resolveColors(); } + //endregion + //===================// @@ -237,17 +233,17 @@ public class ClientBlockStateColorCache // getQuads() isn't thread safe so we need to put this logic in a lock RESOLVE_LOCK.lock(); - if (#if MC_VER <= MC_1_12_2 !this.blockState.getMaterial().isLiquid() #else this.blockState.getFluidState().isEmpty() #endif) + if (this.blockState.getFluidState().isEmpty()) { // look for the first non-empty direction List quads = null; - for (#if MC_VER <= MC_1_12_2 EnumFacing #else Direction #endif direction : COLOR_RESOLUTION_DIRECTION_ORDER) + for (Direction direction : COLOR_RESOLUTION_DIRECTION_ORDER) { quads = this.getQuadsForDirection(direction); if (quads != null && !quads.isEmpty() && !( - this.blockState.getBlock() instanceof #if MC_VER <= MC_1_12_2 BlockRotatedPillar #else RotatedPillarBlock #endif - && direction == #if MC_VER <= MC_1_12_2 EnumFacing #else Direction #endif.UP + this.blockState.getBlock() instanceof RotatedPillarBlock + && direction == Direction.UP ) ) { @@ -264,28 +260,55 @@ public class ClientBlockStateColorCache && !quads.isEmpty() && quads.get(0) != null) { - BakedQuad firstQuad = quads.get(0); + try + { + BakedQuad firstQuad = quads.get(0); - this.needPostTinting = firstQuad.#if MC_VER <= MC_1_12_2 hasTintIndex() #else isTinted() #endif; - #if MC_VER <= MC_1_21_4 - this.tintIndex = firstQuad.getTintIndex(); - #else - this.tintIndex = firstQuad.tintIndex(); - #endif + #if MC_VER <= MC_1_21_11 + this.needPostTinting = firstQuad.isTinted(); + #else + this.needPostTinting = firstQuad.materialInfo().isTinted(); + #endif - #if MC_VER < MC_1_17_1 - this.baseColor = calculateColorFromTexture( - firstQuad.#if MC_VER <= MC_1_12_2 getSprite() #else sprite #endif, - EColorMode.getColorMode(this.blockState.getBlock())); - #elif MC_VER < MC_1_21_5 - this.baseColor = calculateColorFromTexture( - firstQuad.getSprite(), - EColorMode.getColorMode(this.blockState.getBlock())); - #else - this.baseColor = calculateColorFromTexture( - firstQuad.sprite(), - EColorMode.getColorMode(this.blockState.getBlock())); - #endif + #if MC_VER <= MC_1_21_4 + this.tintIndex = firstQuad.getTintIndex(); + #elif MC_VER <= MC_1_21_11 + this.tintIndex = firstQuad.tintIndex(); + #else + this.tintIndex = firstQuad.materialInfo().tintIndex(); + #endif + + #if MC_VER < MC_1_17_1 + this.baseColor = calculateColorFromTexture( + firstQuad.sprite, + EColorMode.getColorMode(this.blockState.getBlock())); + #elif MC_VER < MC_1_21_5 + this.baseColor = calculateColorFromTexture( + firstQuad.getSprite(), + EColorMode.getColorMode(this.blockState.getBlock())); + #elif MC_VER <= MC_1_21_11 + this.baseColor = calculateColorFromTexture( + firstQuad.sprite(), + EColorMode.getColorMode(this.blockState.getBlock())); + #else + this.baseColor = calculateColorFromTexture( + firstQuad.materialInfo().sprite(), + EColorMode.getColorMode(this.blockState.getBlock())); + #endif + } + catch (Exception e) + { + // Shouldn't normally happen, but there was at least + // one report of MC's texture being un-loaded + // which prevented us from getting the texture. + // So we should have some basic backup logic. + + LOGGER.warn("Failed to get texture color for block ["+this.blockStateWrapper.getSerialString()+"] due to: ["+e.getMessage()+"], falling back to particle color."); + + this.needPostTinting = false; + this.tintIndex = 0; + this.baseColor = this.getParticleIconColor(); + } } else { @@ -315,34 +338,23 @@ public class ClientBlockStateColorCache @Nullable private List getUnculledQuads() { return this.getQuadsForDirection(null); } @Nullable - private List getQuadsForDirection(@Nullable #if MC_VER <= MC_1_12_2 EnumFacing #else Direction #endif direction) + private List getQuadsForDirection(@Nullable Direction direction) { - #if MC_VER <= MC_1_12_2 IBlockState #else BlockState #endif effectiveBlockState = this.blockState; + BlockState effectiveBlockState = this.blockState; // if this block is a slab, use it's double variant so we can get the top face, // otherwise the color will use the side, which isn't as accurate - // 1.12.2 doesn't have SlabType as property - #if MC_VER > MC_1_12_2 if (this.blockState.getBlock() instanceof SlabBlock) { - effectiveBlockState = this.blockState.setValue(SlabBlock.TYPE, SlabType.DOUBLE); + effectiveBlockState = this.blockState.setValue( SlabBlock.TYPE, SlabType.DOUBLE ); } - #endif List quads; - #if MC_VER <= MC_1_12_2 - try { - quads = MC.getBlockRendererDispatcher().getModelForState(effectiveBlockState).getQuads(effectiveBlockState, direction, RANDOM.nextLong()); - } - catch (Exception e) - { - quads = Collections.emptyList(); - } - #elif MC_VER < MC_1_21_5 + #if MC_VER < MC_1_21_5 quads = MC.getModelManager().getBlockModelShaper(). getBlockModel(effectiveBlockState).getQuads(effectiveBlockState, direction, RANDOM); - #else + #elif MC_VER <= MC_1_21_11 List blockModelPartList = MC.getModelManager().getBlockModelShaper(). getBlockModel(effectiveBlockState).collectParts(RANDOM); @@ -355,6 +367,17 @@ public class ClientBlockStateColorCache quads.addAll(blockModelPartList.get(i).getQuads(direction)); } } + #else + List blockModelPartList = new ArrayList<>(); + MC.getModelManager().getBlockStateModelSet() + .get(effectiveBlockState).collectParts(RANDOM, blockModelPartList); + + quads = new ArrayList<>(); + for (int i = 0; i < blockModelPartList.size(); i++) + { + // if direction is null this will return the unculled quads + quads.addAll(blockModelPartList.get(i).getQuads(direction)); + } #endif return quads; @@ -385,37 +408,21 @@ public class ClientBlockStateColorCache //_ OpenGL RGBA format Java Order: 0xAA BB GG RR tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v); - #if MC_VER <= MC_1_12_2 - int b = (tempColor & 0x000000FF); - int g = (tempColor & 0x0000FF00) >>> 8; - int r = (tempColor & 0x00FF0000) >>> 16; - int a = (tempColor & 0xFF000000) >>> 24; - #else int r = (tempColor & 0x000000FF); int g = (tempColor & 0x0000FF00) >>> 8; int b = (tempColor & 0x00FF0000) >>> 16; int a = (tempColor & 0xFF000000) >>> 24; - #endif - int scale = 1; if (colorMode == EColorMode.Leaves) { - //switch (//FIXME add config option) - // case BLACK: - // a = 255; //simulate black background of fast leaves - // break; - // case IGNORE: - if (a == 0) { - continue; //same long grass - } - else - { - a = 255; //just in case there are semi transparent pixels - } - // break; - // case TRANSPARENT: - // break; //do nothing, let it count towards transparency - + if (a == 0) + { + continue; //same long grass + } + else + { + a = 255; //just in case there are semi transparent pixels + } } else if (a == 0 && colorMode != EColorMode.Glass) { @@ -461,9 +468,7 @@ public class ClientBlockStateColorCache } private static int getTextureWidth(TextureAtlasSprite texture) { - #if MC_VER <= MC_1_12_2 - return texture.getIconWidth(); - #elif MC_VER < MC_1_19_4 + #if MC_VER < MC_1_19_4 return texture.getWidth(); #else return texture.contents().width(); @@ -471,9 +476,7 @@ public class ClientBlockStateColorCache } private static int getTextureHeight(TextureAtlasSprite texture) { - #if MC_VER <= MC_1_12_2 - return texture.getIconHeight(); - #elif MC_VER < MC_1_19_4 + #if MC_VER < MC_1_19_4 return texture.getHeight(); #else return texture.contents().height(); @@ -507,12 +510,12 @@ public class ClientBlockStateColorCache private int getParticleIconColor() { return calculateColorFromTexture( - #if MC_VER <= MC_1_12_2 - Minecraft.getMinecraft().getBlockRendererDispatcher().getBlockModelShapes().getTexture(this.blockState), - #else + #if MC_VER <= MC_1_21_11 Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState), + #else + Minecraft.getInstance().getModelManager().getBlockStateModelSet().get(this.blockState).particleMaterial().sprite(), #endif - EColorMode.getColorMode(this.blockState.getBlock())); + EColorMode.getColorMode(this.blockState.getBlock())); } @@ -524,83 +527,89 @@ public class ClientBlockStateColorCache public int getColor(BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource, DhBlockPos blockPos) { // only get the tint if the block needs to be tinted - if (!this.needPostTinting) + int tintColor = AbstractDhTintGetter.INVALID_COLOR; + if (this.needPostTinting) { - return this.baseColor; - } - - // don't try tinting blocks that don't support our method of tint getting - if (BROKEN_BLOCK_STATES.contains(this.blockState)) - { - return this.baseColor; - } - - - // attempt to get the tint - int tintColor = -1; - try - { - // 1.12.2 doesn't have BlockAndTintGetter -> get tintColor from biome - #if MC_VER <= MC_1_12_2 - WorldClient world = (WorldClient) this.clientLevelWrapper.getWrappedMcObject(); - BlockPos mcPos = new BlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()); - Block block = this.blockState.getBlock(); - if (block instanceof BlockGrass || block instanceof BlockBush) + // don't try tinting blocks that don't support our method of tint getting + if (BROKEN_BLOCK_STATES.contains(this.blockState)) { - tintColor = biomeWrapper.biome.getGrassColorAtPos(mcPos); + return this.baseColor; } - else if (block instanceof BlockLeaves) + + + // attempt to get the tint + try { - tintColor = biomeWrapper.biome.getFoliageColorAtPos(mcPos); - } - else if (block instanceof BlockLiquid) // We don't want lava to fall into the else block - { - if(block == Blocks.WATER || block == Blocks.FLOWING_WATER) + // try to use the fast tint getter logic first + if (!BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState)) { - tintColor = biomeWrapper.biome.getWaterColor(); - } - } - else - { - BlockColors blockColors = Minecraft.getMinecraft().getBlockColors(); - tintColor = blockColors.colorMultiplier(blockState, world, mcPos, this.tintIndex); - - if (tintColor == -1) - { - tintColor = blockColors.getColor(blockState, world, mcPos); - } - } - #else - // try to use the fast tint getter logic first - if (!BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState)) - { - try - { - TintWithoutLevelOverrider tintOverride = TintWithoutLevelOverrideGetter.get(); - tintOverride.update(biomeWrapper, this.blockStateWrapper, fullDataSource, this.clientLevelWrapper); - - // try using DH's cached tint values first if possible - tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos)); - if (tintColor == AbstractDhTintGetter.INVALID_COLOR) + try { - // one or more tint values weren't calculated, - // we need MC's color resolver + TintWithoutLevelOverrider tintOverride = TintWithoutLevelOverrideGetter.get(); + tintOverride.update(biomeWrapper, this.blockStateWrapper, fullDataSource, this.clientLevelWrapper); + + // try using DH's cached tint values first if possible + tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos)); + if (tintColor == AbstractDhTintGetter.INVALID_COLOR) + { + // one or more tint values weren't calculated, + // we need MC's color resolver + #if MC_VER <= MC_1_21_11 tintColor = Minecraft.getInstance() .getBlockColors() .getColor(this.blockState, - tintOverride, + tintOverride, // tintOverride will save the result of this query to speed up future queries McObjectConverter.Convert(blockPos), this.tintIndex); + #else + BlockTintSource tintSource = Minecraft.getInstance() + .getBlockColors() + .getTintSource(this.blockState, this.tintIndex); + // a tint source may be null for blocks that don't actually need tinting + // in that case the base color should be sufficient + // Example: cherry blossom leaves + if (tintSource != null) + { + BlockPos mcPos = McObjectConverter.Convert(blockPos); + tintColor = tintSource.colorInWorld(this.blockState, tintOverride, mcPos); + if (tintColor == -1) + { + tintColor = tintSource.colorAsTerrainParticle(this.blockState, tintOverride, mcPos); + } + } + + if (tintColor == -1) + { + // no color found, use the base color + tintColor = AbstractDhTintGetter.INVALID_COLOR; + } + + // save this color to speed up future queries + TintWithoutLevelOverrider.setStaticColor(this.blockStateWrapper, biomeWrapper, tintColor); + // try to get the blended color with this new information + tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos)); + #endif + } } - } - catch (UnsupportedOperationException e) - { + catch (Exception e) + { + #if MC_VER <= MC_1_21_11 // this exception generally occurs if the tint requires other blocks besides itself LOGGER.debug("Unable to use ["+ TintWithoutLevelOverrider.class.getSimpleName()+"] to get the block tint for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: [" + e.getMessage() + "]. Attempting to use backup method...", e); BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState); + #else + // only display the error once per block/biome type to reduce log spam + if (!BROKEN_BLOCK_STATES.contains(this.blockState)) + { + LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: [" + e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e); + BROKEN_BLOCK_STATES.add(this.blockState); + } + #endif + } } - } - + + // level-specific logic is only needed for MC 1.21.11 and older + #if MC_VER <= MC_1_21_11 // use the level logic only if requested if (BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState)) { @@ -622,28 +631,48 @@ public class ClientBlockStateColorCache } } #endif - } - catch (Exception e) - { - // only display the error once per block/biome type to reduce log spam - if (!BROKEN_BLOCK_STATES.contains(this.blockState)) + } + catch (Exception e) { - LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: ["+e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e); - BROKEN_BLOCK_STATES.add(this.blockState); + // only display the error once per block/biome type to reduce log spam + if (!BROKEN_BLOCK_STATES.contains(this.blockState)) + { + LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: [" + e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e); + BROKEN_BLOCK_STATES.add(this.blockState); + } } } - - if (tintColor != -1) + int returnColor; + if (tintColor != AbstractDhTintGetter.INVALID_COLOR) { - return ColorUtil.multiplyARGBwithRGB(this.baseColor, tintColor); + returnColor = ColorUtil.multiplyARGBwithRGB(this.baseColor, tintColor); } else { // unable to get the tinted color, use the base color instead - return this.baseColor; + returnColor = this.baseColor; } + + + // only fire an API event if needed + // (this is done to reduce GC pressure and speed up color getting) + if (this.blockStateWrapper.allowApiColorOverride()) + { + DhApiBlockColorOverrideEvent.EventParam eventParam = ColorOverrideEventParamGetter.get(); + eventParam.update( + this.clientLevelWrapper, + this.blockStateWrapper, returnColor, + blockPos.getX(), blockPos.getY(), blockPos.getZ() + ); + ApiEventInjector.INSTANCE.fireAllEvents(DhApiBlockColorOverrideEvent.class, eventParam); + + // let the API user override this color + returnColor = eventParam.getColorAsInt(); + } + + return returnColor; } @@ -651,6 +680,7 @@ public class ClientBlockStateColorCache //================// // helper classes // //================// + //region private enum EColorMode { @@ -662,15 +692,15 @@ public class ClientBlockStateColorCache static EColorMode getColorMode(Block block) { - if (block instanceof #if MC_VER <= MC_1_12_2 BlockLeaves #else LeavesBlock #endif) + if (block instanceof LeavesBlock) { return Leaves; } - if (block instanceof #if MC_VER <= MC_1_12_2 BlockFlower #else FlowerBlock #endif) + if (block instanceof FlowerBlock) { return Flower; } - if (block.toString().toLowerCase().contains("glass")) + if (block.toString().contains("glass")) { return Glass; } @@ -682,6 +712,8 @@ public class ClientBlockStateColorCache } } + //endregion + } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/TextureAtlasSpriteWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/TextureAtlasSpriteWrapper.java index 26d73cfc4..1fa5f0ae2 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/TextureAtlasSpriteWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/TextureAtlasSpriteWrapper.java @@ -19,12 +19,13 @@ package com.seibel.distanthorizons.common.wrappers.block; +import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import net.minecraft.client.renderer.texture.TextureAtlasSprite; #if MC_VER < MC_1_17_1 #elif MC_VER < MC_1_21_3 #else -import com.seibel.distanthorizons.core.util.ColorUtil; +import com.seibel.distanthorizons.coreapi.util.ColorUtil; import net.minecraft.client.renderer.texture.SpriteContents; #endif diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/TintGetterOverride.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/TintGetterOverride.java index dc2a16ace..3554b5efb 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/TintGetterOverride.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/TintGetterOverride.java @@ -63,8 +63,10 @@ public class TintGetterOverride extends AbstractDhTintGetter // methods // //=========// + #if MC_VER <= MC_1_21_11 @Override public float getShade(Direction direction, boolean bl) { return this.parent.getShade(direction, bl); } + #endif @Override public LevelLightEngine getLightEngine() { return this.parent.getLightEngine(); } @@ -175,6 +177,13 @@ public class TintGetterOverride extends AbstractDhTintGetter @Override public int getSectionYFromSectionIndex(int i) { return this.parent.getSectionYFromSectionIndex(i); } + #endif + + + #if MC_VER <= MC_1_21_11 + #else + @Override + public CardinalLighting cardinalLighting() { return CardinalLighting.DEFAULT; } #endif diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/TintWithoutLevelOverrider.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/TintWithoutLevelOverrider.java index e39475d35..406b625c7 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/TintWithoutLevelOverrider.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/TintWithoutLevelOverrider.java @@ -18,9 +18,7 @@ */ package com.seibel.distanthorizons.common.wrappers.block; -#if MC_VER > MC_1_12_2 -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; + import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.world.level.LevelReader; @@ -30,6 +28,12 @@ import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.material.FluidState; import org.jetbrains.annotations.Nullable; +#if MC_VER <= MC_1_21_11 +#else +import net.minecraft.world.level.CardinalLighting; +#endif + + public class TintWithoutLevelOverrider extends AbstractDhTintGetter { @@ -46,9 +50,12 @@ public class TintWithoutLevelOverrider extends AbstractDhTintGetter // methods // //=========// + #if MC_VER <= MC_1_21_11 @Override public float getShade(Direction direction, boolean shade) { throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only."); } + #endif + @Override public LevelLightEngine getLightEngine() { throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only."); } @@ -87,5 +94,18 @@ public class TintWithoutLevelOverrider extends AbstractDhTintGetter #endif + + //=================// + // post MC 1.21.11 // + //=================// + + #if MC_VER <= MC_1_21_11 + #else + @Override + public CardinalLighting cardinalLighting() + { throw new UnsupportedOperationException("ERROR: cardinalLighting() called on TintWithoutLevelOverrider. Object is for tinting only."); } + #endif + + + } -#endif \ No newline at end of file diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java index 47bc58009..01d7745f9 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/chunk/ChunkWrapper.java @@ -130,7 +130,12 @@ public class ChunkWrapper implements IChunkWrapper { this.chunk = chunk; this.wrappedLevel = wrappedLevel; + + #if MC_VER <= MC_1_21_11 this.chunkPos = new DhChunkPos(chunk.getPos().x, chunk.getPos().z); + #else + this.chunkPos = new DhChunkPos(chunk.getPos().x(), chunk.getPos().z()); + #endif } @Override diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/ClassicConfigGUI.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/ClassicConfigGUI.java deleted file mode 100644 index b7c365ff5..000000000 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/ClassicConfigGUI.java +++ /dev/null @@ -1,1366 +0,0 @@ -package com.seibel.distanthorizons.common.wrappers.gui; - -import java.util.AbstractMap; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.function.Function; -import java.util.function.Predicate; -import java.util.regex.Pattern; - -import com.seibel.distanthorizons.api.enums.config.DisallowSelectingViaConfigGui; -import com.seibel.distanthorizons.common.wrappers.gui.config.ConfigGuiInfo; -import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper; -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.config.ConfigHandler; -import com.seibel.distanthorizons.core.config.types.*; - -import com.seibel.distanthorizons.core.config.types.enums.EConfigCommentTextPosition; -import com.seibel.distanthorizons.core.config.types.enums.EConfigValidity; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.jar.updater.SelfUpdater; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.util.AnnotationUtil; -import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui; -import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper; -import com.seibel.distanthorizons.coreapi.ModInfo; -#if MC_VER <= MC_1_12_2 -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.*; -import net.minecraft.client.renderer.Tessellator; -import net.minecraft.util.text.ITextComponent; -import net.minecraft.util.text.Style; -import net.minecraft.util.text.TextFormatting; -#else -import com.seibel.distanthorizons.common.wrappers.gui.updater.ChangelogScreen; -import net.minecraft.ChatFormatting; -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.Font; -import net.minecraft.client.gui.components.AbstractWidget; -import net.minecraft.client.gui.components.Button; -import net.minecraft.client.gui.components.ContainerObjectSelectionList; -import net.minecraft.client.gui.components.EditBox; -import net.minecraft.client.gui.components.events.GuiEventListener; -import net.minecraft.client.gui.screens.Screen; -import net.minecraft.network.chat.Component; -import com.mojang.blaze3d.platform.InputConstants; -import org.lwjgl.glfw.GLFW; -#endif -import com.seibel.distanthorizons.core.logging.DhLogger; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -#if MC_VER <= MC_1_12_2 -#elif MC_VER < MC_1_20_1 -import com.mojang.blaze3d.vertex.PoseStack; -import net.minecraft.client.gui.GuiComponent; -#else -import net.minecraft.client.gui.GuiGraphics; -#endif - -#if MC_VER >= MC_1_17_1 -import net.minecraft.client.gui.narration.NarratableEntry; -#endif - -#if MC_VER <= MC_1_12_2 -import net.minecraft.util.ResourceLocation; -#elif MC_VER <= MC_1_21_10 -import net.minecraft.resources.ResourceLocation; -#else -import net.minecraft.resources.Identifier; -#endif - -import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*; -import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.Translatable; - - -/* - * Based upon TinyConfig but is highly modified - * https://github.com/Minenash/TinyConfig - * - * Note: floats don't work with this system, use doubles. - * - * @author coolGi - * @author Motschen - * @author James Seibel - * @version 5-21-2022 - */ -@SuppressWarnings("unchecked") -public class ClassicConfigGUI -{ - private static final DhLogger LOGGER = new DhLoggerBuilder().build(); - public static final DhLogger RATE_LIMITED_LOGGER = new DhLoggerBuilder() - .maxCountPerSecond(1) - .build(); - - public static final ConfigCoreInterface CONFIG_CORE_INTERFACE = new ConfigCoreInterface(); - - private static final MinecraftClientWrapper MC_CLIENT = MinecraftClientWrapper.INSTANCE; - - - - //==============// - // Initializers // - //==============// - - // Some regexes to check if an input is valid - private static final Pattern INTEGER_ONLY_REGEX = Pattern.compile("(-?[0-9]*)"); - private static final Pattern DECIMAL_ONLY_REGEX = Pattern.compile("-?([\\d]+\\.?[\\d]*|[\\d]*\\.?[\\d]+|\\.)"); - - private static class ConfigScreenConfigs - { - // This contains all the configs for the configs - public static final int SPACE_FROM_RIGHT_SCREEN = 10; - public static final int SPACE_BETWEEN_TEXT_AND_OPTION_FIELD = 8; - public static final int BUTTON_WIDTH_SPACING = 5; - public static final int RESET_BUTTON_WIDTH = 60; - public static final int RESET_BUTTON_HEIGHT = 20; - public static final int OPTION_FIELD_WIDTH = 150; - public static final int OPTION_FIELD_HEIGHT = 20; - public static final int CATEGORY_BUTTON_WIDTH = 200; - public static final int CATEGORY_BUTTON_HEIGHT = 20; - - } - - - - //==============// - // GUI handling // - //==============// - - /** if you want to get this config gui's screen call this */ - public static #if MC_VER <= MC_1_12_2 GuiScreen #else Screen #endif getScreen(#if MC_VER <= MC_1_12_2 GuiScreen #else Screen #endif parent, String category) - { return new DhConfigScreen(parent, category); } - - private static class DhConfigScreen extends DhScreen - { - private static final ILangWrapper LANG_WRAPPER = SingletonInjector.INSTANCE.get(ILangWrapper.class); - - private static final String TRANSLATION_PREFIX = ModInfo.ID + ".config."; - - - private final #if MC_VER <= MC_1_12_2 GuiScreen #else Screen #endif parent; - private final String category; - private ConfigListWidget configListWidget; - private boolean reload = false; - - private #if MC_VER <= MC_1_12_2 GuiButton #else Button #endif doneButton; - - - - //=============// - // constructor // - //=============// - - protected DhConfigScreen(#if MC_VER <= MC_1_12_2 GuiScreen #else Screen #endif parent, String category) - { - super(Translatable( - LANG_WRAPPER.langExists(ModInfo.ID + ".config" + (category.isEmpty() ? "." + category : "") + ".title") ? - ModInfo.ID + ".config.title" : - ModInfo.ID + ".config" + (category.isEmpty() ? "" : "." + category) + ".title") - ); - this.parent = parent; - this.category = category; - } - - - @Override - #if MC_VER <= MC_1_12_2 - public void updateScreen() { super.updateScreen(); } - #else - public void tick() { super.tick(); } - #endif - - - - //==================// - // menu UI creation // - //==================// - - @Override - #if MC_VER <= MC_1_12_2 - public void initGui() - #else - protected void init() - #endif - { - super.#if MC_VER <= MC_1_12_2 initGui(); #else init(); #endif - if (!this.reload) - { - ConfigHandler.INSTANCE.configFileHandler.loadFromFile(); - } - - // Changelog button - #if MC_VER > MC_1_12_2 - if (Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get() - // we only have changelogs for stable builds - && !ModInfo.IS_DEV_BUILD) - { - this.addBtn(new TexturedButtonWidget( - // Where the button is on the screen - this.width - 28, this.height - 28, - // Width and height of the button - 20, 20, - // texture UV Offset - 0, 0, - // Some texture stuff - 0, - #if MC_VER < MC_1_21_1 - new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), - #elif MC_VER <= MC_1_21_10 - ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"), - #else - Identifier.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"), - #endif - 20, 20, - // Create the button and tell it where to go - (buttonWidget) -> { - ChangelogScreen changelogScreen = new ChangelogScreen(this); - if (changelogScreen.usable) - { - Objects.requireNonNull(this.minecraft).setScreen(changelogScreen); - } - else - { - LOGGER.warn("Changelog was not able to open"); - } - }, - // Add a title to the button - Translatable(ModInfo.ID + ".updater.title") - )); - } - #endif - - - // back button - this.addBtn(MakeBtn(Translatable("distanthorizons.general.back"), - (this.width / 2) - 154, this.height - 28, - ConfigScreenConfigs.OPTION_FIELD_WIDTH, ConfigScreenConfigs.OPTION_FIELD_HEIGHT, - (button) -> - { - ConfigHandler.INSTANCE.configFileHandler.loadFromFile(); - #if MC_VER <= MC_1_12_2 - Objects.requireNonNull(this.mc).displayGuiScreen(this.parent); - #else - Objects.requireNonNull(this.minecraft).setScreen(this.parent); - #endif - })); - - // done/close button - this.doneButton = this.addBtn( - MakeBtn(Translatable("distanthorizons.general.done"), - (this.width / 2) + 4, this.height - 28, - ConfigScreenConfigs.OPTION_FIELD_WIDTH, ConfigScreenConfigs.OPTION_FIELD_HEIGHT, - (button) -> - { - ConfigHandler.INSTANCE.configFileHandler.saveToFile(); - #if MC_VER <= MC_1_12_2 - Objects.requireNonNull(this.mc).displayGuiScreen(this.parent); - #else - Objects.requireNonNull(this.minecraft).setScreen(this.parent); - #endif - })); - - this.configListWidget = new ConfigListWidget(#if MC_VER <= MC_1_12_2 this.mc #else this.minecraft #endif, this.width * 2, this.height, 32, 32, 25); - - #if MC_VER > MC_1_12_2 - #if MC_VER < MC_1_20_6 // no background is rendered in MC 1.20.6+ - if (this.minecraft != null && this.minecraft.level != null) - { - this.configListWidget.setRenderBackground(false); - } - #endif - - this.addWidget(this.configListWidget); - #endif - - for (AbstractConfigBase configEntry : ConfigHandler.INSTANCE.configBaseList) - { - try - { - if (configEntry.getCategory().matches(this.category) - && configEntry.getAppearance().showInGui) - { - this.addMenuItem(configEntry); - } - } - catch (Exception e) - { - String message = "ERROR: Failed to show [" + configEntry.getNameAndCategory() + "], error: ["+e.getMessage()+"]"; - if (configEntry.get() != null) - { - message += " with the value [" + configEntry.get() + "] with type [" + configEntry.getType() + "]"; - } - - LOGGER.error(message, e); - } - } - - CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run()); - } - private void addMenuItem(AbstractConfigBase configEntry) - { - trySetupConfigEntry(configEntry); - - if (this.tryCreateInputField(configEntry)) return; - if (this.tryCreateCategoryButton(configEntry)) return; - if (this.tryCreateButton(configEntry)) return; - if (this.tryCreateComment(configEntry)) return; - if (this.tryCreateSpacer(configEntry)) return; - if (this.tryCreateLinkedEntry(configEntry)) return; - - LOGGER.warn("Config [" + configEntry.getNameAndCategory() + "] failed to show. Please try something like changing its type."); - } - - private static void trySetupConfigEntry(AbstractConfigBase configMenuOption) - { - configMenuOption.guiValue = new ConfigGuiInfo(); - Class configValueClass = configMenuOption.getType(); - - if (configMenuOption instanceof ConfigEntry) - { - ConfigEntry configEntry = (ConfigEntry) configMenuOption; - - if (configValueClass == Integer.class) - { - setupTextMenuOption(configEntry, Integer::parseInt, INTEGER_ONLY_REGEX, true); - } - else if (configValueClass == Double.class) - { - setupTextMenuOption(configEntry, Double::parseDouble, DECIMAL_ONLY_REGEX, false); - } - else if (configValueClass == Float.class) - { - setupTextMenuOption(configEntry, Float::parseFloat, DECIMAL_ONLY_REGEX, false); - } - else if (configValueClass == String.class || configValueClass == List.class) - { - // For string or list - setupTextMenuOption(configEntry, String::length, null, true); - } - else if (configValueClass == Boolean.class) - { - ConfigEntry booleanConfigEntry = (ConfigEntry) configEntry; - setupBooleanMenuOption(booleanConfigEntry); - } - else if (configValueClass.isEnum()) - { - ConfigEntry> enumConfigEntry = (ConfigEntry>) configEntry; - Class> configEnumClass = (Class>) configValueClass; - setupEnumMenuOption(enumConfigEntry, configEnumClass); - } - else - { - LOGGER.error("No definition for config with type: ["+configValueClass.getName()+"], for config: ["+configMenuOption.name+"]."); - } - } - - } - private static void setupTextMenuOption(AbstractConfigBase configMenuOption, Function parsingFunc, @Nullable Pattern pattern, boolean cast) - { - final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) configMenuOption.guiValue); - - configGuiInfo.tooltipFunction = - (editBox, button) -> - (stringValue) -> - { - boolean isNumber = (pattern != null); - - stringValue = stringValue.trim(); - if (!(stringValue.isEmpty() || !isNumber || pattern.matcher(stringValue).matches())) - { - return false; - } - - - Number numberValue = configMenuOption.typeIsFloatingPointNumber() ? 0.0 : 0; // different default values are needed so implicit casting works correctly (if not done casting from 0 (an int) to a double will cause an exception) - configGuiInfo.errorMessage = null; - if (isNumber - && !stringValue.isEmpty() - && !stringValue.equals("-") - && !stringValue.equals(".")) - { - ConfigEntry numberConfigEntry = (ConfigEntry) configMenuOption; - - try - { - numberValue = parsingFunc.apply(stringValue); - } - catch (Exception e) - { - numberValue = null; - } - - EConfigValidity validity = numberConfigEntry.getValidity(numberValue); - switch (validity) - { - case VALID: - configGuiInfo.errorMessage = null; - break; - case NUMBER_TOO_LOW: - configGuiInfo.errorMessage = TextOrTranslatable("§cMinimum length is " + numberConfigEntry.getMin()); - break; - case NUMBER_TOO_HIGH: - configGuiInfo.errorMessage = TextOrTranslatable("§cMaximum length is " + numberConfigEntry.getMax()); - break; - case INVALID: - configGuiInfo.errorMessage = TextOrTranslatable("§cValue is invalid"); - break; - } - } - - editBox.setTextColor(((ConfigEntry) configMenuOption).getValidity(numberValue) == EConfigValidity.VALID ? 0xFFFFFFFF : 0xFFFF7777); // white and red - - - if (configMenuOption.getType() == String.class - || configMenuOption.getType() == List.class) - { - ((ConfigEntry) configMenuOption).uiSetWithoutSaving(stringValue); - } - else if (((ConfigEntry) configMenuOption).getValidity(numberValue) == EConfigValidity.VALID) - { - if (!cast) - { - ((ConfigEntry) configMenuOption).uiSetWithoutSaving(numberValue); - } - else - { - ((ConfigEntry) configMenuOption).uiSetWithoutSaving(numberValue != null ? numberValue.intValue() : 0); - } - } - - return true; - }; - } - private static void setupBooleanMenuOption(ConfigEntry booleanConfigEntry) - { - // For boolean - #if MC_VER <= MC_1_12_2 - Function func = value -> Translatable("distanthorizons.general."+((Boolean) value ? "true" : "false")).setStyle(new Style().setColor((Boolean) value ? TextFormatting.GREEN : TextFormatting.RED)); - #else - Function func = value -> Translatable("distanthorizons.general."+((Boolean) value ? "true" : "false")).withStyle((Boolean) value ? ChatFormatting.GREEN : ChatFormatting.RED); - #endif - - final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) booleanConfigEntry.guiValue); - - #if MC_VER <= MC_1_12_2 - configGuiInfo.buttonOptionMap = - new AbstractMap.SimpleEntry>( - (button) -> - { - button.enabled = !booleanConfigEntry.apiIsOverriding(); - - booleanConfigEntry.uiSetWithoutSaving(!booleanConfigEntry.get()); - button.displayString = func.apply(booleanConfigEntry.get()).getFormattedText(); - }, func); - #else - configGuiInfo.buttonOptionMap = - new AbstractMap.SimpleEntry>( - (button) -> - { - button.active = !booleanConfigEntry.apiIsOverriding(); - - booleanConfigEntry.uiSetWithoutSaving(!booleanConfigEntry.get()); - button.setMessage(func.apply(booleanConfigEntry.get())); - }, func); - #endif - } - private static void setupEnumMenuOption(ConfigEntry> enumConfigEntry, Class> enumClass) - { - List> enumList = Arrays.asList(enumClass.getEnumConstants()); - - final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) enumConfigEntry.guiValue); - - Function getEnumTranslatableFunc = (value) -> Translatable(TRANSLATION_PREFIX + "enum." + enumClass.getSimpleName() + "." + enumConfigEntry.get().toString()); - configGuiInfo.buttonOptionMap = - new AbstractMap.SimpleEntry<#if MC_VER <= MC_1_12_2 OnPressed #else Button.OnPress #endif, Function>( - (button) -> - { - // get the currently selected enum and enum index - int startingIndex = enumList.indexOf(enumConfigEntry.get()); - Enum enumValue = enumList.get(startingIndex); - - #if MC_VER <= MC_1_12_2 - boolean shiftPressed = GuiScreen.isShiftKeyDown(); - #else - boolean shiftPressed = InputConstants.isKeyDown(MC_CLIENT.getGlfwWindowId(), GLFW.GLFW_KEY_LEFT_SHIFT) || InputConstants.isKeyDown(MC_CLIENT.getGlfwWindowId(), GLFW.GLFW_KEY_RIGHT_SHIFT); - #endif - - // move forward or backwards depending on if the shift key is pressed - int index = shiftPressed ? startingIndex-1 : startingIndex+1; - - // wrap around to the other side of the array when necessary - if (index >= enumList.size()) { index = 0; } - else if (index < 0) { index = enumList.size() - 1; } - - - // walk through the enums to find the next selectable one - while (index != startingIndex) - { - enumValue = enumList.get(index); - if (!AnnotationUtil.doesEnumHaveAnnotation(enumValue, DisallowSelectingViaConfigGui.class)) - { - // this enum shouldn't be selectable via the UI, - // skip it - break; - } - - // move forward or backwards depending on if the shift key is pressed - index = shiftPressed ? index-1 : index+1; - - // wrap around to the other side of the array when necessary - if (index >= enumList.size()) { index = 0; } - else if (index < 0) { index = enumList.size() - 1; } - } - - - if (index == startingIndex) - { - // one of the enums should be selectable, this is a programmer error - enumValue = enumList.get(startingIndex); - LOGGER.warn("Enum [" + enumValue.getClass() + "] doesn't contain any values that should be selectable via the UI, sticking to the currently selected value [" + enumValue + "]."); - } - - - enumConfigEntry.uiSetWithoutSaving(enumValue); - - #if MC_VER <= MC_1_12_2 - button.enabled = !enumConfigEntry.apiIsOverriding(); - button.displayString = getEnumTranslatableFunc.apply(enumConfigEntry.get()).getFormattedText(); - #else - button.active = !enumConfigEntry.apiIsOverriding(); - button.setMessage(getEnumTranslatableFunc.apply(enumConfigEntry.get())); - #endif - }, getEnumTranslatableFunc); - } - - private boolean tryCreateInputField(AbstractConfigBase configBase) - { - final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) configBase.guiValue); - - if (configBase instanceof ConfigEntry) - { - ConfigEntry configEntry = (ConfigEntry) configBase; - - - //==============// - // reset button // - //==============// - - #if MC_VER <= MC_1_12_2 OnPressed #else Button.OnPress #endif btnAction = (button) -> - { - configEntry.uiSetWithoutSaving(configEntry.getDefaultValue()); - this.reload = true; - #if MC_VER <= MC_1_12_2 - Objects.requireNonNull(this.mc).displayGuiScreen(ClassicConfigGUI.getScreen(this.parent, this.category)); - #else - Objects.requireNonNull(this.minecraft).setScreen(this.parent); - #endif - }; - - int resetButtonPosX = this.width - - ConfigScreenConfigs.RESET_BUTTON_WIDTH - - ConfigScreenConfigs.SPACE_FROM_RIGHT_SCREEN; - int resetButtonPosZ = 0; - - #if MC_VER <= MC_1_12_2 GuiButton #else Button #endif resetButton = MakeBtn( - #if MC_VER <= MC_1_12_2 - Translatable("distanthorizons.general.reset").setStyle(new Style().setColor(TextFormatting.RED)), - #else - Translatable("distanthorizons.general.reset").withStyle(ChatFormatting.RED), - #endif - resetButtonPosX, resetButtonPosZ, - ConfigScreenConfigs.RESET_BUTTON_WIDTH, ConfigScreenConfigs.RESET_BUTTON_HEIGHT, - btnAction); - - if (configEntry.apiIsOverriding()) - { - #if MC_VER <= MC_1_12_2 - resetButton.enabled = false; - resetButton.displayString = Translatable("distanthorizons.general.apiOverride").setStyle(new Style().setColor(TextFormatting.DARK_GRAY)).getFormattedText(); - #else - resetButton.active = false; - resetButton.setMessage(Translatable("distanthorizons.general.apiOverride").withStyle(ChatFormatting.DARK_GRAY)); - #endif - } - else - { - resetButton.#if MC_VER <= MC_1_12_2 enabled #else active #endif = true; - } - - - - //==============// - // option field // - //==============// - - #if MC_VER <= MC_1_12_2 ITextComponent #else Component #endif textComponent = this.GetTranslatableTextComponentForConfig(configEntry); - - int optionFieldPosX = this.width - - ConfigScreenConfigs.SPACE_FROM_RIGHT_SCREEN - - ConfigScreenConfigs.RESET_BUTTON_WIDTH - - ConfigScreenConfigs.BUTTON_WIDTH_SPACING - - ConfigScreenConfigs.OPTION_FIELD_WIDTH; - int optionFieldPosZ = 0; - - if (configGuiInfo.buttonOptionMap != null) - { - // enum/multi option input button - - Map.Entry<#if MC_VER <= MC_1_12_2 OnPressed #else Button.OnPress #endif, Function> widget = configGuiInfo.buttonOptionMap; - if (configEntry.getType().isEnum()) - { - widget.setValue((value) -> Translatable(TRANSLATION_PREFIX + "enum." + configEntry.getType().getSimpleName() + "." + configEntry.get().toString())); - } - - #if MC_VER <= MC_1_12_2 GuiButton #else Button #endif button = MakeBtn( - widget.getValue().apply(configEntry.get()), - optionFieldPosX, optionFieldPosZ, - ConfigScreenConfigs.OPTION_FIELD_WIDTH, ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT, - widget.getKey()); - - // deactivate the button if the API is overriding it - button.#if MC_VER <= MC_1_12_2 enabled #else active #endif = !configEntry.apiIsOverriding(); - - - this.configListWidget.addButton(this, configEntry, - button, - resetButton, - null, - textComponent); - - return true; - } - else - { - // text box input - - #if MC_VER <= MC_1_12_2 GuiTextField #else EditBox #endif widget = new #if MC_VER <= MC_1_12_2 GuiTextField #else EditBox #endif( - #if MC_VER <= MC_1_12_2 0, #endif - #if MC_VER <= MC_1_12_2 this.fontRenderer #else this.font #endif, - optionFieldPosX, optionFieldPosZ, - ConfigScreenConfigs.OPTION_FIELD_WIDTH - 4, ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT - #if MC_VER > MC_1_12_2 ,Translatable("") #endif ); - widget.#if MC_VER <= MC_1_12_2 setMaxStringLength(3_000_000); #else setMaxLength(3_000_000); #endif // hopefully 3 million characters should be enough for any normal use-case, lol - widget.#if MC_VER <= MC_1_12_2 setText #else insertText #endif (String.valueOf(configEntry.get())); - - Predicate processor = configGuiInfo.tooltipFunction.apply(widget, this.doneButton); - widget.#if MC_VER <= MC_1_12_2 setValidator(processor::test); #else setFilter(processor); #endif - - this.configListWidget.addButton(this, configEntry, widget, resetButton, null, textComponent); - - return true; - } - } - - return false; - } - private boolean tryCreateCategoryButton(AbstractConfigBase configType) - { - if (configType instanceof ConfigCategory) - { - ConfigCategory configCategory = (ConfigCategory) configType; - - #if MC_VER <= MC_1_12_2 ITextComponent #else Component #endif textComponent = this.GetTranslatableTextComponentForConfig(configCategory); - - int categoryPosX = this.width - ConfigScreenConfigs.CATEGORY_BUTTON_WIDTH - ConfigScreenConfigs.SPACE_FROM_RIGHT_SCREEN; - int categoryPosZ = this.height - ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT; // Note: the posZ value here seems to be ignored - - #if MC_VER <= MC_1_12_2 GuiButton #else Button #endif widget = MakeBtn(textComponent, - categoryPosX, categoryPosZ, - ConfigScreenConfigs.CATEGORY_BUTTON_WIDTH, ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT, - ((button) -> - { - ConfigHandler.INSTANCE.configFileHandler.saveToFile(); - #if MC_VER <= MC_1_12_2 - Objects.requireNonNull(this.mc).displayGuiScreen(ClassicConfigGUI.getScreen(this, configCategory.getDestination())); - #else - Objects.requireNonNull(this.minecraft).setScreen(ClassicConfigGUI.getScreen(this, configCategory.getDestination())); - #endif - })); - this.configListWidget.addButton(this, configType, widget, null, null, null); - - return true; - } - - return false; - } - private boolean tryCreateButton(AbstractConfigBase configType) - { - if (configType instanceof ConfigUIButton) - { - ConfigUIButton configUiButton = (ConfigUIButton) configType; - - #if MC_VER <= MC_1_12_2 ITextComponent #else Component #endif textComponent = this.GetTranslatableTextComponentForConfig(configUiButton); - - int buttonPosX = this.width - ConfigScreenConfigs.CATEGORY_BUTTON_WIDTH - ConfigScreenConfigs.SPACE_FROM_RIGHT_SCREEN; - - #if MC_VER <= MC_1_12_2 GuiButton #else Button #endif widget = MakeBtn(textComponent, - buttonPosX, this.height - 28, - ConfigScreenConfigs.CATEGORY_BUTTON_WIDTH, ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT, - (button) -> ((ConfigUIButton) configType).runAction()); - this.configListWidget.addButton(this, configType, widget, null, null, null); - - return true; - } - - return false; - } - private boolean tryCreateComment(AbstractConfigBase configType) - { - if (configType instanceof ConfigUIComment) - { - ConfigUIComment configUiComment = (ConfigUIComment) configType; - - #if MC_VER <= MC_1_12_2 ITextComponent #else Component #endif textComponent = this.GetTranslatableTextComponentForConfig(configUiComment); - if (configUiComment.parentConfigPath != null) - { - textComponent = Translatable(TRANSLATION_PREFIX + configUiComment.parentConfigPath); - } - - this.configListWidget.addButton(this, configType, null, null, null, textComponent); - - return true; - } - - return false; - } - private boolean tryCreateSpacer(AbstractConfigBase configType) - { - if (configType instanceof ConfigUISpacer) - { - #if MC_VER <= MC_1_12_2 GuiButton #else Button #endif spacerButton = MakeBtn(Translatable("distanthorizons.general.spacer"), - 10, 10, // having too small of a size causes division by 0 errors in older MC versions (IE 1.20.1) - 1, 1, - (button) -> {}); - - spacerButton.visible = false; - this.configListWidget.addButton(this, configType, spacerButton, null, null, null); - - return true; - } - - return false; - } - private boolean tryCreateLinkedEntry(AbstractConfigBase configType) - { - if (configType instanceof ConfigUiLinkedEntry) - { - this.addMenuItem(((ConfigUiLinkedEntry) configType).get()); - - return true; - } - - return false; - } - - private #if MC_VER <= MC_1_12_2 ITextComponent #else Component #endif GetTranslatableTextComponentForConfig(AbstractConfigBase configType) - { return Translatable(TRANSLATION_PREFIX + configType.getNameAndCategory());} - - - - //===========// - // rendering // - //===========// - - @Override - #if MC_VER <= MC_1_12_2 - public void drawScreen(int mouseX, int mouseY, float delta) - #elif MC_VER < MC_1_20_1 - public void render(PoseStack matrices, int mouseX, int mouseY, float delta) - #else - public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta) - #endif - { - #if MC_VER <= MC_1_12_2 - this.drawDefaultBackground(); - #elif MC_VER < MC_1_20_2 // 1.20.2 now enables this by default in the `this.list.render` function - this.renderBackground(matrices); // Renders background - #else - super.render(matrices, mouseX, mouseY, delta); - #endif - - #if MC_VER <= MC_1_12_2 - this.configListWidget.drawScreen(mouseX, mouseY, delta); // Render buttons - #else - this.configListWidget.render(matrices, mouseX, mouseY, delta); // Render buttons - #endif - - // Render config title - this.DhDrawCenteredString( - #if MC_VER > MC_1_12_2 - matrices, this.font, - #endif - this.title, - this.width / 2, 15, - #if MC_VER < MC_1_21_6 - 0xFFFFFF // RGB white - #else - 0xFFFFFFFF // ARGB white - #endif); - - - // render DH version - this.DhDrawString( - #if MC_VER > MC_1_12_2 - matrices, this.font, - #endif - TextOrLiteral(ModInfo.VERSION), 2, this.height - 10, - #if MC_VER < MC_1_21_6 - 0xAAAAAA // RGB white - #else - 0xFFAAAAAA // ARGB white - #endif); - - // If the update is pending, display this message to inform the user that it will apply when the game restarts - if (SelfUpdater.deleteOldJarOnJvmShutdown) - { - this.DhDrawString( - #if MC_VER > MC_1_12_2 - matrices, this.font, - #endif - Translatable(ModInfo.ID + ".updater.waitingForClose"), 4, this.height - 42, - #if MC_VER < MC_1_21_6 - 0xFFFFFF // RGB white - #else - 0xFFFFFFFF // ARGB white - #endif); - } - - #if MC_VER <= MC_1_12_2 - super.drawScreen(mouseX, mouseY, delta); - #elif MC_VER < MC_1_20_2 - super.render(matrices, mouseX, mouseY, delta); - #endif - - this.renderTooltip(#if MC_VER > MC_1_12_2 matrices,#endif mouseX, mouseY, delta); - } - - #if MC_VER <= MC_1_12_2 - private void renderTooltip(int mouseX, int mouseY, float delta) - #elif MC_VER < MC_1_20_1 - private void renderTooltip(PoseStack matrices, int mouseX, int mouseY, float delta) - #else - private void renderTooltip(GuiGraphics matrices, int mouseX, int mouseY, float delta) - #endif - { - #if MC_VER <= MC_1_12_2 Gui #else AbstractWidget #endif hoveredWidget = this.configListWidget.getHoveredButton(mouseX, mouseY); - if (hoveredWidget == null) - { - return; - } - - - DhButtonEntry button = DhButtonEntry.BUTTON_BY_WIDGET.get(hoveredWidget); - - - // A quick fix for tooltips on linked entries - AbstractConfigBase configBase = ConfigUiLinkedEntry.class.isAssignableFrom(button.dhConfigType.getClass()) ? - ((ConfigUiLinkedEntry) button.dhConfigType).get() : - button.dhConfigType; - - boolean apiOverrideActive = false; - if (configBase instanceof ConfigEntry) - { - apiOverrideActive = ((ConfigEntry)configBase).apiIsOverriding(); - } - - String key = TRANSLATION_PREFIX + (configBase.category.isEmpty() ? "" : configBase.category + ".") + configBase.getName() + ".@tooltip"; - - if (apiOverrideActive) - { - key = "distanthorizons.general.disabledByApi.@tooltip"; - } - - // display the validation error tooltip if present - final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) configBase.guiValue); - if (configGuiInfo.errorMessage != null) - { - this.DhRenderTooltip( - #if MC_VER > MC_1_12_2 - matrices, this.font, - #endif - configGuiInfo.errorMessage, mouseX, mouseY); - } - // display the tooltip if present - else if (LANG_WRAPPER.langExists(key)) - { - List<#if MC_VER <= MC_1_12_2 ITextComponent #else Component #endif> list = new ArrayList<>(); - String lang = LANG_WRAPPER.getLang(key); - for (String langLine : lang.split("\n")) - { - list.add(TextOrTranslatable(langLine)); - } - - this.DhRenderComponentTooltip( - #if MC_VER > MC_1_12_2 - matrices, this.font, - #endif - list, mouseX, mouseY); - } - } - - //==========// - // input // - //==========// - - #if MC_VER <= MC_1_12_2 - @Override - protected void mouseClicked(int mouseX, int mouseY, int mouseButton) throws java.io.IOException - { - super.mouseClicked(mouseX, mouseY, mouseButton); - - if (mouseY >= this.configListWidget.top && mouseY <= this.configListWidget.bottom) - { - for (ClassicConfigGUI.DhButtonEntry entry : this.configListWidget.children) - { - if (entry.button instanceof GuiButton btn && btn.visible) - { - if (btn.mousePressed(this.mc, mouseX, mouseY)) - { - btn.playPressSound(this.mc.getSoundHandler()); - OnPressed handler = GuiHelper.HANDLER_BY_BUTTON.get(btn); - if (handler != null) handler.pressed(btn); - } - } - else if (entry.button instanceof GuiTextField field && field.getVisible()) - { - field.mouseClicked(mouseX, mouseY, mouseButton); - } - - if (entry.resetButton instanceof GuiButton reset && reset.visible) - { - if (reset.mousePressed(this.mc, mouseX, mouseY)) - { - reset.playPressSound(this.mc.getSoundHandler()); - OnPressed handler = GuiHelper.HANDLER_BY_BUTTON.get(reset); - if (handler != null) handler.pressed(reset); - } - } - } - } - } - - @Override - protected void keyTyped(char typedChar, int keyCode) throws java.io.IOException - { - super.keyTyped(typedChar, keyCode); - for (ClassicConfigGUI.DhButtonEntry entry : this.configListWidget.children) - { - if (entry.button instanceof GuiTextField field) - { - field.textboxKeyTyped(typedChar, keyCode); - } - } - } - - @Override - public void handleMouseInput() throws java.io.IOException - { - super.handleMouseInput(); - this.configListWidget.handleMouseInput(); - } - - #endif - - //==========// - // shutdown // - //==========// - - /** When you close it, it goes to the previous screen and saves */ - @Override - #if MC_VER <= MC_1_12_2 - public void onGuiClosed() - #else - public void onClose() - #endif - { - ConfigHandler.INSTANCE.configFileHandler.saveToFile(); - #if MC_VER > MC_1_12_2 - Objects.requireNonNull(this.minecraft).setScreen(this.parent); - #endif - CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run()); - } - - - } - - - - //================// - // helper classes // - //================// - - public static class ConfigListWidget extends #if MC_VER <= MC_1_12_2 GuiListExtended #else ContainerObjectSelectionList #endif - { - #if MC_VER <= MC_1_12_2 - private List children = new ArrayList<>(); - #endif - - #if MC_VER <= MC_1_12_2 FontRenderer #else Font #endif textRenderer; - - public ConfigListWidget(Minecraft minecraftClient, int canvasWidth, int canvasHeight, int topMargin, int botMargin, int itemSpacing) - { - #if MC_VER < MC_1_20_4 - super(minecraftClient, canvasWidth, canvasHeight, topMargin, canvasHeight - botMargin, itemSpacing); - #else - super(minecraftClient, canvasWidth, canvasHeight - (topMargin + botMargin), topMargin, itemSpacing); - #endif - - this.centerListVertically = false; - this.textRenderer = minecraftClient.#if MC_VER <= MC_1_12_2 fontRenderer #else font #endif; - } - - #if MC_VER<= MC_1_12_2 - @Override - protected int getSize() - { - return this.children.size(); - } - - @Override - public IGuiListEntry getListEntry(int index) - { - return this.children.get(index); - } - - @Override - protected void drawContainerBackground(Tessellator tessellator) - { - if (this.mc.world != null) - { - return; // in-game don't draw dirt background - } - super.drawContainerBackground(tessellator); - } - - #endif - - #if MC_VER <= MC_1_12_2 - public void addButton(DhConfigScreen gui, AbstractConfigBase dhConfigType, Gui button, GuiButton resetButton, GuiButton indexButton, ITextComponent text) - #else - public void addButton(DhConfigScreen gui, AbstractConfigBase dhConfigType, AbstractWidget button, AbstractWidget resetButton, AbstractWidget indexButton, Component text) - #endif - { this.#if MC_VER <= MC_1_12_2 children.add #else addEntry #endif(new DhButtonEntry(gui, dhConfigType, button, text, resetButton, indexButton)); } - - @Override - public int #if MC_VER <= MC_1_12_2 getListWidth() #else getRowWidth() #endif { return 10_000; } - - public #if MC_VER <= MC_1_12_2 Gui #else AbstractWidget #endif getHoveredButton(double mouseX, double mouseY) - { - for (DhButtonEntry buttonEntry : this.children#if MC_VER > MC_1_12_2() #endif) - { - #if MC_VER <= MC_1_12_2 - Gui gui = buttonEntry.button; - if (gui == null) continue; - - double minX, minY, maxX, maxY; - - if (gui instanceof GuiButton button) - { - if (!button.visible) continue; - minX = button.x; - minY = button.y; - maxX = minX + button.width; - maxY = minY + button.height; - } - else if (gui instanceof GuiTextField field) - { - if (!field.getVisible()) continue; - minX = field.x; - minY = field.y; - maxX = minX + field.width; - maxY = minY + field.height; - } - else - { - continue; - } - - if (mouseX >= minX && mouseX < maxX && mouseY >= minY && mouseY < maxY) - { - return gui; - } - #else - AbstractWidget button = (AbstractWidget) buttonEntry.button; - if (button == null || !button.visible) continue; - - #if MC_VER < MC_1_19_4 - double minX = button.x; - double minY = button.y; - #else - double minX = button.getX(); - double minY = button.getY(); - #endif - - double maxX = minX + button.getWidth(); - double maxY = minY + button.getHeight(); - - if (mouseX >= minX && mouseX < maxX && mouseY >= minY && mouseY < maxY) - { - return button; - } - #endif - } - - return null; - } - - } - - - public static class DhButtonEntry #if MC_VER <= MC_1_12_2 implements GuiListExtended.IGuiListEntry #else extends ContainerObjectSelectionList.Entry #endif - { - private static final #if MC_VER <= MC_1_12_2 FontRenderer #else Font #endif textRenderer = Minecraft. #if MC_VER <= MC_1_12_2 getMinecraft().fontRenderer; #else getInstance().font; #endif - - private final #if MC_VER <= MC_1_12_2 Gui #else AbstractWidget #endif button; - - private final DhConfigScreen gui; - private final AbstractConfigBase dhConfigType; - - private final #if MC_VER <= MC_1_12_2 Gui #else AbstractWidget #endif resetButton; - private final #if MC_VER <= MC_1_12_2 Gui #else AbstractWidget #endif indexButton; - private final #if MC_VER <= MC_1_12_2 ITextComponent #else Component #endif text; - private final List<#if MC_VER <= MC_1_12_2 Gui #else AbstractWidget #endif> children = new ArrayList<>(); - - @NotNull - private final EConfigCommentTextPosition textPosition; - - public static final Map< #if MC_VER <= MC_1_12_2 Gui #else AbstractWidget #endif, #if MC_VER <= MC_1_12_2 ITextComponent #else Component #endif> TEXT_BY_WIDGET = new HashMap<>(); - public static final Map< #if MC_VER <= MC_1_12_2 Gui #else AbstractWidget #endif, DhButtonEntry> BUTTON_BY_WIDGET = new HashMap<>(); - - - - #if MC_VER <= MC_1_12_2 - public DhButtonEntry(DhConfigScreen gui, AbstractConfigBase dhConfigType, Gui button, ITextComponent text, GuiButton resetButton, GuiButton indexButton) - #else - public DhButtonEntry(DhConfigScreen gui, AbstractConfigBase dhConfigType, AbstractWidget button, Component text, AbstractWidget resetButton, AbstractWidget indexButton) - #endif - { - TEXT_BY_WIDGET.put(button, text); - BUTTON_BY_WIDGET.put(button, this); - - this.gui = gui; - this.dhConfigType = dhConfigType; - - this.button = button; - this.resetButton = resetButton; - this.text = text; - this.indexButton = indexButton; - - if (button != null) { this.children.add(button); } - if (resetButton != null) { this.children.add(resetButton); } - if (indexButton != null) { this.children.add(indexButton); } - - - EConfigCommentTextPosition textPosition = null; - if (this.dhConfigType instanceof ConfigUIComment) - { - textPosition = ((ConfigUIComment)this.dhConfigType).textPosition; - } - - if (textPosition == null) - { - if (this.button != null) - { - // if a button is present - textPosition = EConfigCommentTextPosition.RIGHT_JUSTIFIED; - } - else - { - textPosition = EConfigCommentTextPosition.CENTERED_OVER_BUTTONS; - } - } - this.textPosition = textPosition; - - } - - #if MC_VER <= MC_1_12_2 - @Override - public void drawEntry(int slotIndex, int x, int y, int listWidth, int slotHeight, int mouseX, int mouseY, boolean isSelected, float tickDelta) - #elif MC_VER < MC_1_20_1 - @Override - public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) - #elif MC_VER < MC_1_21_9 - @Override - public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) - #else - @Override - public void renderContent(GuiGraphics matrices, int mouseX, int mouseY, boolean hovered, float tickDelta) - #endif - { - try - { - // setting the "y" variable is necessary so each child item - // renders at the correct height, - // if not set they will render off-screen. - #if MC_VER < MC_1_21_9 - // Y value passed in from method args - #else - int y = this.getY(); - #endif - - - - if (this.button != null) - { - #if MC_VER <= MC_1_12_2 - if (this.button instanceof GuiButton guiButton) - { - SetY(guiButton, y); - guiButton.drawButton(Minecraft.getMinecraft(), mouseX, mouseY, tickDelta); - } - if (this.button instanceof GuiTextField guiTextField) - { - SetY(guiTextField, y); - guiTextField.drawTextBox(); - } - #else - SetY(this.button, y); - this.button.render(matrices, mouseX, mouseY, tickDelta); - #endif - } - - if (this.resetButton != null) - { - SetY(#if MC_VER <= MC_1_12_2 (GuiButton) #endif this.resetButton, y); - #if MC_VER <= MC_1_12_2 - ((GuiButton) this.resetButton).drawButton(Minecraft.getMinecraft(), mouseX, mouseY, tickDelta); - #else - this.resetButton.render(matrices, mouseX, mouseY, tickDelta); - #endif - } - - if (this.indexButton != null) - { - SetY(#if MC_VER <= MC_1_12_2 (GuiButton) #endif this.indexButton, y); - #if MC_VER <= MC_1_12_2 - ((GuiButton) this.indexButton).drawButton(Minecraft.getMinecraft(), mouseX, mouseY, tickDelta); - #else - this.indexButton.render(matrices, mouseX, mouseY, tickDelta); - #endif - } - - if (this.text != null) - { - #if MC_VER <= MC_1_12_2 - int translatedLength = textRenderer.getStringWidth(this.text.getFormattedText()); - #else - int translatedLength = textRenderer.width(this.text); - #endif - - int textXPos; - if (this.textPosition == EConfigCommentTextPosition.RIGHT_JUSTIFIED) - { - // text right justified aligned against the buttons - textXPos = this.gui.width - - translatedLength - - ConfigScreenConfigs.SPACE_BETWEEN_TEXT_AND_OPTION_FIELD - - ConfigScreenConfigs.SPACE_FROM_RIGHT_SCREEN - - ConfigScreenConfigs.OPTION_FIELD_WIDTH - - ConfigScreenConfigs.BUTTON_WIDTH_SPACING - - ConfigScreenConfigs.RESET_BUTTON_WIDTH; - } - else if (this.textPosition == EConfigCommentTextPosition.CENTERED_OVER_BUTTONS) - { - // have button centered relative to a category button - textXPos = this.gui.width - - (translatedLength / 2) - - (ConfigScreenConfigs.CATEGORY_BUTTON_WIDTH / 2) - - ConfigScreenConfigs.SPACE_FROM_RIGHT_SCREEN; - } - else if (this.textPosition == EConfigCommentTextPosition.CENTER_OF_SCREEN) - { - // have button centered in the screen - textXPos = (this.gui.width / 2) - - (translatedLength / 2); - } - else - { - throw new UnsupportedOperationException("No text position render defined for [" + this.textPosition + "]"); - } - - #if MC_VER <= MC_1_12_2 - textRenderer.drawString(this.text.getFormattedText(), textXPos, y + 5,0xFFFFFF); - #elif MC_VER < MC_1_20_1 - GuiComponent.drawString(matrices, textRenderer, - this.text, - textXPos, y + 5, - 0xFFFFFF); - #elif MC_VER < MC_1_21_6 - matrices.drawString(textRenderer, - this.text, - textXPos, y + 5, - 0xFFFFFF); - #else - matrices.drawString(textRenderer, - this.text, - textXPos, y + 5, - 0xFFFFFFFF); - #endif - } - } - catch (Exception e) - { - // should prevent crashing the game if there's an issue - RATE_LIMITED_LOGGER.error("Unexpected gui rendering issue: ["+e.getMessage()+"]", e); - } - } - - #if MC_VER <= MC_1_12_2 - @Override - public void updatePosition(int slotIndex, int x, int y, float partialTicks) { } - - @Override - public boolean mousePressed(int slotIndex, int mouseX, int mouseY, int mouseEvent, int relativeX, int relativeY) - { - return false; // handled in DhConfigScreen.mouseClicked - } - - @Override - public void mouseReleased(int slotIndex, int x, int y, int mouseEvent, int relativeX, int relativeY) { } - #endif - - #if MC_VER > MC_1_12_2 - @Override - public @NotNull List children() - { return this.children; } - #endif - - #if MC_VER >= MC_1_17_1 - @Override - public @NotNull List narratables() - { return this.children; } - #endif - - - - } - - - - //================// - // event handling // - //================// - - public static class ConfigCoreInterface implements IConfigGui - { - /** - * in the future it would be good to pass in the current page and other variables, - * but for now just knowing when the page is closed is good enough - */ - public final ArrayList onScreenChangeListenerList = new ArrayList<>(); - - - - @Override - public void addOnScreenChangeListener(Runnable newListener) { this.onScreenChangeListenerList.add(newListener); } - @Override - public void removeOnScreenChangeListener(Runnable oldListener) { this.onScreenChangeListenerList.remove(oldListener); } - - } - -} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/DhDebugScreenEntry.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/DhDebugScreenEntry.java index 775e7c5f1..6933238b7 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/DhDebugScreenEntry.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/DhDebugScreenEntry.java @@ -3,6 +3,7 @@ package com.seibel.distanthorizons.common.wrappers.gui; #if MC_VER < MC_1_21_9 // not supported for older MC versions #else + import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.coreapi.ModInfo; @@ -19,6 +20,8 @@ import net.minecraft.world.level.chunk.LevelChunk; #if MC_VER <= MC_1_21_10 import net.minecraft.resources.ResourceLocation; +#else +import net.minecraft.resources.Identifier; #endif #endif @@ -32,6 +35,7 @@ public class DhDebugScreenEntry implements net.minecraft.client.gui.components.d { public static void register() { + #if MC_VER <= MC_1_21_11 // This method is private, so its access will need to be widened DebugScreenEntries.register( // The id, this will be displayed on the options screen @@ -44,6 +48,23 @@ public class DhDebugScreenEntry implements net.minecraft.client.gui.components.d // The screen entry new DhDebugScreenEntry() ); + #elif MC_VER <= MC_1_21_11 + DebugScreenEntries.allEntries().put( + // The id, this will be displayed on the options screen + Identifier.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, "distant_horizons"), + + // The screen entry + new DhDebugScreenEntry() + ); + #else + DebugScreenEntries.register( + // The id, this will be displayed on the options screen + ModInfo.RESOURCE_NAMESPACE, + + // The screen entry + new DhDebugScreenEntry() + ); + #endif } @@ -58,6 +79,8 @@ public class DhDebugScreenEntry implements net.minecraft.client.gui.components.d displayer.addLine(message); } + //region + //// The following will display like so if it is the only entry on the screen: //// First left! First Right! //// @@ -86,6 +109,8 @@ public class DhDebugScreenEntry implements net.minecraft.client.gui.components.d // //displayer.addToGroup(GROUP_TWO, "I am another group!"); //displayer.addToGroup(GROUP_TWO, "This will appear after with no line breaks!"); + + //endregion } @Override @@ -94,5 +119,8 @@ public class DhDebugScreenEntry implements net.minecraft.client.gui.components.d // Always show regardless of accessibility option return true; } + + + } #endif diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/DhScreen.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/DhScreen.java index d8cd36be1..af277f529 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/DhScreen.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/DhScreen.java @@ -6,16 +6,20 @@ import net.minecraft.client.gui.GuiScreen; import net.minecraft.util.text.ITextComponent; #else import net.minecraft.client.gui.Font; -#if MC_VER < MC_1_20_1 -import com.mojang.blaze3d.vertex.PoseStack; -#else -import net.minecraft.client.gui.GuiGraphics; -#endif + import net.minecraft.client.gui.components.Button; import net.minecraft.client.gui.screens.Screen; import net.minecraft.network.chat.Component; #endif +#if MC_VER < MC_1_20_1 +import com.mojang.blaze3d.vertex.PoseStack; +#elif MC_VER <= MC_1_21_11 +import net.minecraft.client.gui.GuiGraphics; +#else +import net.minecraft.client.gui.GuiGraphicsExtractor; +#endif + import java.util.List; public class DhScreen extends #if MC_VER <= MC_1_12_2 GuiScreen #else Screen #endif @@ -116,7 +120,7 @@ public class DhScreen extends #if MC_VER <= MC_1_12_2 GuiScreen #else Screen #en { guiStack.renderTooltip(font, text, x, y); } - #else + #elif MC_VER <= MC_1_21_11 protected void DhDrawCenteredString(GuiGraphics guiStack, Font font, Component text, int x, int y, int color) { guiStack.drawCenteredString(font, text, x, y, color); @@ -125,10 +129,6 @@ public class DhScreen extends #if MC_VER <= MC_1_12_2 GuiScreen #else Screen #en { guiStack.drawString(font, text, x, y, color); } - //protected void DhRenderTooltip(GuiGraphics guiStack, Font font, List text, int x, int y) - //{ - // //guiStack.renderTooltip(font, text, x, y); - //} protected void DhRenderComponentTooltip(GuiGraphics guiStack, Font font, List comp, int x, int y) { guiStack.setComponentTooltipForNextFrame(font, comp, x, y); @@ -137,6 +137,23 @@ public class DhScreen extends #if MC_VER <= MC_1_12_2 GuiScreen #else Screen #en { guiStack.setTooltipForNextFrame(font, text, x, y); } + #else + protected void DhDrawCenteredString(GuiGraphicsExtractor guiStack, Font font, Component text, int x, int y, int color) + { + guiStack.centeredText(font, text, x, y, color); + } + protected void DhDrawString(GuiGraphicsExtractor guiStack, Font font, Component text, int x, int y, int color) + { + guiStack.text(font, text, x, y, color); + } + protected void DhRenderComponentTooltip(GuiGraphicsExtractor guiStack, Font font, List comp, int x, int y) + { + guiStack.setComponentTooltipForNextFrame(font, comp, x, y); + } + protected void DhRenderTooltip(GuiGraphicsExtractor guiStack, Font font, Component text, int x, int y) + { + guiStack.setTooltipForNextFrame(font, text, x, y); + } #endif diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/GetConfigScreen.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/GetConfigScreen.java index 1e4ed0390..9e57db5a7 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/GetConfigScreen.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/GetConfigScreen.java @@ -1,5 +1,6 @@ package com.seibel.distanthorizons.common.wrappers.gui; +import com.seibel.distanthorizons.common.wrappers.gui.classicConfig.ClassicConfigGUI; import com.seibel.distanthorizons.core.config.ConfigHandler; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.coreapi.ModInfo; diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/MinecraftScreen.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/MinecraftScreen.java index 6f1fe3c53..9e1f4ae30 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/MinecraftScreen.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/MinecraftScreen.java @@ -1,43 +1,42 @@ package com.seibel.distanthorizons.common.wrappers.gui; -#if MC_VER <= MC_1_12_2 -import net.minecraft.client.gui.GuiListExtended; -import net.minecraft.client.gui.GuiScreen; -import net.minecraft.client.gui.GuiSlot; -import org.lwjglx.opengl.Display; -#else + +import com.mojang.blaze3d.platform.Window; + + +import com.seibel.distanthorizons.core.config.gui.AbstractScreen; + +import net.minecraft.client.Minecraft; import net.minecraft.client.gui.components.ContainerObjectSelectionList; import net.minecraft.client.gui.screens.Screen; -import com.mojang.blaze3d.platform.Window; -import com.mojang.blaze3d.vertex.PoseStack; -#endif -import com.seibel.distanthorizons.core.config.gui.AbstractScreen; -import net.minecraft.client.Minecraft; -#if MC_VER >= MC_1_20_1 -import net.minecraft.client.gui.GuiGraphics; -#endif import org.jetbrains.annotations.NotNull; +#if MC_VER < MC_1_20_1 +import com.mojang.blaze3d.vertex.PoseStack; +#elif MC_VER <= MC_1_21_11 +import net.minecraft.client.gui.GuiGraphics; +#else +import net.minecraft.client.gui.GuiGraphicsExtractor; +#endif + import java.nio.file.Path; import java.util.*; public class MinecraftScreen { - public static #if MC_VER <= MC_1_12_2 GuiScreen #else Screen #endif getScreen(#if MC_VER <= MC_1_12_2 GuiScreen #else Screen #endif parent, AbstractScreen screen, String translationName) + public static Screen getScreen(Screen parent, AbstractScreen screen, String translationName) { return new ConfigScreenRenderer(parent, screen, translationName); } private static class ConfigScreenRenderer extends DhScreen { - private final #if MC_VER <= MC_1_12_2 GuiScreen #else Screen #endif parent; + private final Screen parent; private ConfigListWidget configListWidget; private AbstractScreen screen; - #if MC_VER <= MC_1_12_2 - public static net.minecraft.util.text.TextComponentTranslation translate(String str, Object... args) - { return new net.minecraft.util.text.TextComponentTranslation(str, args); } - #elif MC_VER < MC_1_19_2 + + #if MC_VER < MC_1_19_2 public static net.minecraft.network.chat.TranslatableComponent translate(String str, Object... args) { return new net.minecraft.network.chat.TranslatableComponent(str, args); } #else @@ -45,12 +44,10 @@ public class MinecraftScreen { return net.minecraft.network.chat.Component.translatable(str, args); } #endif - protected ConfigScreenRenderer(#if MC_VER <= MC_1_12_2 GuiScreen #else Screen #endif parent, AbstractScreen screen, String translationName) + protected ConfigScreenRenderer(Screen parent, AbstractScreen screen, String translationName) { super(translate(translationName)); - #if MC_VER <= MC_1_12_2 - screen.minecraftWindow = Display.getWindow(); - #elif MC_VER < MC_1_21_9 + #if MC_VER < MC_1_21_9 screen.minecraftWindow = Minecraft.getInstance().getWindow().getWindow(); #else screen.minecraftWindow = Minecraft.getInstance().getWindow().handle(); @@ -60,28 +57,18 @@ public class MinecraftScreen } @Override - #if MC_VER <= MC_1_12_2 - public void initGui() - #else protected void init() - #endif { - super.#if MC_VER <= MC_1_12_2 initGui(); #else init(); #endif // Init Minecraft's screen - #if MC_VER <= MC_1_12_2 - this.screen.width = Display.getWidth(); - this.screen.height = Display.getHeight(); - #else + super.init(); // Init Minecraft's screen Window mcWindow = this.minecraft.getWindow(); this.screen.width = mcWindow.getWidth(); this.screen.height = mcWindow.getHeight(); - #endif this.screen.scaledWidth = this.width; this.screen.scaledHeight = this.height; this.screen.init(); // Init our own config screen - this.configListWidget = new ConfigListWidget(#if MC_VER <= MC_1_12_2 this.mc #else this.minecraft #endif, this.width, this.height, 0, 0, 25); // Select the area to tint + this.configListWidget = new ConfigListWidget(this.minecraft, this.width, this.height, 0, 0, 25); // Select the area to tint - #if MC_VER > MC_1_12_2 #if MC_VER < MC_1_20_6 // no background is rendered in MC 1.20.6+ if (this.minecraft != null && this.minecraft.level != null) // Check if in game { @@ -90,21 +77,18 @@ public class MinecraftScreen #endif this.addWidget(this.configListWidget); // Add the tint to the things to be rendered - #endif } @Override - #if MC_VER <= MC_1_12_2 - public void drawScreen(int mouseX, int mouseY, float delta) - #elif MC_VER < MC_1_20_1 + #if MC_VER < MC_1_20_1 public void render(PoseStack matrices, int mouseX, int mouseY, float delta) - #else + #elif MC_VER <= MC_1_21_11 public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta) + #else + public void extractRenderState(GuiGraphicsExtractor matrices, int mouseX, int mouseY, float delta) #endif { - #if MC_VER <= MC_1_12_2 - //this.drawDefaultBackground(); // seems to do nothing - #elif MC_VER < MC_1_20_2 + #if MC_VER < MC_1_20_2 this.renderBackground(matrices); // Render background #elif MC_VER < MC_1_21_6 this.renderBackground(matrices, mouseX, mouseY, delta); // Render background @@ -112,91 +96,65 @@ public class MinecraftScreen // background blur is already being rendered, rendering again causes the game to crash #endif - #if MC_VER <= MC_1_12_2 - //this.configListWidget.drawScreen(mouseX, mouseY, delta); // seems to do nothing - #else + #if MC_VER <= MC_1_21_11 this.configListWidget.render(matrices, mouseX, mouseY, delta); // Renders the items in the render list (currently only used to tint background darker) + #else + this.configListWidget.extractRenderState(matrices, mouseX, mouseY, delta); // Renders the items in the render list (currently only used to tint background darker) #endif - + this.screen.mouseX = mouseX; this.screen.mouseY = mouseY; this.screen.render(delta); // Render everything on the main screen - #if MC_VER <= MC_1_12_2 - super.drawScreen(mouseX, mouseY, delta); - #else + #if MC_VER <= MC_1_21_11 super.render(matrices, mouseX, mouseY, delta); // Render the vanilla stuff (currently only used for the background and tint) + #else + super.extractRenderState(matrices, mouseX, mouseY, delta); // Renders the items in the render list (currently only used to tint background darker) #endif } - @Override #if MC_VER <= MC_1_21_10 - #if MC_VER <= MC_1_12_2 - public void setWorldAndResolution(Minecraft mc, int width, int height) - #else + @Override public void resize(Minecraft mc, int width, int height) - #endif #else + @Override public void resize(int width, int height) #endif { // Resize Minecraft's screen - #if MC_VER <= MC_1_12_2 - super.setWorldAndResolution(mc, width, height); - #elif MC_VER <= MC_1_21_10 + #if MC_VER <= MC_1_21_10 super.resize(mc, width, height); #else super.resize(width, height); #endif - #if MC_VER <= MC_1_12_2 - this.screen.width = Display.getWidth(); - this.screen.height = Display.getHeight(); - #else + Window mcWindow = this.minecraft.getWindow(); this.screen.width = mcWindow.getWidth(); this.screen.height = mcWindow.getHeight(); - #endif this.screen.scaledWidth = this.width; this.screen.scaledHeight = this.height; this.screen.onResize(); // Resize our screen } @Override - #if MC_VER <= MC_1_12_2 - public void updateScreen() - #else public void tick() - #endif { - super.#if MC_VER <= MC_1_12_2 updateScreen(); #else tick(); #endif // Tick Minecraft's screen + super.tick(); // Tick Minecraft's screen this.screen.tick(); // Tick our screen if (this.screen.close) // If we decide to close the screen, then actually close the screen { - #if MC_VER <= MC_1_12_2 - this.onGuiClosed(); - #else this.onClose(); - #endif } } @Override - #if MC_VER <= MC_1_12_2 - public void onGuiClosed() - #else public void onClose() - #endif { this.screen.onClose(); // Close our screen - #if MC_VER <= MC_1_12_2 - Objects.requireNonNull(this.mc).displayGuiScreen(this.parent); - #else Objects.requireNonNull(this.minecraft).setScreen(this.parent); // Goto the parent screen - #endif } - #if MC_VER > MC_1_12_2 @Override public void onFilesDrop(@NotNull List files) { this.screen.onFilesDrop(files); } @@ -205,10 +163,10 @@ public class MinecraftScreen @Override public boolean shouldCloseOnEsc() { return this.screen.shouldCloseOnEsc; } - #endif + } - public static class ConfigListWidget extends #if MC_VER <= MC_1_12_2 GuiListExtended #else ContainerObjectSelectionList #endif + public static class ConfigListWidget extends ContainerObjectSelectionList { public ConfigListWidget(Minecraft minecraftClient, int canvasWidth, int canvasHeight, int topMargin, int botMargin, int itemSpacing) { @@ -220,20 +178,6 @@ public class MinecraftScreen this.centerListVertically = false; } - #if MC_VER <= MC_1_12_2 - @Override - protected int getSize() - { - return 0; - } - @Override - public IGuiListEntry getListEntry(int index) - { - return null; - } - #endif - - } } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/NativeDialogUtil.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/NativeDialogUtil.java new file mode 100644 index 000000000..375d37cbb --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/NativeDialogUtil.java @@ -0,0 +1,38 @@ +package com.seibel.distanthorizons.common.wrappers.gui; + +import org.lwjgl.util.tinyfd.TinyFileDialogs; + +/** + * Should be used instead of the direct call to {@link TinyFileDialogs} + * so we can run additional validation and/or string cleanup. + * Otherwise, we may get error messages back.

+ * + * source: + * https://sourceforge.net/projects/tinyfiledialogs/ + * + * @see TinyFileDialogs + */ +public class NativeDialogUtil +{ + /** + * @param dialogType the dialog type. One of:
"ok""okcancel""yesno""yesnocancel"
+ * @param iconType the icon type. One of:
"info""warning""error""question"
+ */ + public static void showDialog(String title, String message, String dialogType, String iconType) + { + // Tinyfd doesn't support the following characters, attempting to display them will cause the message + // to be replaced with an error message + String unsafeCharsRegex = "['\"`]"; + + title = title.replaceAll(unsafeCharsRegex, ""); + message = message.replaceAll(unsafeCharsRegex, ""); + + #if MC_VER <= MC_1_21_11 + TinyFileDialogs.tinyfd_messageBox(title, message, dialogType, iconType, false); + #else + // https://mfbridge.github.io/tinyfiledialogs/reference/messageBox.html + TinyFileDialogs.tinyfd_messageBox(title, message, dialogType, iconType, 1 /* ok/yes */); + #endif + } + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/TexturedButtonWidget.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/TexturedButtonWidget.java index f914fefa6..f51f5fe3d 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/TexturedButtonWidget.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/TexturedButtonWidget.java @@ -18,20 +18,14 @@ */ package com.seibel.distanthorizons.common.wrappers.gui; -#if MC_VER > MC_1_12_2 + import net.minecraft.network.chat.Component; -#endif #if MC_VER >= MC_1_17_1 import net.minecraft.client.gui.components.Button; #endif -#if MC_VER <= MC_1_12_2 -import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiButton; -import net.minecraft.client.renderer.GlStateManager; -import net.minecraft.util.ResourceLocation; -#elif MC_VER < MC_1_17_1 +#if MC_VER < MC_1_17_1 import net.minecraft.client.gui.components.ImageButton; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.vertex.PoseStack; @@ -51,14 +45,15 @@ import net.minecraft.client.renderer.RenderType; import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.renderer.RenderType; import net.minecraft.client.renderer.RenderPipelines; -#else +#elif MC_VER <= MC_1_21_11 import net.minecraft.client.gui.GuiGraphics; import net.minecraft.client.renderer.RenderPipelines; +#else +import net.minecraft.client.gui.GuiGraphicsExtractor; +import net.minecraft.client.renderer.RenderPipelines; #endif -#if MC_VER <= MC_1_12_2 -import net.minecraft.util.ResourceLocation; -#elif MC_VER <= MC_1_21_10 +#if MC_VER <= MC_1_21_10 import net.minecraft.resources.ResourceLocation; #else import net.minecraft.resources.Identifier; @@ -71,14 +66,16 @@ import net.minecraft.resources.Identifier; * @version 2023-10-03 */ #if MC_VER < MC_1_20_2 -public class TexturedButtonWidget extends #if MC_VER <= MC_1_12_2 GuiButton #else ImageButton #endif +@SuppressWarnings("deprecation") // we use a few deprecated Mojang functions (as expected when running on old MC versions) +public class TexturedButtonWidget extends ImageButton #else +@SuppressWarnings("deprecation") // we use a few deprecated Mojang functions (as expected when running on old MC versions) public class TexturedButtonWidget extends Button #endif { public final boolean renderBackground; - #if MC_VER >= MC_1_20_2 || MC_VER <= MC_1_12_2 + #if MC_VER >= MC_1_20_2 private final int u; private final int v; private final int hoveredVOffset; @@ -95,40 +92,27 @@ public class TexturedButtonWidget extends Button public TexturedButtonWidget( - #if MC_VER <= MC_1_12_2 int id ,#endif int x, int y, int width, int height, int u, int v, int hoveredVOffset, + int x, int y, int width, int height, int u, int v, int hoveredVOffset, #if MC_VER <= MC_1_21_10 ResourceLocation textureResourceLocation, #else Identifier textureResourceLocation, #endif - int textureWidth, int textureHeight, #if MC_VER > MC_1_12_2 OnPress pressAction,#endif #if MC_VER <= MC_1_12_2 String #else Component #endif text) + int textureWidth, int textureHeight, OnPress pressAction, Component text) { - this( - #if MC_VER <= MC_1_12_2 - id, - #endif - x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, - #if MC_VER > MC_1_12_2 - pressAction, - #endif - text, true - ); + this(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text, true); } public TexturedButtonWidget( - #if MC_VER <= MC_1_12_2 int id ,#endif int x, int y, int width, int height, int u, int v, int hoveredVOffset, + int x, int y, int width, int height, int u, int v, int hoveredVOffset, #if MC_VER <= MC_1_21_10 ResourceLocation textureResourceLocation, #else Identifier textureResourceLocation, - #endif - int textureWidth, int textureHeight, #if MC_VER > MC_1_12_2 OnPress pressAction,#endif #if MC_VER <= MC_1_12_2 String #else Component #endif text, boolean renderBackground) + #endif + int textureWidth, int textureHeight, OnPress pressAction, Component text, + boolean renderBackground) { - #if MC_VER < MC_1_20_2 && MC_VER > MC_1_12_2 + #if MC_VER < MC_1_20_2 super(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text); #else - - #if MC_VER <= MC_1_12_2 - super(id, x, y, width, height, text); - #else // We don't pass in the text option since it will render (we normally pass it in for narration) super(x, y, width, height, Component.empty(), pressAction, DEFAULT_NARRATION); - #endif this.u = u; this.v = v; @@ -143,27 +127,7 @@ public class TexturedButtonWidget extends Button this.renderBackground = renderBackground; } - #if MC_VER <= MC_1_12_2 - @Override - public void drawButton(Minecraft mc, int mouseX, int mouseY, float partialTicks) { - if (this.visible) { - //Render vanilla background - mc.getTextureManager().bindTexture(BUTTON_TEXTURES); - GlStateManager.color(1.0F, 1.0F, 1.0F, 1.0F); - this.hovered = mouseX >= this.x && mouseY >= this.y && mouseX < this.x + this.width && mouseY < this.y + this.height; - int i = this.getHoverState(this.hovered); - GlStateManager.enableBlend(); - GlStateManager.tryBlendFuncSeparate(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA, GlStateManager.SourceFactor.ONE, GlStateManager.DestFactor.ZERO); - GlStateManager.blendFunc(GlStateManager.SourceFactor.SRC_ALPHA, GlStateManager.DestFactor.ONE_MINUS_SRC_ALPHA); - this.drawTexturedModalRect(this.x, this.y, 0, 46 + i * 20, this.width / 2, this.height); - this.drawTexturedModalRect(this.x + this.width / 2, this.y, 200 - this.width / 2, 46 + i * 20, this.width / 2, this.height); - - //Render DH texture - mc.getTextureManager().bindTexture(textureResourceLocation); - drawModalRectWithCustomSizedTexture(this.x, this.y, this.u, (hoveredVOffset * (i - 1)), this.width, this.height, this.textureWidth, this.textureHeight); - } - } - #elif MC_VER < MC_1_20_2 + #if MC_VER < MC_1_20_2 #if MC_VER < MC_1_19_4 @Override public void renderButton(PoseStack matrices, int mouseX, int mouseY, float delta) @@ -235,9 +199,12 @@ public class TexturedButtonWidget extends Button #if MC_VER < MC_1_21_11 @Override public void renderWidget(GuiGraphics matrices, int mouseX, int mouseY, float delta) - #else + #elif MC_VER <= MC_1_21_11 @Override protected void renderContents(GuiGraphics matrices, int mouseX, int mouseY, float delta) + #else + @Override + protected void extractContents(GuiGraphicsExtractor matrices, int mouseX, int mouseY, float delta) #endif { if (this.renderBackground) diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/classicConfig/ClassicConfigGUI.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/classicConfig/ClassicConfigGUI.java new file mode 100644 index 000000000..fad8baa43 --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/classicConfig/ClassicConfigGUI.java @@ -0,0 +1,384 @@ +package com.seibel.distanthorizons.common.wrappers.gui.classicConfig; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import com.seibel.distanthorizons.core.config.types.*; + +import com.seibel.distanthorizons.core.config.types.enums.EConfigCommentTextPosition; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.ContainerObjectSelectionList; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import com.seibel.distanthorizons.core.logging.DhLogger; +import org.jetbrains.annotations.NotNull; + + +#if MC_VER < MC_1_20_1 +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.GuiComponent; +#elif MC_VER <= MC_1_21_11 +import net.minecraft.client.gui.GuiGraphics; +#else +import net.minecraft.client.gui.GuiGraphicsExtractor; +#endif + +#if MC_VER >= MC_1_17_1 +import net.minecraft.client.gui.narration.NarratableEntry; +#endif + +#if MC_VER <= MC_1_21_10 +#else +import net.minecraft.resources.Identifier; +#endif + +import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*; + + +/* + * Based upon TinyConfig but is highly modified + * https://github.com/Minenash/TinyConfig + * + * @author coolGi + * @author Motschen + * @author James Seibel + * @version 5-21-2022 + */ +@SuppressWarnings("unchecked") +public class ClassicConfigGUI +{ + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + public static final DhLogger RATE_LIMITED_LOGGER = new DhLoggerBuilder() + .maxCountPerSecond(1) + .build(); + + public static final ConfigCoreInterface CONFIG_CORE_INTERFACE = new ConfigCoreInterface(); + + + + //==============// + // Initializers // + //==============// + + // Some regexes to check if an input is valid + public static final Pattern INTEGER_ONLY_REGEX = Pattern.compile("(-?[0-9]*)"); + public static final Pattern DECIMAL_ONLY_REGEX = Pattern.compile("-?([\\d]+\\.?[\\d]*|[\\d]*\\.?[\\d]+|\\.)"); + + public static class ConfigScreenConfigs + { + // This contains all the configs for the configs + public static final int SPACE_FROM_RIGHT_SCREEN = 10; + public static final int SPACE_BETWEEN_TEXT_AND_OPTION_FIELD = 8; + public static final int BUTTON_WIDTH_SPACING = 5; + public static final int RESET_BUTTON_WIDTH = 60; + public static final int RESET_BUTTON_HEIGHT = 20; + public static final int OPTION_FIELD_WIDTH = 150; + public static final int OPTION_FIELD_HEIGHT = 20; + public static final int CATEGORY_BUTTON_WIDTH = 200; + public static final int CATEGORY_BUTTON_HEIGHT = 20; + + } + + + + //==============// + // GUI handling // + //==============// + + /** if you want to get this config gui's screen call this */ + public static Screen getScreen(Screen parent, String category) + { return new DhConfigScreen(parent, category); } + + + + //================// + // helper classes // + //================// + + public static class ConfigListWidget extends ContainerObjectSelectionList + { + Font textRenderer; + + public ConfigListWidget(Minecraft minecraftClient, int canvasWidth, int canvasHeight, int topMargin, int botMargin, int itemSpacing) + { + #if MC_VER < MC_1_20_4 + super(minecraftClient, canvasWidth, canvasHeight, topMargin, canvasHeight - botMargin, itemSpacing); + #else + super(minecraftClient, canvasWidth, canvasHeight - (topMargin + botMargin), topMargin, itemSpacing); + #endif + + this.centerListVertically = false; + this.textRenderer = minecraftClient.font; + } + + public void addButton(DhConfigScreen gui, AbstractConfigBase dhConfigType, AbstractWidget button, AbstractWidget resetButton, AbstractWidget indexButton, Component text) + { this.addEntry(new DhButtonEntry(gui, dhConfigType, button, text, resetButton, indexButton)); } + + @Override + public int getRowWidth() { return 10_000; } + + public AbstractWidget getHoveredButton(double mouseX, double mouseY) + { + for (DhButtonEntry buttonEntry : this.children()) + { + AbstractWidget button = buttonEntry.button; + if (button != null + && button.visible) + { + #if MC_VER < MC_1_19_4 + double minX = button.x; + double minY = button.y; + #else + double minX = button.getX(); + double minY = button.getY(); + #endif + + double maxX = minX + button.getWidth(); + double maxY = minY + button.getHeight(); + + if (mouseX >= minX && mouseX < maxX + && mouseY >= minY && mouseY < maxY) + { + return button; + } + } + } + + return null; + } + + } + + + public static class DhButtonEntry extends ContainerObjectSelectionList.Entry + { + private static final Font textRenderer = Minecraft.getInstance().font; + + private final AbstractWidget button; + + private final DhConfigScreen gui; + + private final AbstractWidget resetButton; + private final AbstractWidget indexButton; + private final Component text; + private final List children = new ArrayList<>(); + + @NotNull + private final EConfigCommentTextPosition textPosition; + public final AbstractConfigBase dhConfigType; + + public static final Map TEXT_BY_WIDGET = new HashMap<>(); + public static final Map BUTTON_BY_WIDGET = new HashMap<>(); + + + + public DhButtonEntry( + DhConfigScreen gui, AbstractConfigBase dhConfigType, + AbstractWidget button, Component text, AbstractWidget resetButton, AbstractWidget indexButton) + { + TEXT_BY_WIDGET.put(button, text); + BUTTON_BY_WIDGET.put(button, this); + + this.gui = gui; + this.dhConfigType = dhConfigType; + + this.button = button; + this.resetButton = resetButton; + this.text = text; + this.indexButton = indexButton; + + if (button != null) { this.children.add(button); } + if (resetButton != null) { this.children.add(resetButton); } + if (indexButton != null) { this.children.add(indexButton); } + + + EConfigCommentTextPosition textPosition = null; + if (this.dhConfigType instanceof ConfigUIComment) + { + textPosition = ((ConfigUIComment)this.dhConfigType).textPosition; + } + + if (textPosition == null) + { + if (this.button != null) + { + // if a button is present + textPosition = EConfigCommentTextPosition.RIGHT_JUSTIFIED; + } + else + { + textPosition = EConfigCommentTextPosition.CENTERED_OVER_BUTTONS; + } + } + this.textPosition = textPosition; + + } + + + + @Override + #if MC_VER < MC_1_20_1 + public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) + #elif MC_VER < MC_1_21_9 + public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) + #elif MC_VER <= MC_1_21_11 + public void renderContent(GuiGraphics matrices, int mouseX, int mouseY, boolean hovered, float tickDelta) + #else + public void extractContent(GuiGraphicsExtractor matrices, int mouseX, int mouseY, boolean hovered, float tickDelta) + #endif + { + try + { + // setting the "y" variable is necessary so each child item + // renders at the correct height, + // if not set they will render off-screen. + #if MC_VER < MC_1_21_9 + // Y value passed in from method args + #else + int y = this.getY(); + #endif + + + + if (this.button != null) + { + SetY(this.button, y); + #if MC_VER <= MC_1_21_11 + this.button.render(matrices, mouseX, mouseY, tickDelta); + #else + this.button.extractRenderState(matrices, mouseX, mouseY, tickDelta); + #endif + } + + if (this.resetButton != null) + { + SetY(this.resetButton, y); + #if MC_VER <= MC_1_21_11 + this.resetButton.render(matrices, mouseX, mouseY, tickDelta); + #else + this.resetButton.extractRenderState(matrices, mouseX, mouseY, tickDelta); + #endif + } + + if (this.indexButton != null) + { + SetY(this.indexButton, y); + #if MC_VER <= MC_1_21_11 + this.indexButton.render(matrices, mouseX, mouseY, tickDelta); + #else + this.indexButton.extractRenderState(matrices, mouseX, mouseY, tickDelta); + #endif + } + + if (this.text != null) + { + int translatedLength = textRenderer.width(this.text); + + int textXPos; + if (this.textPosition == EConfigCommentTextPosition.RIGHT_JUSTIFIED) + { + // text right justified aligned against the buttons + textXPos = this.gui.width + - translatedLength + - ConfigScreenConfigs.SPACE_BETWEEN_TEXT_AND_OPTION_FIELD + - ConfigScreenConfigs.SPACE_FROM_RIGHT_SCREEN + - ConfigScreenConfigs.OPTION_FIELD_WIDTH + - ConfigScreenConfigs.BUTTON_WIDTH_SPACING + - ConfigScreenConfigs.RESET_BUTTON_WIDTH; + } + else if (this.textPosition == EConfigCommentTextPosition.CENTERED_OVER_BUTTONS) + { + // have button centered relative to a category button + textXPos = this.gui.width + - (translatedLength / 2) + - (ConfigScreenConfigs.CATEGORY_BUTTON_WIDTH / 2) + - ConfigScreenConfigs.SPACE_FROM_RIGHT_SCREEN; + } + else if (this.textPosition == EConfigCommentTextPosition.CENTER_OF_SCREEN) + { + // have button centered in the screen + textXPos = (this.gui.width / 2) + - (translatedLength / 2); + } + else + { + throw new UnsupportedOperationException("No text position render defined for [" + this.textPosition + "]"); + } + + + #if MC_VER < MC_1_20_1 + GuiComponent.drawString(matrices, textRenderer, + this.text, + textXPos, y + 5, + 0xFFFFFF); + #elif MC_VER < MC_1_21_6 + matrices.drawString(textRenderer, + this.text, + textXPos, y + 5, + 0xFFFFFF); + #elif MC_VER <= MC_1_21_11 + matrices.drawString(textRenderer, + this.text, + textXPos, y + 5, + 0xFFFFFFFF); + #else + matrices.text(textRenderer, + this.text, + textXPos, y + 5, + 0xFFFFFFFF); + #endif + } + } + catch (Exception e) + { + // should prevent crashing the game if there's an issue + RATE_LIMITED_LOGGER.error("Unexpected gui rendering issue: ["+e.getMessage()+"]", e); + } + } + + @Override + public @NotNull List children() + { return this.children; } + + #if MC_VER >= MC_1_17_1 + @Override + public @NotNull List narratables() + { return this.children; } + #endif + + + + } + + + + //================// + // event handling // + //================// + + public static class ConfigCoreInterface implements IConfigGui + { + /** + * in the future it would be good to pass in the current page and other variables, + * but for now just knowing when the page is closed is good enough + */ + public final ArrayList onScreenChangeListenerList = new ArrayList<>(); + + + + @Override + public void addOnScreenChangeListener(Runnable newListener) { this.onScreenChangeListenerList.add(newListener); } + @Override + public void removeOnScreenChangeListener(Runnable oldListener) { this.onScreenChangeListenerList.remove(oldListener); } + + } + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/classicConfig/DhConfigScreen.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/classicConfig/DhConfigScreen.java new file mode 100644 index 000000000..c5c4a110a --- /dev/null +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/classicConfig/DhConfigScreen.java @@ -0,0 +1,799 @@ +package com.seibel.distanthorizons.common.wrappers.gui.classicConfig; + +import java.util.AbstractMap; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.regex.Pattern; + +import com.seibel.distanthorizons.api.enums.config.DisallowSelectingViaConfigGui; +import com.seibel.distanthorizons.common.wrappers.gui.DhScreen; +import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget; +import com.seibel.distanthorizons.common.wrappers.gui.config.ConfigGuiInfo; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper; +import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.config.ConfigHandler; +import com.seibel.distanthorizons.core.config.types.*; +import com.seibel.distanthorizons.common.wrappers.gui.updater.ChangelogScreen; + +import com.seibel.distanthorizons.core.config.types.enums.EConfigCommentTextPosition; +import com.seibel.distanthorizons.core.config.types.enums.EConfigValidity; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; +import com.seibel.distanthorizons.core.jar.updater.SelfUpdater; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.util.AnnotationUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui; +import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper; +import com.seibel.distanthorizons.coreapi.ModInfo; +import net.minecraft.ChatFormatting; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.client.gui.components.AbstractWidget; +import net.minecraft.client.gui.components.Button; +import net.minecraft.client.gui.components.ContainerObjectSelectionList; +import net.minecraft.client.gui.components.EditBox; +import net.minecraft.client.gui.components.events.GuiEventListener; +import net.minecraft.client.gui.screens.Screen; +import net.minecraft.network.chat.Component; +import com.seibel.distanthorizons.core.logging.DhLogger; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + + +#if MC_VER < MC_1_20_1 +import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.GuiComponent; +#elif MC_VER <= MC_1_21_11 +import net.minecraft.client.gui.GuiGraphics; +#else +import net.minecraft.client.gui.GuiGraphicsExtractor; +#endif + +#if MC_VER >= MC_1_17_1 +import net.minecraft.client.gui.narration.NarratableEntry; +#endif + +#if MC_VER <= MC_1_21_10 +import net.minecraft.resources.ResourceLocation; +#else +import net.minecraft.resources.Identifier; +#endif + +import org.lwjgl.glfw.GLFW; +import com.mojang.blaze3d.platform.InputConstants; + +import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*; +import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.Translatable; + +class DhConfigScreen extends DhScreen +{ + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + private static final ILangWrapper LANG_WRAPPER = SingletonInjector.INSTANCE.get(ILangWrapper.class); + + private static final String TRANSLATION_PREFIX = ModInfo.ID + ".config."; + + private static final MinecraftClientWrapper MC_CLIENT = MinecraftClientWrapper.INSTANCE; + + + private final Screen parent; + private final String category; + private ClassicConfigGUI.ConfigListWidget configListWidget; + private boolean reload = false; + + private Button doneButton; + + + + //=============// + // constructor // + //=============// + + protected DhConfigScreen(Screen parent, String category) + { + super(Translatable( + LANG_WRAPPER.langExists(ModInfo.ID + ".config" + (category.isEmpty() ? "." + category : "") + ".title") ? + ModInfo.ID + ".config.title" : + ModInfo.ID + ".config" + (category.isEmpty() ? "" : "." + category) + ".title") + ); + this.parent = parent; + this.category = category; + } + + + @Override + public void tick() { super.tick(); } + + + + //==================// + // menu UI creation // + //==================// + + @Override + protected void init() + { + super.init(); + if (!this.reload) + { + ConfigHandler.INSTANCE.configFileHandler.loadFromFile(); + } + + // Changelog button + if (Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get() + // we only have changelogs for stable builds + && !ModInfo.IS_DEV_BUILD) + { + this.addBtn(new TexturedButtonWidget( + // Where the button is on the screen + this.width - 28, this.height - 28, + // Width and height of the button + 20, 20, + // texture UV Offset + 0, 0, + // Some texture stuff + 0, + #if MC_VER < MC_1_21_1 + new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"), + #elif MC_VER <= MC_1_21_10 + ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"), + #else + Identifier.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"), + #endif + 20, 20, + // Create the button and tell it where to go + (buttonWidget) -> { + ChangelogScreen changelogScreen = new ChangelogScreen(this); + if (changelogScreen.usable) + { + Objects.requireNonNull(this.minecraft).setScreen(changelogScreen); + } + else + { + LOGGER.warn("Changelog was not able to open"); + } + }, + // Add a title to the button + Translatable(ModInfo.ID + ".updater.title") + )); + } + + + // back button + this.addBtn(MakeBtn(Translatable("distanthorizons.general.back"), + (this.width / 2) - 154, this.height - 28, + ClassicConfigGUI.ConfigScreenConfigs.OPTION_FIELD_WIDTH, ClassicConfigGUI.ConfigScreenConfigs.OPTION_FIELD_HEIGHT, + (button) -> + { + ConfigHandler.INSTANCE.configFileHandler.loadFromFile(); + Objects.requireNonNull(this.minecraft).setScreen(this.parent); + })); + + // done/close button + this.doneButton = this.addBtn( + MakeBtn(Translatable("distanthorizons.general.done"), + (this.width / 2) + 4, this.height - 28, + ClassicConfigGUI.ConfigScreenConfigs.OPTION_FIELD_WIDTH, ClassicConfigGUI.ConfigScreenConfigs.OPTION_FIELD_HEIGHT, + (button) -> + { + ConfigHandler.INSTANCE.configFileHandler.saveToFile(); + Objects.requireNonNull(this.minecraft).setScreen(this.parent); + })); + + this.configListWidget = new ClassicConfigGUI.ConfigListWidget(this.minecraft, this.width * 2, this.height, 32, 32, 25); + + #if MC_VER < MC_1_20_6 // no background is rendered in MC 1.20.6+ + if (this.minecraft != null && this.minecraft.level != null) + { + this.configListWidget.setRenderBackground(false); + } + #endif + + this.addWidget(this.configListWidget); + + for (AbstractConfigBase configEntry : ConfigHandler.INSTANCE.configBaseList) + { + try + { + if (configEntry.getCategory().matches(this.category) + && configEntry.getAppearance().showInGui) + { + this.addMenuItem(configEntry); + } + } + catch (Exception e) + { + String message = "ERROR: Failed to show [" + configEntry.getNameAndCategory() + "], error: [" + e.getMessage() + "]"; + if (configEntry.get() != null) + { + message += " with the value [" + configEntry.get() + "] with type [" + configEntry.getType() + "]"; + } + + LOGGER.error(message, e); + } + } + + ClassicConfigGUI.CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run()); + } + private void addMenuItem(AbstractConfigBase configEntry) + { + trySetupConfigEntry(configEntry); + + if (this.tryCreateInputField(configEntry)) return; + if (this.tryCreateCategoryButton(configEntry)) return; + if (this.tryCreateButton(configEntry)) return; + if (this.tryCreateComment(configEntry)) return; + if (this.tryCreateSpacer(configEntry)) return; + if (this.tryCreateLinkedEntry(configEntry)) return; + + LOGGER.warn("Config [" + configEntry.getNameAndCategory() + "] failed to show. Please try something like changing its type."); + } + + private static void trySetupConfigEntry(AbstractConfigBase configMenuOption) + { + configMenuOption.guiValue = new ConfigGuiInfo(); + Class configValueClass = configMenuOption.getType(); + + if (configMenuOption instanceof ConfigEntry) + { + ConfigEntry configEntry = (ConfigEntry) configMenuOption; + + if (configValueClass == Integer.class) + { + setupTextMenuOption(configEntry, Integer::parseInt, ClassicConfigGUI.INTEGER_ONLY_REGEX, true); + } + else if (configValueClass == Double.class) + { + setupTextMenuOption(configEntry, Double::parseDouble, ClassicConfigGUI.DECIMAL_ONLY_REGEX, false); + } + else if (configValueClass == Float.class) + { + setupTextMenuOption(configEntry, Float::parseFloat, ClassicConfigGUI.DECIMAL_ONLY_REGEX, false); + } + else if (configValueClass == String.class || configValueClass == List.class) + { + // For string or list + setupTextMenuOption(configEntry, String::length, null, true); + } + else if (configValueClass == Boolean.class) + { + ConfigEntry booleanConfigEntry = (ConfigEntry) configEntry; + setupBooleanMenuOption(booleanConfigEntry); + } + else if (configValueClass.isEnum()) + { + ConfigEntry> enumConfigEntry = (ConfigEntry>) configEntry; + Class> configEnumClass = (Class>) configValueClass; + setupEnumMenuOption(enumConfigEntry, configEnumClass); + } + else + { + LOGGER.error("No definition for config with type: [" + configValueClass.getName() + "], for config: [" + configMenuOption.name + "]."); + } + } + + } + private static void setupTextMenuOption(AbstractConfigBase configMenuOption, Function parsingFunc, @Nullable Pattern pattern, boolean cast) + { + final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) configMenuOption.guiValue); + + configGuiInfo.tooltipFunction = + (editBox, button) -> + (stringValue) -> + { + boolean isNumber = (pattern != null); + + stringValue = stringValue.trim(); + if (!(stringValue.isEmpty() || !isNumber || pattern.matcher(stringValue).matches())) + { + return false; + } + + + Number numberValue = configMenuOption.typeIsFloatingPointNumber() ? 0.0 : 0; // different default values are needed so implicit casting works correctly (if not done casting from 0 (an int) to a double will cause an exception) + configGuiInfo.errorMessage = null; + if (isNumber + && !stringValue.isEmpty() + && !stringValue.equals("-") + && !stringValue.equals(".")) + { + ConfigEntry numberConfigEntry = (ConfigEntry) configMenuOption; + + try + { + numberValue = parsingFunc.apply(stringValue); + } + catch (Exception e) + { + numberValue = null; + } + + EConfigValidity validity = numberConfigEntry.getValidity(numberValue); + switch (validity) + { + case VALID: + configGuiInfo.errorMessage = null; + break; + case NUMBER_TOO_LOW: + configGuiInfo.errorMessage = TextOrTranslatable("§cMinimum length is " + numberConfigEntry.getMin()); + break; + case NUMBER_TOO_HIGH: + configGuiInfo.errorMessage = TextOrTranslatable("§cMaximum length is " + numberConfigEntry.getMax()); + break; + case INVALID: + configGuiInfo.errorMessage = TextOrTranslatable("§cValue is invalid"); + break; + } + } + + editBox.setTextColor(((ConfigEntry) configMenuOption).getValidity(numberValue) == EConfigValidity.VALID ? 0xFFFFFFFF : 0xFFFF7777); // white and red + + + if (configMenuOption.getType() == String.class + || configMenuOption.getType() == List.class) + { + ((ConfigEntry) configMenuOption).uiSetWithoutSaving(stringValue); + } + else if (((ConfigEntry) configMenuOption).getValidity(numberValue) == EConfigValidity.VALID) + { + if (!cast) + { + ((ConfigEntry) configMenuOption).uiSetWithoutSaving(numberValue); + } + else + { + ((ConfigEntry) configMenuOption).uiSetWithoutSaving(numberValue != null ? numberValue.intValue() : 0); + } + } + + return true; + }; + } + private static void setupBooleanMenuOption(ConfigEntry booleanConfigEntry) + { + // For boolean + Function func = value -> Translatable("distanthorizons.general." + ((Boolean) value ? "true" : "false")).withStyle((Boolean) value ? ChatFormatting.GREEN : ChatFormatting.RED); + + final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) booleanConfigEntry.guiValue); + + configGuiInfo.buttonOptionMap = + new AbstractMap.SimpleEntry>( + (button) -> + { + button.active = !booleanConfigEntry.apiIsOverriding(); + + booleanConfigEntry.uiSetWithoutSaving(!booleanConfigEntry.get()); + button.setMessage(func.apply(booleanConfigEntry.get())); + }, func); + } + private static void setupEnumMenuOption(ConfigEntry> enumConfigEntry, Class> enumClass) + { + List> enumList = Arrays.asList(enumClass.getEnumConstants()); + + final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) enumConfigEntry.guiValue); + + Function getEnumTranslatableFunc = (value) -> Translatable(TRANSLATION_PREFIX + "enum." + enumClass.getSimpleName() + "." + enumConfigEntry.get().toString()); + configGuiInfo.buttonOptionMap = + new AbstractMap.SimpleEntry>( + (button) -> + { + // get the currently selected enum and enum index + int startingIndex = enumList.indexOf(enumConfigEntry.get()); + Enum enumValue = enumList.get(startingIndex); + + boolean shiftPressed = + InputConstants.isKeyDown(MC_CLIENT.getGlfwWindowId(), GLFW.GLFW_KEY_LEFT_SHIFT) + || InputConstants.isKeyDown(MC_CLIENT.getGlfwWindowId(), GLFW.GLFW_KEY_RIGHT_SHIFT); + + + + // move forward or backwards depending on if the shift key is pressed + int index = shiftPressed ? startingIndex - 1 : startingIndex + 1; + + // wrap around to the other side of the array when necessary + if (index >= enumList.size()) + { + index = 0; + } + else if (index < 0) + { + index = enumList.size() - 1; + } + + + // walk through the enums to find the next selectable one + while (index != startingIndex) + { + enumValue = enumList.get(index); + if (!AnnotationUtil.doesEnumHaveAnnotation(enumValue, DisallowSelectingViaConfigGui.class)) + { + // this enum shouldn't be selectable via the UI, + // skip it + break; + } + + // move forward or backwards depending on if the shift key is pressed + index = shiftPressed ? index - 1 : index + 1; + + // wrap around to the other side of the array when necessary + if (index >= enumList.size()) + { + index = 0; + } + else if (index < 0) + { + index = enumList.size() - 1; + } + } + + + if (index == startingIndex) + { + // one of the enums should be selectable, this is a programmer error + enumValue = enumList.get(startingIndex); + LOGGER.warn("Enum [" + enumValue.getClass() + "] doesn't contain any values that should be selectable via the UI, sticking to the currently selected value [" + enumValue + "]."); + } + + + enumConfigEntry.uiSetWithoutSaving(enumValue); + + button.active = !enumConfigEntry.apiIsOverriding(); + + button.setMessage(getEnumTranslatableFunc.apply(enumConfigEntry.get())); + }, getEnumTranslatableFunc); + } + + private boolean tryCreateInputField(AbstractConfigBase configBase) + { + final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) configBase.guiValue); + + if (configBase instanceof ConfigEntry) + { + ConfigEntry configEntry = (ConfigEntry) configBase; + + + //==============// + // reset button // + //==============// + + Button.OnPress btnAction = (button) -> + { + configEntry.uiSetWithoutSaving(configEntry.getDefaultValue()); + this.reload = true; + Objects.requireNonNull(this.minecraft).setScreen(this); + }; + + int resetButtonPosX = this.width + - ClassicConfigGUI.ConfigScreenConfigs.RESET_BUTTON_WIDTH + - ClassicConfigGUI.ConfigScreenConfigs.SPACE_FROM_RIGHT_SCREEN; + int resetButtonPosZ = 0; + + Button resetButton = MakeBtn( + Translatable("distanthorizons.general.reset").withStyle(ChatFormatting.RED), + resetButtonPosX, resetButtonPosZ, + ClassicConfigGUI.ConfigScreenConfigs.RESET_BUTTON_WIDTH, ClassicConfigGUI.ConfigScreenConfigs.RESET_BUTTON_HEIGHT, + btnAction); + + if (configEntry.apiIsOverriding()) + { + resetButton.active = false; + resetButton.setMessage(Translatable("distanthorizons.general.apiOverride").withStyle(ChatFormatting.DARK_GRAY)); + } + else + { + resetButton.active = true; + } + + + + //==============// + // option field // + //==============// + + Component textComponent = this.GetTranslatableTextComponentForConfig(configEntry); + + int optionFieldPosX = this.width + - ClassicConfigGUI.ConfigScreenConfigs.SPACE_FROM_RIGHT_SCREEN + - ClassicConfigGUI.ConfigScreenConfigs.RESET_BUTTON_WIDTH + - ClassicConfigGUI.ConfigScreenConfigs.BUTTON_WIDTH_SPACING + - ClassicConfigGUI.ConfigScreenConfigs.OPTION_FIELD_WIDTH; + int optionFieldPosZ = 0; + + if (configGuiInfo.buttonOptionMap != null) + { + // enum/multi option input button + + Map.Entry> widget = configGuiInfo.buttonOptionMap; + if (configEntry.getType().isEnum()) + { + widget.setValue((value) -> Translatable(TRANSLATION_PREFIX + "enum." + configEntry.getType().getSimpleName() + "." + configEntry.get().toString())); + } + + Button button = MakeBtn( + widget.getValue().apply(configEntry.get()), + optionFieldPosX, optionFieldPosZ, + ClassicConfigGUI.ConfigScreenConfigs.OPTION_FIELD_WIDTH, ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT, + widget.getKey()); + + // deactivate the button if the API is overriding it + button.active = !configEntry.apiIsOverriding(); + + + this.configListWidget.addButton(this, configEntry, + button, + resetButton, + null, + textComponent); + + return true; + } + else + { + // text box input + + EditBox widget = new EditBox(this.font, + optionFieldPosX, optionFieldPosZ, + ClassicConfigGUI.ConfigScreenConfigs.OPTION_FIELD_WIDTH - 4, ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT, + Translatable("")); + widget.setMaxLength(3_000_000); // hopefully 3 million characters should be enough for any normal use-case, lol + widget.insertText(String.valueOf(configEntry.get())); + + Predicate processor = configGuiInfo.tooltipFunction.apply(widget, this.doneButton); + #if MC_VER <= MC_1_21_11 + widget.setFilter(processor); + #else + widget.setResponder(processor::test); + #endif + + this.configListWidget.addButton(this, configEntry, widget, resetButton, null, textComponent); + + return true; + } + } + + return false; + } + private boolean tryCreateCategoryButton(AbstractConfigBase configType) + { + if (configType instanceof ConfigCategory) + { + ConfigCategory configCategory = (ConfigCategory) configType; + + Component textComponent = this.GetTranslatableTextComponentForConfig(configCategory); + + int categoryPosX = this.width - ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_WIDTH - ClassicConfigGUI.ConfigScreenConfigs.SPACE_FROM_RIGHT_SCREEN; + int categoryPosZ = this.height - ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT; // Note: the posZ value here seems to be ignored + + Button widget = MakeBtn(textComponent, + categoryPosX, categoryPosZ, + ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_WIDTH, ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT, + ((button) -> + { + ConfigHandler.INSTANCE.configFileHandler.saveToFile(); + Objects.requireNonNull(this.minecraft).setScreen(ClassicConfigGUI.getScreen(this, configCategory.getDestination())); + })); + this.configListWidget.addButton(this, configType, widget, null, null, null); + + return true; + } + + return false; + } + private boolean tryCreateButton(AbstractConfigBase configType) + { + if (configType instanceof ConfigUIButton) + { + ConfigUIButton configUiButton = (ConfigUIButton) configType; + + Component textComponent = this.GetTranslatableTextComponentForConfig(configUiButton); + + int buttonPosX = this.width - ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_WIDTH - ClassicConfigGUI.ConfigScreenConfigs.SPACE_FROM_RIGHT_SCREEN; + + Button widget = MakeBtn(textComponent, + buttonPosX, this.height - 28, + ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_WIDTH, ClassicConfigGUI.ConfigScreenConfigs.CATEGORY_BUTTON_HEIGHT, + (button) -> ((ConfigUIButton) configType).runAction()); + this.configListWidget.addButton(this, configType, widget, null, null, null); + + return true; + } + + return false; + } + private boolean tryCreateComment(AbstractConfigBase configType) + { + if (configType instanceof ConfigUIComment) + { + ConfigUIComment configUiComment = (ConfigUIComment) configType; + + Component textComponent = this.GetTranslatableTextComponentForConfig(configUiComment); + if (configUiComment.parentConfigPath != null) + { + textComponent = Translatable(TRANSLATION_PREFIX + configUiComment.parentConfigPath); + } + + this.configListWidget.addButton(this, configType, null, null, null, textComponent); + + return true; + } + + return false; + } + private boolean tryCreateSpacer(AbstractConfigBase configType) + { + if (configType instanceof ConfigUISpacer) + { + Button spacerButton = MakeBtn(Translatable("distanthorizons.general.spacer"), + 10, 10, // having too small of a size causes division by 0 errors in older MC versions (IE 1.20.1) + 1, 1, + (button) -> { }); + + spacerButton.visible = false; + this.configListWidget.addButton(this, configType, spacerButton, null, null, null); + + return true; + } + + return false; + } + private boolean tryCreateLinkedEntry(AbstractConfigBase configType) + { + if (configType instanceof ConfigUiLinkedEntry) + { + this.addMenuItem(((ConfigUiLinkedEntry) configType).get()); + + return true; + } + + return false; + } + + private Component GetTranslatableTextComponentForConfig(AbstractConfigBase configType) + { return Translatable(TRANSLATION_PREFIX + configType.getNameAndCategory()); } + + + + //===========// + // rendering // + //===========// + + @Override +#if MC_VER < MC_1_20_1 + public void render(PoseStack matrices, int mouseX, int mouseY, float delta) +#elif MC_VER <= MC_1_21_11 + public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta) +#else + public void extractRenderState(GuiGraphicsExtractor matrices, int mouseX, int mouseY, float delta) + #endif + { + #if MC_VER < MC_1_20_2 // 1.20.2 now enables this by default in the `this.list.render` function + this.renderBackground(matrices); + #elif MC_VER <= MC_1_21_11 + super.render(matrices, mouseX, mouseY, delta); + #else + super.extractRenderState(matrices, mouseX, mouseY, delta); + #endif + + // Render buttons + #if MC_VER <= MC_1_21_11 + this.configListWidget.render(matrices, mouseX, mouseY, delta); + #else + this.configListWidget.extractRenderState(matrices, mouseX, mouseY, delta); + #endif + + + // Render config title + this.DhDrawCenteredString(matrices, this.font, this.title, + this.width / 2, 15, + #if MC_VER < MC_1_21_6 + 0xFFFFFF // RGB white + #else + 0xFFFFFFFF // ARGB white + #endif ); + + + // render DH version + this.DhDrawString(matrices, this.font, TextOrLiteral(ModInfo.VERSION), 2, this.height - 10, + #if MC_VER < MC_1_21_6 + 0xAAAAAA // RGB white + #else + 0xFFAAAAAA // ARGB white + #endif ); + + // If the update is pending, display this message to inform the user that it will apply when the game restarts + if (SelfUpdater.deleteOldJarOnJvmShutdown) + { + this.DhDrawString(matrices, this.font, Translatable(ModInfo.ID + ".updater.waitingForClose"), 4, this.height - 42, + #if MC_VER < MC_1_21_6 + 0xFFFFFF // RGB white + #else + 0xFFFFFFFF // ARGB white + #endif ); + } + + + this.renderTooltip(matrices, mouseX, mouseY, delta); + + #if MC_VER < MC_1_20_2 + super.render(matrices, mouseX, mouseY, delta); + #endif + } + + #if MC_VER < MC_1_20_1 + private void renderTooltip(PoseStack matrices, int mouseX, int mouseY, float delta) + #elif MC_VER <= MC_1_21_11 + private void renderTooltip(GuiGraphics matrices, int mouseX, int mouseY, float delta) +#else + private void renderTooltip(GuiGraphicsExtractor matrices, int mouseX, int mouseY, float delta) + #endif + { + AbstractWidget hoveredWidget = this.configListWidget.getHoveredButton(mouseX, mouseY); + if (hoveredWidget == null) + { + return; + } + + + ClassicConfigGUI.DhButtonEntry button = ClassicConfigGUI.DhButtonEntry.BUTTON_BY_WIDGET.get(hoveredWidget); + + + // A quick fix for tooltips on linked entries + AbstractConfigBase configBase = ConfigUiLinkedEntry.class.isAssignableFrom(button.dhConfigType.getClass()) ? + ((ConfigUiLinkedEntry) button.dhConfigType).get() : + button.dhConfigType; + + boolean apiOverrideActive = false; + if (configBase instanceof ConfigEntry) + { + apiOverrideActive = ((ConfigEntry) configBase).apiIsOverriding(); + } + + String key = TRANSLATION_PREFIX + (configBase.category.isEmpty() ? "" : configBase.category + ".") + configBase.getName() + ".@tooltip"; + + if (apiOverrideActive) + { + key = "distanthorizons.general.disabledByApi.@tooltip"; + } + + // display the validation error tooltip if present + final ConfigGuiInfo configGuiInfo = ((ConfigGuiInfo) configBase.guiValue); + if (configGuiInfo.errorMessage != null) + { + this.DhRenderTooltip(matrices, this.font, configGuiInfo.errorMessage, mouseX, mouseY); + } + // display the tooltip if present + else if (LANG_WRAPPER.langExists(key)) + { + List list = new ArrayList<>(); + String lang = LANG_WRAPPER.getLang(key); + for (String langLine : lang.split("\n")) + { + list.add(TextOrTranslatable(langLine)); + } + + this.DhRenderComponentTooltip(matrices, this.font, list, mouseX, mouseY); + } + } + + + + //==========// + // shutdown // + //==========// + + /** When you close it, it goes to the previous screen and saves */ + @Override + public void onClose() + { + ConfigHandler.INSTANCE.configFileHandler.saveToFile(); + Objects.requireNonNull(this.minecraft).setScreen(this.parent); + + ClassicConfigGUI.CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run()); + } + + +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/updater/ChangelogScreen.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/updater/ChangelogScreen.java index 54f0e3409..9f931db62 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/updater/ChangelogScreen.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/updater/ChangelogScreen.java @@ -23,8 +23,10 @@ import net.minecraft.client.gui.narration.NarratableEntry; #if MC_VER < MC_1_20_1 import net.minecraft.client.gui.GuiComponent; import com.mojang.blaze3d.vertex.PoseStack; -#else +#elif MC_VER <= MC_1_21_11 import net.minecraft.client.gui.GuiGraphics; +#else +import net.minecraft.client.gui.GuiGraphicsExtractor; #endif @@ -168,8 +170,10 @@ public class ChangelogScreen extends DhScreen @Override #if MC_VER < MC_1_20_1 public void render(PoseStack matrices, int mouseX, int mouseY, float delta) - #else + #elif MC_VER <= MC_1_21_11 public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta) + #else + public void extractRenderState(GuiGraphicsExtractor matrices, int mouseX, int mouseY, float delta) #endif { #if MC_VER < MC_1_20_2 @@ -206,8 +210,14 @@ public class ChangelogScreen extends DhScreen // render order matters, otherwise on 1.20.6+ the blurred background will render on top of the text + #if MC_VER <= MC_1_21_11 super.render(matrices, mouseX, mouseY, delta); // Render the buttons this.changelogArea.render(matrices, mouseX, mouseY, delta); // Render the changelog + #else + super.extractRenderState(matrices, mouseX, mouseY, delta); // Render the buttons + this.changelogArea.extractRenderState(matrices, mouseX, mouseY, delta); // Render the changelog + #endif + this.DhDrawCenteredString(matrices, this.font, this.title, this.width / 2, 15, 0xFFFFFF); // Render title } @@ -264,10 +274,14 @@ public class ChangelogScreen extends DhScreen @Override public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta) { matrices.drawString(textRenderer, this.text, 12, y + 5, 0xFFFFFF); } - #else + #elif MC_VER <= MC_1_21_11 @Override public void renderContent(GuiGraphics matrices, int y, int x, boolean hovered, float tickDelta) { matrices.drawString(textRenderer, this.text, 12, y + 5, 0xFFFFFF); } + #else + @Override + public void extractContent(GuiGraphicsExtractor matrices, int y, int x, boolean hovered, float tickDelta) + { matrices.text(textRenderer, this.text, 12, y + 5, 0xFFFFFF); } #endif @Override diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/updater/UpdateModScreen.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/updater/UpdateModScreen.java index b87d3e01a..d254c0601 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/updater/UpdateModScreen.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/gui/updater/UpdateModScreen.java @@ -13,10 +13,12 @@ import com.seibel.distanthorizons.core.logging.DhLogger; import net.minecraft.client.gui.screens.Screen; -#if MC_VER >= MC_1_20_1 +#if MC_VER < MC_1_20_1 +import com.mojang.blaze3d.vertex.PoseStack; +#elif MC_VER <= MC_1_21_11 import net.minecraft.client.gui.GuiGraphics; #else -import com.mojang.blaze3d.vertex.PoseStack; +import net.minecraft.client.gui.GuiGraphicsExtractor; #endif #if MC_VER <= MC_1_21_10 @@ -172,8 +174,10 @@ public class UpdateModScreen extends DhScreen @Override #if MC_VER < MC_1_20_1 public void render(PoseStack matrices, int mouseX, int mouseY, float delta) - #else + #elif MC_VER <= MC_1_21_11 public void render(GuiGraphics matrices, int mouseX, int mouseY, float delta) + #else + public void extractRenderState(GuiGraphicsExtractor matrices, int mouseX, int mouseY, float delta) #endif { #if MC_VER < MC_1_20_2 @@ -184,8 +188,12 @@ public class UpdateModScreen extends DhScreen // background blur is already being rendered, rendering again causes the game to crash #endif + #if MC_VER <= MC_1_21_11 super.render(matrices, mouseX, mouseY, delta); // Render the buttons - + #else + super.extractRenderState(matrices, mouseX, mouseY, delta); + #endif + // Render the text's this.DhDrawCenteredString(matrices, this.font, Translatable(ModInfo.ID + ".updater.text1"), diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftClientWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftClientWrapper.java index 43a49f5f6..3b56dc920 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftClientWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftClientWrapper.java @@ -21,6 +21,8 @@ package com.seibel.distanthorizons.common.wrappers.minecraft; import java.io.File; +import com.mojang.blaze3d.platform.Window; +import com.seibel.distanthorizons.common.wrappers.gui.NativeDialogUtil; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; import com.seibel.distanthorizons.core.render.RenderThreadTaskHandler; @@ -34,30 +36,19 @@ import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.logging.DhLogger; -import net.minecraft.client.Minecraft; -import net.minecraft.client.multiplayer.ServerData; -#if MC_VER <= MC_1_12_2 -import net.minecraft.client.entity.EntityPlayerSP; -import net.minecraft.client.multiplayer.WorldClient; -import net.minecraft.crash.CrashReport; -import net.minecraft.profiler.Profiler; -import net.minecraft.util.math.BlockPos; -import net.minecraft.util.math.ChunkPos; -import net.minecraft.util.text.TextComponentString; -#else import net.minecraft.CrashReport; import net.minecraft.client.CloudStatus; +import net.minecraft.client.Minecraft; import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.multiplayer.ServerData; import net.minecraft.client.player.LocalPlayer; import net.minecraft.core.BlockPos; import net.minecraft.util.profiling.ProfilerFiller; import net.minecraft.world.level.ChunkPos; -import com.mojang.blaze3d.platform.Window; -#endif import org.jetbrains.annotations.Nullable; -#if MC_VER < MC_1_19_2 && MC_VER > MC_1_12_2 +#if MC_VER < MC_1_19_2 import net.minecraft.network.chat.TextComponent; #endif @@ -66,7 +57,7 @@ import net.minecraft.network.chat.TextComponent; import net.minecraft.util.profiling.Profiler; #endif -#if MC_VER <= MC_1_21_10 && MC_VER > MC_1_12_2 +#if MC_VER <= MC_1_21_10 import net.minecraft.client.GraphicsStatus; #else #endif @@ -79,7 +70,7 @@ import net.minecraft.client.GraphicsStatus; public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecraftSharedWrapper { private static final DhLogger LOGGER = new DhLoggerBuilder().build(); - private static final Minecraft MINECRAFT = Minecraft.#if MC_VER <= MC_1_12_2 getMinecraft() #else getInstance() #endif; + private static final Minecraft MINECRAFT = Minecraft.getInstance(); public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper(); @@ -94,17 +85,17 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra //region @Override - public boolean hasSinglePlayerServer() { return MINECRAFT.#if MC_VER <= MC_1_12_2 isSingleplayer() #else hasSingleplayerServer() #endif; } + public boolean hasSinglePlayerServer() { return MINECRAFT.hasSingleplayerServer(); } @Override public boolean clientConnectedToDedicatedServer() { - return MINECRAFT.#if MC_VER <= MC_1_12_2 getCurrentServerData() #else getCurrentServer() #endif != null + return MINECRAFT.getCurrentServer() != null && !this.hasSinglePlayerServer(); } @Override public boolean connectedToReplay() { - return MINECRAFT.#if MC_VER <= MC_1_12_2 getCurrentServerData() #else getCurrentServer() #endif == null + return MINECRAFT.getCurrentServer() == null && !this.hasSinglePlayerServer() ; } @@ -117,8 +108,8 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra } else { - ServerData server = MINECRAFT.#if MC_VER <= MC_1_12_2 getCurrentServerData() #else getCurrentServer() #endif; - return (server != null) ? server.#if MC_VER <= MC_1_12_2 serverName #else name #endif : "NULL"; + ServerData server = MINECRAFT.getCurrentServer(); + return (server != null) ? server.name : "NULL"; } } @Override @@ -130,15 +121,15 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra } else { - ServerData server = MINECRAFT.#if MC_VER <= MC_1_12_2 getCurrentServerData() #else getCurrentServer() #endif; - return (server != null) ? server.#if MC_VER <= MC_1_12_2 serverIP #else ip #endif : "NA"; + ServerData server = MINECRAFT.getCurrentServer(); + return (server != null) ? server.ip : "NA"; } } @Override public String getCurrentServerVersion() { - ServerData server = MINECRAFT.#if MC_VER <= MC_1_12_2 getCurrentServerData() #else getCurrentServer() #endif; - return (server != null) ? server.#if MC_VER <= MC_1_12_2 gameVersion #else version.getString() #endif : "UNKOWN"; + ServerData server = MINECRAFT.getCurrentServer(); + return (server != null) ? server.version.getString() : "UNKOWN"; } //endregion @@ -150,7 +141,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra //=================// //region - public #if MC_VER <= MC_1_12_2 EntityPlayerSP #else LocalPlayer #endif getPlayer() { return MINECRAFT.player; } + public LocalPlayer getPlayer() { return MINECRAFT.player; } @Override public boolean playerExists() { return MINECRAFT.player != null; } @@ -158,33 +149,36 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra @Override public DhBlockPos getPlayerBlockPos() { - #if MC_VER <= MC_1_12_2 EntityPlayerSP #else LocalPlayer #endif player = this.getPlayer(); + LocalPlayer player = this.getPlayer(); if (player == null) { return new DhBlockPos(0, 0, 0); } - BlockPos playerPos = player.#if MC_VER <= MC_1_12_2 getPosition() #else blockPosition() #endif; + BlockPos playerPos = player.blockPosition(); return new DhBlockPos(playerPos.getX(), playerPos.getY(), playerPos.getZ()); } @Override public DhChunkPos getPlayerChunkPos() { - #if MC_VER <= MC_1_12_2 EntityPlayerSP #else LocalPlayer #endif player = this.getPlayer(); + LocalPlayer player = this.getPlayer(); if (player == null) { return new DhChunkPos(0, 0); } - #if MC_VER <= MC_1_12_2 - ChunkPos playerPos = new ChunkPos(player.getPosition()); - #elif MC_VER < MC_1_17_1 + #if MC_VER < MC_1_17_1 ChunkPos playerPos = new ChunkPos(player.blockPosition()); #else ChunkPos playerPos = player.chunkPosition(); #endif + + #if MC_VER <= MC_1_21_11 return new DhChunkPos(playerPos.x, playerPos.z); + #else + return new DhChunkPos(playerPos.x(), playerPos.z()); + #endif } //endregion @@ -204,7 +198,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra @Nullable public IClientLevelWrapper getWrappedClientLevel(boolean bypassLevelKeyManager) { - #if MC_VER <= MC_1_12_2 WorldClient #else ClientLevel #endif level = MINECRAFT.#if MC_VER <= MC_1_12_2 world #else level #endif; + ClientLevel level = MINECRAFT.level; if (level == null) { return null; @@ -225,15 +219,13 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra @Override public void sendChatMessage(String string) { - #if MC_VER <= MC_1_12_2 EntityPlayerSP #else LocalPlayer #endif player = this.getPlayer(); + LocalPlayer player = this.getPlayer(); if (player == null) { return; } - #if MC_VER <= MC_1_12_2 - player.sendMessage(new TextComponentString(string)); - #elif MC_VER < MC_1_19_2 + #if MC_VER < MC_1_19_2 player.sendMessage(new TextComponent(string), getPlayer().getUUID()); #elif MC_VER < MC_1_21_9 player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false); @@ -241,7 +233,11 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra RenderThreadTaskHandler.INSTANCE.queueRunningOnRenderThread("MinecraftClientWrapper sendChatMessage", () -> { + #if MC_VER <= MC_1_21_11 player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false); + #else + player.sendSystemMessage(net.minecraft.network.chat.Component.translatable(string)); + #endif }); #endif } @@ -249,18 +245,18 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra @Override public void sendOverlayMessage(String string) { - #if MC_VER <= MC_1_12_2 EntityPlayerSP #else LocalPlayer #endif player = this.getPlayer(); + LocalPlayer player = this.getPlayer(); if (player == null) { return; } - #if MC_VER <= MC_1_12_2 - MINECRAFT.ingameGUI.setOverlayMessage(string, /*animateColor*/false); - #elif MC_VER < MC_1_19_2 + #if MC_VER < MC_1_19_2 player.displayClientMessage(new TextComponent(string), /*isOverlay*/true); - #else + #elif MC_VER <= MC_1_21_11 player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/true); + #else + player.sendOverlayMessage(net.minecraft.network.chat.Component.translatable(string)); #endif } @@ -277,9 +273,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra { LOGGER.info("Disabling vanilla clouds... This is done to prevent vanilla clouds from rendering on top of Distant Horizons LODs."); - #if MC_VER <= MC_1_12_2 - MINECRAFT.gameSettings.clouds = 0; - #elif MC_VER <= MC_1_18_2 + #if MC_VER <= MC_1_18_2 MINECRAFT.options.renderClouds = CloudStatus.OFF; #else MINECRAFT.options.cloudStatus().set(CloudStatus.OFF); @@ -300,9 +294,8 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra public void disableFabulousTransparency() { String reasoning = "This is done to fix vanilla chunks (specifically water blocks) not fading into Distant Horizons LODs when DH's 'Vanilla Fade' option is enabled."; - #if MC_VER <= MC_1_12_2 - // fabulous graphics was added in MC 1.16 - #elif MC_VER <= MC_1_18_2 + + #if MC_VER <= MC_1_18_2 LOGGER.info("Disabling fabulous graphics... "+reasoning); GraphicsStatus oldGraphicsStatus = MINECRAFT.options.graphicsMode; @@ -338,7 +331,6 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra * no override and not included in {@link IMinecraftClientWrapper} * since this would only be used in common/client, not core. */ - #if MC_VER > MC_1_12_2 public #if MC_VER < MC_1_21_9 long #else Window @@ -352,15 +344,12 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra return MINECRAFT.getWindow(); #endif } - #endif @Override public IProfilerWrapper getProfiler() { - #if MC_VER <= MC_1_12_2 Profiler #else ProfilerFiller #endif profiler; - #if MC_VER <= MC_1_12_2 - profiler = MINECRAFT.profiler; - #elif MC_VER < MC_1_21_3 + ProfilerFiller profiler; + #if MC_VER < MC_1_21_3 profiler = MINECRAFT.getProfiler(); #else profiler = Profiler.get(); @@ -382,18 +371,27 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra public void crashMinecraft(String errorMessage, Throwable exception) { LOGGER.fatal(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...", exception); - CrashReport report = new CrashReport(errorMessage, exception); - #if MC_VER <= MC_1_12_2 - MINECRAFT.crashed(report); - #elif MC_VER < MC_1_20_4 - Minecraft.crash(report); - #else - MINECRAFT.delayCrash(report); - #endif + + // Only crash once the renderer has been set up. + // If the renderer hasn't been set up yet crashing MC will + // cause a Blaze3D/UI error instead of the error we're trying to send. + executeOnRenderThread(() -> + { + CrashReport report = new CrashReport(errorMessage, exception); + #if MC_VER < MC_1_20_4 + Minecraft.crash(report); + #else + MINECRAFT.delayCrash(report); + #endif + }); } @Override - public void executeOnRenderThread(Runnable runnable) { MINECRAFT.#if MC_VER <= MC_1_12_2 addScheduledTask #else execute #endif(runnable); } + public void executeOnRenderThread(Runnable runnable) { MINECRAFT.execute(runnable); } + + @Override + public void showDialog(String title, String message, String dialogType, String iconType) + { NativeDialogUtil.showDialog(title, message, dialogType, iconType); } //endregion @@ -405,7 +403,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra //region @Override - public Object getOptionsObject() { return MINECRAFT.#if MC_VER <= MC_1_12_2 gameSettings #else options #endif; } + public Object getOptionsObject() { return MINECRAFT.options; } //endregion @@ -420,23 +418,19 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra public boolean isDedicatedServer() { return false; } @Override - public File getInstallationDirectory() { return MINECRAFT.#if MC_VER <= MC_1_12_2 gameDir #else gameDirectory #endif; } + public File getInstallationDirectory() { return MINECRAFT.gameDirectory; } @Override public int getPlayerCount() { // can be null if the server hasn't finished booting up yet - if (MINECRAFT.#if MC_VER <= MC_1_12_2 getIntegratedServer() #else getSingleplayerServer() #endif == null) + if (MINECRAFT.getSingleplayerServer() == null) { return 1; } else { - #if MC_VER <= MC_1_12_2 - return MINECRAFT.getIntegratedServer().getCurrentPlayerCount(); - #else return MINECRAFT.getSingleplayerServer().getPlayerCount(); - #endif } } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftGLWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftGLWrapper.java index 160853ba0..4639d2ae2 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftGLWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftGLWrapper.java @@ -217,21 +217,6 @@ public class MinecraftGLWrapper { GL32.glDeleteBuffers(buffer); - // attempt to fix a bug with Mac where de-allocated buffers - // are still being used, causing a SIGSEGV native crash - // and/or corrupting native memory - if (EPlatform.get() == EPlatform.MACOS) - { - // force the delete buffer (and any other in-flight) GL - // commands to finish before continuing - GL32.glFinish(); - - // James hates this idea. - // He kinda hopes it doesn't help, - // but maybe metal's API just doesn't finish correctly so we need to wait a moment longer before continuing. - try { Thread.sleep(1); } catch (InterruptedException ignore) { } - } - // MC's implementation has a bug where it will throw: // GL_INVALID_OPERATION in glBufferData(immutable) // when attempting to delete Storage Buffers diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftRenderWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftRenderWrapper.java index c9a3e7176..440b4935f 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftRenderWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/MinecraftRenderWrapper.java @@ -23,14 +23,17 @@ import java.awt.Color; import java.lang.invoke.MethodHandles; import java.util.concurrent.ConcurrentHashMap; +import com.mojang.blaze3d.pipeline.RenderTarget; +import com.mojang.blaze3d.platform.NativeImage; import com.seibel.distanthorizons.api.enums.config.EDhApiLodShading; import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper; import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; import com.seibel.distanthorizons.core.enums.EDhDirection; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.util.ColorUtil; +import com.seibel.distanthorizons.coreapi.util.ColorUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; #if MC_VER < MC_1_17_1 @@ -38,6 +41,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; import net.minecraft.client.renderer.FogRenderer; import com.mojang.blaze3d.systems.RenderSystem; #else +import net.minecraft.client.renderer.fog.FogData; import net.minecraft.client.renderer.fog.FogRenderer; #endif @@ -54,15 +58,8 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.util.math.Vec3d; import com.seibel.distanthorizons.core.util.math.Vec3f; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor; -#if MC_VER <= MC_1_12_2 -import net.minecraft.client.Minecraft; -import net.minecraft.client.renderer.entity.RenderManager; -import net.minecraft.block.state.IBlockState; -import net.minecraft.init.MobEffects; -import net.minecraft.util.math.BlockPos; -import net.minecraftforge.fluids.IFluidBlock; -#else import net.minecraft.client.Camera; import net.minecraft.client.Minecraft; import net.minecraft.core.BlockPos; @@ -70,17 +67,11 @@ import net.minecraft.core.Direction; import net.minecraft.world.effect.MobEffects; import net.minecraft.world.phys.Vec3; -import com.mojang.blaze3d.pipeline.RenderTarget; -import com.mojang.blaze3d.platform.NativeImage; -#endif - import com.seibel.distanthorizons.core.logging.DhLogger; import org.jetbrains.annotations.NotNull; import org.joml.Vector4f; -#if MC_VER <= MC_1_12_2 -import org.lwjgl.opengl.GL15; -#elif MC_VER < MC_1_17_1 +#if MC_VER < MC_1_17_1 import net.minecraft.tags.FluidTags; import net.minecraft.world.entity.Entity; import net.minecraft.world.level.material.FluidState; @@ -108,8 +99,10 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper { public static final MinecraftRenderWrapper INSTANCE = new MinecraftRenderWrapper(); + private static final IOptifineAccessor OPTIFINE_ACCESSOR = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class); + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); - private static final Minecraft MC = Minecraft.#if MC_VER <= MC_1_12_2 getMinecraft() #else getInstance() #endif; + private static final Minecraft MC = Minecraft.getInstance(); /** * In the case of immersive portals multiple levels may be active at once, causing conflicting lightmaps.
@@ -140,10 +133,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper @Override public Vec3f getLookAtVector() { - #if MC_VER <= MC_1_12_2 - net.minecraft.util.math.Vec3d lookVector = (MC.getRenderViewEntity().getLook(MC.getRenderPartialTicks())); - return new Vec3f((float) lookVector.x, (float) lookVector.y, (float) lookVector.z); - #elif MC_VER <= MC_1_21_10 + #if MC_VER <= MC_1_21_10 Camera camera = MC.gameRenderer.getMainCamera(); return new Vec3f(camera.getLookVector().x(), camera.getLookVector().y(), camera.getLookVector().z()); #else @@ -163,53 +153,44 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper { return false; } - else if (MC.player.#if MC_VER <= MC_1_12_2 getActivePotionMap() #else getActiveEffectsMap() #endif == null) + else if (MC.player.getActiveEffectsMap() == null) { return false; } else { - #if MC_VER <= MC_1_12_2 - return MC.player.getActivePotionEffect(MobEffects.BLINDNESS) != null; - #else return MC.player.getActiveEffectsMap().get(MobEffects.BLINDNESS) != null #if MC_VER >= MC_1_19_2 || MC.player.getActiveEffectsMap().get(MobEffects.DARKNESS) != null // Deep dark effect #endif ; - #endif } } @Override public Vec3d getCameraExactPosition() { - #if MC_VER <= MC_1_12_2 - RenderManager rm = MC.getRenderManager(); - return new Vec3d(rm.viewerPosX, rm.viewerPosY, rm.viewerPosZ); - #else Camera camera = MC.gameRenderer.getMainCamera(); #if MC_VER <= MC_1_21_10 Vec3 projectedView = camera.getPosition(); #else Vec3 projectedView = camera.position(); #endif - return new Vec3d(projectedView.x, projectedView.y, projectedView.z); - #endif + return new Vec3d(projectedView.x, projectedView.y, projectedView.z); } @Override public float getPartialTickTime() { - #if MC_VER <= MC_1_12_2 - return MC.getRenderPartialTicks(); - #elif MC_VER < MC_1_21_1 + #if MC_VER < MC_1_21_1 return MC.getFrameTime(); #elif MC_VER < MC_1_21_3 return MC.getTimer().getRealtimeDeltaTicks(); - #else + #elif MC_VER <= MC_1_21_11 return MC.deltaTracker.getRealtimeDeltaTicks(); + #else + return MC.getDeltaTracker().getRealtimeDeltaTicks(); #endif } @@ -279,12 +260,30 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper return Color.white; } + float darkenAmount; + #if MC_VER <= MC_1_21_11 + darkenAmount = MC.gameRenderer.getDarkenWorldAmount(MC.deltaTracker.getGameTimeDeltaPartialTick(true)); + #else + darkenAmount = MC.gameRenderer.getBossOverlayWorldDarkening(MC.deltaTracker.getGameTimeDeltaPartialTick(true)); + #endif + + #if MC_VER <= MC_1_21_11 Vector4f colorValues = mcFogRenderer.setupFog( MC.gameRenderer.getMainCamera(), MC.options.getEffectiveRenderDistance(), MC.deltaTracker, - MC.gameRenderer.getDarkenWorldAmount(MC.deltaTracker.getGameTimeDeltaPartialTick(true)), + darkenAmount, MC.level); + #else + FogData fogData = mcFogRenderer.setupFog( + MC.gameRenderer.getMainCamera(), + MC.options.getEffectiveRenderDistance(), + MC.deltaTracker, + darkenAmount, + MC.level); + Vector4f colorValues = fogData.color; + #endif + return new Color( Math.max(0f, Math.min(colorValues.x, 1f)), // r Math.max(0f, Math.min(colorValues.y, 1f)), // g @@ -297,19 +296,11 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper @Override public Color getSkyColor() { - #if MC_VER <= MC_1_12_2 - if (MC.world.provider.hasSkyLight()) - #else if (MC.level.dimensionType().hasSkyLight()) - #endif { #if MC_VER < MC_1_17_1 float frameTime = this.getPartialTickTime(); - #if MC_VER <= MC_1_12_2 - net.minecraft.util.math.Vec3d colorValues = MC.world.getSkyColor(MC.getRenderViewEntity(), frameTime); - #else Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getBlockPosition(), frameTime); - #endif return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z); #elif MC_VER < MC_1_21_3 float frameTime = this.getPartialTickTime(); @@ -330,23 +321,11 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper } } - @Override - public double getFov(float partialTicks) - { - #if MC_VER <= MC_1_12_2 - return MC.entityRenderer.getFOVModifier(partialTicks, true); - #else - return MC.gameRenderer.getFov(MC.gameRenderer.getMainCamera(), partialTicks, true); - #endif - } - /** Measured in chunks */ @Override public int getRenderDistance() { - #if MC_VER <= MC_1_12_2 - return MC.gameSettings.renderDistanceChunks; - #elif MC_VER <= MC_1_17_1 + #if MC_VER <= MC_1_17_1 return MC.options.renderDistance; #else return MC.options.getEffectiveRenderDistance(); @@ -356,18 +335,14 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper @Override public int getFrameLimit() { - #if MC_VER <= MC_1_12_2 - return MC.gameSettings.limitFramerate; - #elif MC_VER <= MC_1_18_2 + #if MC_VER <= MC_1_18_2 return MC.options.framerateLimit; #else return MC.options.framerateLimit().get(); #endif } - #if MC_VER > MC_1_12_2 protected RenderTarget getRenderTarget() { return MC.getMainRenderTarget(); } - #endif @Override public boolean mcRendersToFrameBuffer() @@ -393,14 +368,12 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper public int getTargetFramebuffer() { // used so we can access the framebuffer shaders end up rendering to - if (AbstractOptifineAccessor.optifinePresent()) + if (OPTIFINE_ACCESSOR != null) { return this.finalLevelFrameBufferId; } - #if MC_VER <= MC_1_12_2 - return MC.getFramebuffer().framebufferObject; - #elif MC_VER < MC_1_21_5 + #if MC_VER < MC_1_21_5 return this.getRenderTarget().frameBufferId; #else // MC renders to a texture and then directly to the default FBO now @@ -415,10 +388,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper @Override public int getDepthTextureId() { - #if MC_VER <= MC_1_12_2 - //1.12.2 is using renderbuffer instead of framebuffer for depth texture - return -1; - #elif MC_VER < MC_1_21_5 + #if MC_VER < MC_1_21_5 return this.getRenderTarget().getDepthTextureId(); #else try @@ -448,9 +418,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper @Override public int getColorTextureId() { - #if MC_VER <= MC_1_12_2 - return MC.getFramebuffer().framebufferTexture; - #elif MC_VER < MC_1_21_5 + #if MC_VER < MC_1_21_5 return this.getRenderTarget().getColorTextureId(); #else try @@ -480,9 +448,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper @Override public int getTargetFramebufferViewportWidth() { - #if MC_VER <= MC_1_12_2 - return MC.getFramebuffer().framebufferWidth; - #elif MC_VER < MC_1_21_9 + #if MC_VER < MC_1_21_9 return this.getRenderTarget().viewWidth; #else return this.getRenderTarget().width; @@ -492,9 +458,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper @Override public int getTargetFramebufferViewportHeight() { - #if MC_VER <= MC_1_12_2 - return MC.getFramebuffer().framebufferHeight; - #elif MC_VER < MC_1_21_9 + #if MC_VER < MC_1_21_9 return this.getRenderTarget().viewHeight; #else return this.getRenderTarget().height; @@ -507,11 +471,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper @Override public boolean isFogStateSpecial() { - #if MC_VER <= MC_1_12_2 - BlockPos blockPos = new BlockPos(MC.getRenderViewEntity().getPositionEyes(MC.getRenderPartialTicks())); - IBlockState fluidState = MC.getRenderViewEntity().world.getBlockState(blockPos); - return this.playerHasBlindingEffect() || fluidState.getMaterial().isLiquid() || fluidState.getBlock() instanceof IFluidBlock; - #elif MC_VER < MC_1_17_1 + #if MC_VER < MC_1_17_1 Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); FluidState fluidState = camera.getFluidInCamera(); Entity entity = camera.getEntity(); @@ -529,7 +489,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper * It's better to use {@link MinecraftRenderWrapper#setLightmapId(int, IClientLevelWrapper)} if possible, * however old MC versions don't support it. */ - #if MC_VER > MC_1_12_2 public void updateLightmap(NativeImage lightPixels, IClientLevelWrapper level) { // Using ClientLevelWrapper as the key would be better, but we don't have a consistent way to create the same @@ -540,8 +499,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper LightMapWrapper wrapper = this.lightmapByDimensionType.computeIfAbsent(dimensionType, (dimType) -> new LightMapWrapper()); wrapper.uploadLightmap(lightPixels); } - #endif - public void setLightmapId(int tetxureId, IClientLevelWrapper level) { // Using ClientLevelWrapper as the key would be better, but we don't have a consistent way to create the same @@ -575,19 +532,20 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper { default: case AUTO: - #if MC_VER <= MC_1_12_2 - // 1.12.2 has no getShade, fall through to ENABLED - #else if (MC.level != null) { Direction mcDir = McObjectConverter.Convert(lodDirection); + #if MC_VER <= MC_1_21_11 return MC.level.getShade(mcDir, true); + #else + return MC.level.cardinalLighting().byFace(mcDir); + #endif } else { return 0.0f; } - #endif + case ENABLED: switch (lodDirection) { diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/ProfilerWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/ProfilerWrapper.java index 9f15dbfaa..b9e4392fc 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/ProfilerWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/minecraft/ProfilerWrapper.java @@ -21,33 +21,49 @@ package com.seibel.distanthorizons.common.wrappers.minecraft; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; -#if MC_VER <= MC_1_12_2 -import net.minecraft.profiler.Profiler; -#else import net.minecraft.util.profiling.ProfilerFiller; -#endif -/** - * @author James Seibel - * @version 11-20-2021 - */ public class ProfilerWrapper implements IProfilerWrapper { - public #if MC_VER <= MC_1_12_2 Profiler #else ProfilerFiller #endif profiler; + public ProfilerFiller profiler; - public ProfilerWrapper(#if MC_VER <= MC_1_12_2 Profiler #else ProfilerFiller #endif newProfiler) { this.profiler = newProfiler; } + public ProfilerWrapper(ProfilerFiller newProfiler) { this.profiler = newProfiler; } - - /** starts a new section inside the currently running section */ @Override - public void push(String newSection) { this.profiler.#if MC_VER <= MC_1_12_2 startSection(newSection) #else push(newSection) #endif; } + public IProfileBlock push(String newSection) + { + this.profiler.push(newSection); + return new ProfileBlock(this.profiler); + } - /** ends the currently running section and starts a new one */ @Override - public void popPush(String newSection) { this.profiler.#if MC_VER <= MC_1_12_2 endStartSection(newSection) #else popPush(newSection) #endif; } + public void popPush(String newSection) + { + this.profiler.popPush(newSection); + } + + + + //================// + // helper classes // + //================// + //region + + public static class ProfileBlock implements IProfileBlock + { + private final ProfilerFiller profiler; + public ProfileBlock(ProfilerFiller newProfiler) { this.profiler = newProfiler; } + + + @Override + public void close() + { + this.profiler.pop(); + } + } + + //endregion + - /** ends the currently running section */ - @Override - public void pop() { this.profiler.#if MC_VER <= MC_1_12_2 endSection() #else pop() #endif; } } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java index 05e6eeb7f..11f1173b5 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/world/ClientLevelWrapper.java @@ -59,7 +59,7 @@ import net.minecraft.world.level.chunk.status.ChunkStatus; #elif MC_VER < MC_1_21_3 import net.minecraft.world.phys.Vec3; #else -import com.seibel.distanthorizons.core.util.ColorUtil; +import com.seibel.distanthorizons.coreapi.util.ColorUtil; #endif #if MC_VER <= MC_1_21_10 diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/BatchGenerationEnvironment.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/BatchGenerationEnvironment.java index 4fa0281fe..4c594b46d 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/BatchGenerationEnvironment.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/BatchGenerationEnvironment.java @@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableMap; import com.seibel.distanthorizons.api.DhApi; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep; +import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper; #if MC_VER > MC_1_12_2 import com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling.ChunkFileReader; @@ -56,6 +57,7 @@ import java.util.function.Consumer; import com.seibel.distanthorizons.coreapi.ModInfo; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; #if MC_VER > MC_1_12_2 import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepBiomes; @@ -169,22 +171,12 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm static { - boolean isTerraFirmaCraftPresent = false; - try - { - Class.forName("net.dries007.tfc.world.TFCChunkGenerator"); - isTerraFirmaCraftPresent = true; - LOGGER.info("TerraFirmaCraft detected."); - } - catch (ClassNotFoundException ignore) { } - - ImmutableMap.Builder builder = ImmutableMap.builder(); builder.put(EDhApiWorldGenerationStep.EMPTY, 1); builder.put(EDhApiWorldGenerationStep.STRUCTURE_START, 0); builder.put(EDhApiWorldGenerationStep.STRUCTURE_REFERENCE, 0); - builder.put(EDhApiWorldGenerationStep.BIOMES, isTerraFirmaCraftPresent ? 1 : 0); - builder.put(EDhApiWorldGenerationStep.NOISE, isTerraFirmaCraftPresent ? 1 : 0); + builder.put(EDhApiWorldGenerationStep.BIOMES, 0); + builder.put(EDhApiWorldGenerationStep.NOISE, 0); builder.put(EDhApiWorldGenerationStep.SURFACE, 0); builder.put(EDhApiWorldGenerationStep.CARVERS, 0); builder.put(EDhApiWorldGenerationStep.LIQUID_CARVERS, 0); @@ -221,11 +213,6 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm LOGGER.info("TerraForge Chunk Generator detected: [" + generator.getClass() + "], Distant Generation will try its best to support it."); LOGGER.info("If it does crash, turn Distant Generation off or set it to to [" + EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY + "]."); } - else if (generator.getClass().toString().equals("class net.dries007.tfc.world.TFCChunkGenerator")) - { - LOGGER.info("TerraFirmaCraft Chunk Generator detected: [" + generator.getClass() + "], Distant Generation will try its best to support it."); - LOGGER.info("If it does crash, turn Distant Generation off or set it to to [" + EDhApiDistantGeneratorMode.PRE_EXISTING_ONLY + "]."); - } else { LOGGER.warn("Unknown Chunk Generator detected: [" + generator.getClass() + "], Distant Generation May Fail!"); @@ -373,12 +360,12 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm while (existingChunkPosIterator.hasNext()) { ChunkPos chunkPos = existingChunkPosIterator.next(); - DhChunkPos dhChunkPos = new DhChunkPos(chunkPos.x, chunkPos.z); + DhChunkPos dhChunkPos = McObjectConverter.Convert(chunkPos); CompletableFuture getExistingChunkFuture // running async allows file IO to run in parallel when C2ME is present = this.chunkFileReader.createEmptyOrPreExistingChunkWrapperAsync( - chunkPos.x, chunkPos.z, + dhChunkPos.getX(), dhChunkPos.getZ(), chunkSkyLightingByDhPos, chunkBlockLightingByDhPos, chunkWrappersByDhPos); readFutureByDhChunkPos.put(dhChunkPos, getExistingChunkFuture); @@ -405,7 +392,7 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm while (emptyChunkPosIterator.hasNext()) { ChunkPos chunkPos = emptyChunkPosIterator.next(); - DhChunkPos dhChunkPos = new DhChunkPos(chunkPos.x, chunkPos.z); + DhChunkPos dhChunkPos = McObjectConverter.Convert(chunkPos); // create empty chunks outside the generation radius if (!readFutureByDhChunkPos.containsKey(dhChunkPos)) @@ -456,7 +443,11 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm relZ + refPosZ + zOffsetFinal)); ChunkAccess centerChunk = regionChunks.stream() + #if MC_VER <= MC_1_21_11 .filter((chunk) -> chunk.getPos().x == centerX && chunk.getPos().z == centerZ) + #else + .filter((chunk) -> chunk.getPos().x() == centerX && chunk.getPos().z() == centerZ) + #endif .findFirst() .orElseGet(() -> regionChunks.getFirst()); @@ -554,9 +545,9 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm Iterator iterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0); while (iterator.hasNext()) { - ChunkPos pos = iterator.next(); - DhChunkPos dhPos = new DhChunkPos(pos.x, pos.z); - ChunkWrapper wrappedChunk = chunkWrappersByDhPos.get(dhPos); + ChunkPos chunkPos = iterator.next(); + DhChunkPos dhChunkPos = McObjectConverter.Convert(chunkPos); + ChunkWrapper wrappedChunk = chunkWrappersByDhPos.get(dhChunkPos); // only pass along chunks that have been generated up to BIOMES // this is to prevent issues with generating existing @@ -570,7 +561,7 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm if (!this.generatedChunkWithoutBiomeWarningLogged) { this.generatedChunkWithoutBiomeWarningLogged = true; - LOGGER.warn("Chunk [" + dhPos + "] wasn't generated up to BIOMES, world gen may appear empty."); + LOGGER.warn("Chunk [" + dhChunkPos + "] wasn't generated up to BIOMES, world gen may appear empty."); } } } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/InternalServerGenerator.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/InternalServerGenerator.java index bb2cf3990..b2e6b9717 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/InternalServerGenerator.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/InternalServerGenerator.java @@ -1,6 +1,7 @@ package com.seibel.distanthorizons.common.wrappers.worldGeneration; import com.seibel.distanthorizons.api.DhApi; +import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.GlobalWorldGenParams; import com.seibel.distanthorizons.core.api.internal.ClientApi; @@ -11,11 +12,9 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; import com.seibel.distanthorizons.core.enums.MinecraftTextFormat; import com.seibel.distanthorizons.core.generation.DhLightingEngine; -import com.seibel.distanthorizons.core.level.DhServerLevel; import com.seibel.distanthorizons.core.level.IDhServerLevel; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.util.ExceptionUtil; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.TimerUtil; @@ -23,15 +22,8 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IC2meAccessor; import com.seibel.distanthorizons.coreapi.ModInfo; +import org.jetbrains.annotations.Nullable; -#if MC_VER <= MC_1_12_2 -import net.minecraft.util.math.ChunkPos; -import net.minecraft.world.World; -import net.minecraft.world.WorldServer; -import net.minecraft.world.chunk.Chunk; -import net.minecraft.world.gen.ChunkProviderServer; -import net.minecraftforge.common.ForgeChunkManager; -#else import net.minecraft.server.level.ChunkHolder; import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.TicketType; @@ -43,9 +35,7 @@ import net.minecraft.world.level.chunk.ChunkStatus; #else import net.minecraft.world.level.chunk.status.ChunkStatus; #endif -#endif -import org.jetbrains.annotations.Nullable; import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; @@ -74,10 +64,7 @@ public class InternalServerGenerator */ private static final int MS_TO_IGNORE_CHUNK_AFTER_COMPLETION = 5_000; - #if MC_VER <= MC_1_12_2 - public static Map DH_SERVER_GEN_TICKET_MAP = new HashMap<>(); - private final ForgeChunkManager.Ticket DH_SERVER_GEN_TICKET; - #elif MC_VER < MC_1_21_5 + #if MC_VER < MC_1_21_5 private static final TicketType DH_SERVER_GEN_TICKET = TicketType.create("dh_server_gen_ticket", Comparator.comparingLong(ChunkPos::toLong)); #elif MC_VER < MC_1_21_9 private static final TicketType DH_SERVER_GEN_TICKET = new TicketType(/* timeout, 0 = disabled*/0L, /* persist */ false, TicketType.TicketUse.LOADING); @@ -104,13 +91,6 @@ public class InternalServerGenerator { this.params = params; this.dhServerLevel = dhServerLevel; - #if MC_VER <= MC_1_12_2 - this.DH_SERVER_GEN_TICKET = DH_SERVER_GEN_TICKET_MAP.get((WorldServer) this.dhServerLevel.getServerLevelWrapper().getWrappedMcObject()); - if (this.DH_SERVER_GEN_TICKET == null) - { - LOGGER.error("DH_SERVER_GEN_TICKET is null for level: " + dhServerLevel.getServerLevelWrapper().getDimensionName()); - } - #endif this.updateManager = WorldChunkUpdateManager.INSTANCE.getByLevelWrapper(this.dhServerLevel.getServerLevelWrapper()); } @@ -130,14 +110,14 @@ public class InternalServerGenerator // create gen requests // //=====================// - ArrayList> getChunkFutureList = new ArrayList<>(); + ArrayList> getChunkFutureList = new ArrayList<>(); { Iterator chunkPosIterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0); while (chunkPosIterator.hasNext()) { ChunkPos chunkPos = chunkPosIterator.next(); - CompletableFuture<#if MC_VER <= MC_1_12_2 Chunk #else ChunkAccess #endif> requestChunkFuture = + CompletableFuture requestChunkFuture = this.requestChunkFromServerAsync(chunkPos) // log errors if necessary .whenCompleteAsync( @@ -180,8 +160,8 @@ public class InternalServerGenerator ArrayList chunkWrappers = new ArrayList<>(); for (int i = 0; i < getChunkFutureList.size(); i++) { - CompletableFuture<#if MC_VER <= MC_1_12_2 Chunk #else ChunkAccess #endif> getChunkFuture = getChunkFutureList.get(i); - #if MC_VER <= MC_1_12_2 Chunk #else ChunkAccess #endif chunk = getChunkFuture.join(); + CompletableFuture getChunkFuture = getChunkFutureList.get(i); + ChunkAccess chunk = getChunkFuture.join(); if (chunk != null) { ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, this.dhServerLevel.getLevelWrapper()); @@ -213,12 +193,22 @@ public class InternalServerGenerator } finally { + ArrayList> releaseFutures = new ArrayList<>(); + // release all chunks from the server to prevent out of memory issues Iterator chunkPosIterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0); while (chunkPosIterator.hasNext()) { ChunkPos chunkPos = chunkPosIterator.next(); - this.releaseChunkFromServer(this.params.mcServerLevel, this.params.dhServerLevel, chunkPos); + releaseFutures.add(this.releaseChunkFromServerAsync(this.params.mcServerLevel, chunkPos)); + } + + // wait for all release futures to finish to prevent an issue where DH queues + // tickets faster than MC can clear them out + for (int i = 0; i < releaseFutures.size(); i++) + { + CompletableFuture releaseFuture = releaseFutures.get(i); + releaseFuture.join(); } } } @@ -255,49 +245,8 @@ public class InternalServerGenerator LOGGER.warn(c2meWarning); } } - private CompletableFuture<#if MC_VER <= MC_1_12_2 Chunk #else ChunkAccess #endif> requestChunkFromServerAsync(ChunkPos chunkPos) + private CompletableFuture requestChunkFromServerAsync(ChunkPos chunkPos) { - #if MC_VER <= MC_1_12_2 - WorldServer level = this.params.mcServerLevel; - - if (this.updateManager != null) - { - this.updateManager.removePosToIgnore(new DhChunkPos(chunkPos.x, chunkPos.z)); - } - - CompletableFuture future = new CompletableFuture<>(); - level.getMinecraftServer().addScheduledTask(() -> - { - try - { - ChunkProviderServer provider = (ChunkProviderServer) level.getChunkProvider(); - - // load neighbours first so the target chunk can fully populate - for (int i = -1; i <= 1; i++) - { - for (int j = -1; j <= 1; j++) - { - if (i != 0 || j != 0) - { - if (!provider.isChunkGeneratedAt(chunkPos.x + i, chunkPos.z + j)) - { - provider.provideChunk(chunkPos.x + i, chunkPos.z + j); - } - } - } - } - - ForgeChunkManager.forceChunk(DH_SERVER_GEN_TICKET, chunkPos); - Chunk chunk = provider.provideChunk(chunkPos.x, chunkPos.z); - future.complete(chunk); - } - catch (Exception e) - { - future.completeExceptionally(e); - } - }); - return future; - #else return CompletableFuture.supplyAsync(() -> { ServerLevel level = this.params.mcServerLevel; @@ -305,9 +254,9 @@ public class InternalServerGenerator // ignore chunk update events for this position if (this.updateManager != null) { - this.updateManager.removePosToIgnore(new DhChunkPos(chunkPos.x, chunkPos.z)); + this.updateManager.addPosToIgnore(McObjectConverter.Convert(chunkPos)); } - + #if MC_VER < MC_1_21_5 int chunkLevel = 33; // 33 is equivalent to FULL Chunk level.getChunkSource().distanceManager.addTicket(DH_SERVER_GEN_TICKET, chunkPos, chunkLevel, chunkPos); @@ -318,7 +267,10 @@ public class InternalServerGenerator // probably not the most optimal to run updates here, but fast enough level.getChunkSource().distanceManager.runAllUpdates(level.getChunkSource().chunkMap); - ChunkHolder chunkHolder = level.getChunkSource().chunkMap.getUpdatingChunkIfPresent(chunkPos.toLong()); + ChunkHolder chunkHolder = level.getChunkSource().chunkMap + .getUpdatingChunkIfPresent( + #if MC_VER <= MC_1_21_11 chunkPos.toLong() #else chunkPos.pack() #endif + ); if (chunkHolder == null) { throw new IllegalStateException("No chunk chunkHolder for pos ["+chunkPos+"] after ticket has been added."); @@ -337,41 +289,15 @@ public class InternalServerGenerator }, this.params.mcServerLevel.getChunkSource().chunkMap.mainThreadExecutor) .thenCompose(Function.identity()); - #endif } /** * mitigates out of memory issues in the vanilla chunk system.
* See: https://github.com/pop4959/Chunky/pull/383 */ - private void releaseChunkFromServer(#if MC_VER <= MC_1_12_2 WorldServer #else ServerLevel #endif level, IDhServerLevel dhLevel, ChunkPos chunkPos) + private CompletableFuture releaseChunkFromServerAsync(ServerLevel level, ChunkPos chunkPos) { - #if MC_VER <= MC_1_12_2 - level.getMinecraftServer().addScheduledTask(() -> - { - try - { - ForgeChunkManager.unforceChunk(DH_SERVER_GEN_TICKET, chunkPos); - - // give MC a few seconds to save the chunk before - // we can process update events there again - this.chunkSaveIgnoreTimer.schedule(new TimerTask() - { - @Override - public void run() - { - if (InternalServerGenerator.this.updateManager != null) - { - InternalServerGenerator.this.updateManager.addPosToIgnore(new DhChunkPos(chunkPos.x, chunkPos.z)); - } - } - }, MS_TO_IGNORE_CHUNK_AFTER_COMPLETION); - } - catch (Exception e) - { - LOGGER.warn("Failed to release chunk back to internal server. Error: ["+e.getMessage()+"]", e); - } - }); - #else + CompletableFuture removeTicketFuture = new CompletableFuture<>(); + level.getChunkSource().chunkMap.mainThreadExecutor.execute(() -> { try @@ -399,7 +325,7 @@ public class InternalServerGenerator { if (InternalServerGenerator.this.updateManager != null) { - InternalServerGenerator.this.updateManager.addPosToIgnore(new DhChunkPos(chunkPos.x, chunkPos.z)); + InternalServerGenerator.this.updateManager.removePosToIgnore(McObjectConverter.Convert(chunkPos)); } } }, MS_TO_IGNORE_CHUNK_AFTER_COMPLETION); @@ -407,12 +333,17 @@ public class InternalServerGenerator } catch (Exception e) { - LOGGER.warn("Failed to release chunk back to internal server. Error: ["+e.getMessage()+"]", e); + LOGGER.warn("Failed to release chunk ["+chunkPos+"] back to internal server. Error: ["+e.getMessage()+"]", e); + } + finally + { + removeTicketFuture.complete(null); } }); - #endif + + return removeTicketFuture; } -} \ No newline at end of file +} diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/chunkFileHandling/ChunkCompoundTagParser.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/chunkFileHandling/ChunkCompoundTagParser.java index f771ac8ff..a3381679a 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/chunkFileHandling/ChunkCompoundTagParser.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/chunkFileHandling/ChunkCompoundTagParser.java @@ -21,12 +21,14 @@ package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHand #if MC_VER > MC_1_12_2 import com.mojang.serialization.Codec; +import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.level.IDhServerLevel; import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage; @@ -674,7 +676,9 @@ public class ChunkCompoundTagParser { LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) -> { - LOGGER.warn("Unable to deserialize blocks for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " + + DhChunkPos dhChunkPos = McObjectConverter.Convert(chunkPos); + + LOGGER.warn("Unable to deserialize blocks for chunk section [" + dhChunkPos.getX() + ", " + sectionYIndex + ", " + dhChunkPos.getZ() + "], error: ["+newMessage+"]. " + "This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem."); return newMessage; @@ -684,7 +688,9 @@ public class ChunkCompoundTagParser { LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) -> { - LOGGER.warn("Unable to deserialize biomes for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " + + DhChunkPos dhChunkPos = McObjectConverter.Convert(chunkPos); + + LOGGER.warn("Unable to deserialize biomes for chunk section [" + dhChunkPos.getX() + ", " + sectionYIndex + ", " + dhChunkPos.getZ() + "], error: ["+newMessage+"]. " + "This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem."); return newMessage; diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/DhLitWorldGenRegion.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/DhLitWorldGenRegion.java index 88dcd3c4a..df42e7f98 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/DhLitWorldGenRegion.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/DhLitWorldGenRegion.java @@ -23,9 +23,12 @@ package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject; import java.lang.invoke.MethodHandles; import java.util.List; import java.util.concurrent.locks.ReentrantLock; + +import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.util.LodUtil; import net.minecraft.world.level.block.EntityBlock; import net.minecraft.world.level.block.SpawnerBlock; @@ -90,7 +93,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion public final int writeRadius; public final int size; - private final ChunkPos firstPos; + private final DhChunkPos firstPos; private final List cache; private final Long2ObjectOpenHashMap chunkMap = new Long2ObjectOpenHashMap(); @@ -150,7 +153,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion centerChunk); #endif - this.firstPos = chunkList.get(0).getPos(); + this.firstPos = McObjectConverter.Convert(chunkList.get(0).getPos()); this.serverLevel = serverLevel; this.generator = generator; this.lightEngine = lightEngine; @@ -166,17 +169,22 @@ public class DhLitWorldGenRegion extends WorldGenRegion @Override public boolean ensureCanWrite(BlockPos blockPos) { - int i = SectionPos.blockToSectionCoord(blockPos.getX()); - int j = SectionPos.blockToSectionCoord(blockPos.getZ()); - ChunkPos chunkPos = this.getCenter(); - ChunkAccess center = this.getChunk(chunkPos.x, chunkPos.z); - int k = Math.abs(chunkPos.x - i); - int l = Math.abs(chunkPos.z - j); - if (k > this.writeRadius || l > this.writeRadius) + DhChunkPos chunkPos = McObjectConverter.Convert(this.getCenter()); + + int sectionCoordX = SectionPos.blockToSectionCoord(blockPos.getX()); + int sectionCoordZ = SectionPos.blockToSectionCoord(blockPos.getZ()); + + // TODO what do these "abs" positions mean? + int absX = Math.abs(chunkPos.getX() - sectionCoordX); + int absZ = Math.abs(chunkPos.getZ() - sectionCoordZ); + if (absX > this.writeRadius + || absZ > this.writeRadius) { return false; } + #if MC_VER >= MC_1_18_2 + ChunkAccess center = this.getChunk(chunkPos.getX(), chunkPos.getZ()); if (center.isUpgrading()) { LevelHeightAccessor levelHeightAccessor = center.getHeightAccessorForGeneration(); @@ -197,6 +205,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion } } #endif + return true; } #endif @@ -373,7 +382,15 @@ public class DhLitWorldGenRegion extends WorldGenRegion if (chunk == null) { // check memory - chunk = this.chunkMap.get(ChunkPos.asLong(chunkX, chunkZ)); + + long chunkPosAsLong; + #if MC_VER <= MC_1_21_11 + chunkPosAsLong = ChunkPos.asLong(chunkX, chunkZ); + #else + chunkPosAsLong = ChunkPos.pack(chunkX, chunkZ); + #endif + + chunk = this.chunkMap.get(chunkPosAsLong); if (chunk == null) { // chunk isn't in memory, generate a new one @@ -382,7 +399,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion { throw new NullPointerException("The provided generator should not return null!"); } - this.chunkMap.put(ChunkPos.asLong(chunkX, chunkZ), chunk); + this.chunkMap.put(chunkPosAsLong, chunk); } } @@ -402,17 +419,18 @@ public class DhLitWorldGenRegion extends WorldGenRegion /** Use this instead of super.hasChunk() to bypass C2ME concurrency checks */ public boolean superHasChunk(int x, int z) { - int k = x - this.firstPos.x; - int l = z - this.firstPos.z; - return l >= 0 && l < this.size && k >= 0 && k < this.size; + int xOffset = x - this.firstPos.getX(); + int zOffset = z - this.firstPos.getZ(); + return zOffset >= 0 && zOffset < this.size + && xOffset >= 0 && xOffset < this.size; } /** Use this instead of super.getChunk() to bypass C2ME concurrency checks */ private ChunkAccess superGetChunk(int x, int z) { - int k = x - this.firstPos.x; - int l = z - this.firstPos.z; - return this.cache.get(k + l * this.size); + int xOffset = x - this.firstPos.getX(); + int zOffset = z - this.firstPos.getZ(); + return this.cache.get(xOffset + zOffset * this.size); } diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/RegionFileStorageExternalCache.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/RegionFileStorageExternalCache.java index 498c5a0f7..88a6d2778 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/RegionFileStorageExternalCache.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/RegionFileStorageExternalCache.java @@ -70,8 +70,13 @@ public class RegionFileStorageExternalCache implements AutoCloseable } + long chunkPosLong; + #if MC_VER <= MC_1_21_11 + chunkPosLong = ChunkPos.asLong(chunkPos.getRegionX(), chunkPos.getRegionZ()); + #else + chunkPosLong = ChunkPos.pack(chunkPos.getRegionX(), chunkPos.getRegionZ()); + #endif - long chunkPosLong = ChunkPos.asLong(chunkPos.getRegionX(), chunkPos.getRegionZ()); RegionFile regionFile = null; // Check vanilla cache @@ -85,7 +90,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable { this.getRegionFileLock.lock(); - #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 + #if MC_VER <= MC_1_17_1 regionFile = this.storage.getRegionFile(chunkPos); // keeping the region cache size low helps prevent concurrency issues @@ -105,7 +110,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable } catch (ArrayIndexOutOfBoundsException e) { - #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 + #if MC_VER <= MC_1_17_1 // the file just wasn't cached break; #else @@ -180,7 +185,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable regionFile = new RegionFile(new RegionStorageInfo("level", null, "level type"), regionFilePath, storageFolderPath, false); #endif - this.regionFileCache.add(new RegionFileCache(ChunkPos.asLong(chunkPos.getRegionX(), chunkPos.getRegionZ()), regionFile)); + this.regionFileCache.add(new RegionFileCache(chunkPosLong, regionFile)); while (this.regionFileCache.size() > MAX_CACHE_SIZE) { this.regionFileCache.poll().file.close(); diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/WorldGenStructFeatManager.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/WorldGenStructFeatManager.java index e92e280bf..b2b58adee 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/WorldGenStructFeatManager.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/mimicObject/WorldGenStructFeatManager.java @@ -29,6 +29,8 @@ import java.util.stream.Stream; import com.google.common.collect.ImmutableList; +import com.seibel.distanthorizons.common.wrappers.McObjectConverter; +import com.seibel.distanthorizons.core.pos.DhChunkPos; import it.unimi.dsi.fastutil.longs.LongSet; import net.minecraft.core.BlockPos; import net.minecraft.core.SectionPos; @@ -223,9 +225,10 @@ public class WorldGenStructFeatManager extends #if MC_VER < MC_1_19_2 StructureF } #else @Override - public List startsForStructure(ChunkPos sectionPos, Predicate predicate) + public List startsForStructure(ChunkPos chunkPos, Predicate predicate) { - ChunkAccess chunk = _getChunk(sectionPos.x, sectionPos.z, ChunkStatus.STRUCTURE_REFERENCES); + DhChunkPos dhChunkPos = McObjectConverter.Convert(chunkPos); + ChunkAccess chunk = _getChunk(dhChunkPos.getX(), dhChunkPos.getZ(), ChunkStatus.STRUCTURE_REFERENCES); if (chunk == null) return List.of(); // Copied from StructureFeatureManager::startsForFeature(...) diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/params/GlobalWorldGenParams.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/params/GlobalWorldGenParams.java index 9ebec01c4..68137faed 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/params/GlobalWorldGenParams.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/params/GlobalWorldGenParams.java @@ -133,10 +133,14 @@ public final class GlobalWorldGenParams this.worldOptions = worldData.worldGenOptions(); this.biomes = registry.registryOrThrow(Registries.BIOME); this.worldSeed = worldOptions.seed(); - #else + #elif MC_VER <= MC_1_21_11 this.worldOptions = worldData.worldGenOptions(); this.biomes = this.registry.lookupOrThrow(Registries.BIOME); this.worldSeed = this.worldOptions.seed(); + #else + this.worldOptions = server.getWorldGenSettings().options(); + this.biomes = this.registry.lookupOrThrow(Registries.BIOME); + this.worldSeed = this.worldOptions.seed(); #endif diff --git a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/params/ThreadWorldGenParams.java b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/params/ThreadWorldGenParams.java index 44f8468bd..afc7caf84 100644 --- a/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/params/ThreadWorldGenParams.java +++ b/common/src/main/java/com/seibel/distanthorizons/common/wrappers/worldGeneration/params/ThreadWorldGenParams.java @@ -41,8 +41,6 @@ public final class ThreadWorldGenParams public StructureCheck structCheck; #endif - boolean isValid = true; - // used for some older MC versions private static GlobalWorldGenParams previousGlobalWorldGenParams = null; @@ -56,7 +54,6 @@ public final class ThreadWorldGenParams { ThreadWorldGenParams threadParam = LOCAL_PARAM_REF.get(); if (threadParam != null - && threadParam.isValid && threadParam.level == globalParams.mcServerLevel) { return threadParam; diff --git a/common/src/main/resources/26_1.distanthorizons.accesswidener b/common/src/main/resources/26_1.distanthorizons.accesswidener new file mode 100644 index 000000000..ffde71cec --- /dev/null +++ b/common/src/main/resources/26_1.distanthorizons.accesswidener @@ -0,0 +1,56 @@ +accessWidener v1 official + +# used when determining where to save files to +accessible field net/minecraft/world/level/storage/SavedDataStorage dataFolder Ljava/nio/file/Path; +# used to help determine what folder a clientLevel is +accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J + +# used when rendering +accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)F +accessible field net/minecraft/client/Minecraft deltaTracker Lnet/minecraft/client/DeltaTracker$Timer; +accessible field net/minecraft/client/renderer/LevelRenderer level Lnet/minecraft/client/multiplayer/ClientLevel; + + +# used for grabbing vanilla rendered chunks +accessible field net/minecraft/client/renderer/LevelRenderer visibleSections Lit/unimi/dsi/fastutil/objects/ObjectArrayList; + +# world generation +accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z +accessible field net/minecraft/world/level/lighting/LightEngine storage Lnet/minecraft/world/level/lighting/LayerLightSectionStorage; +accessible method net/minecraft/world/level/lighting/LayerLightSectionStorage lightOnInSection (J)Z +accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager; +accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder; +accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V +accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager; +accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop; + +# getting existing chunks outside the main thread +accessible method net/minecraft/server/level/ChunkMap getVisibleChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder; + +# lod generation from save file +accessible field net/minecraft/world/level/chunk/storage/SimpleRegionStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker; +accessible field net/minecraft/world/level/chunk/storage/IOWorker storage Lnet/minecraft/world/level/chunk/storage/RegionFileStorage; +accessible field net/minecraft/world/level/chunk/storage/RegionFileStorage regionCache Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap; +accessible field net/minecraft/world/level/chunk/storage/RegionFileStorage folder Ljava/nio/file/Path; + +# grabbing textures +accessible class net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture +accessible method net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture getFrameX (I)I +accessible method net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture getFrameY (I)I +accessible field net/minecraft/client/renderer/texture/SpriteContents animatedTexture Lnet/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture; +accessible field net/minecraft/client/renderer/texture/SpriteContents originalImage Lcom/mojang/blaze3d/platform/NativeImage; + +# UI stuff +accessible field net/minecraft/client/gui/components/AbstractButton SPRITES Lnet/minecraft/client/gui/components/WidgetSprites; +# Handles inserting the config button +accessible field net/minecraft/client/gui/layouts/HeaderAndFooterLayout headerFrame Lnet/minecraft/client/gui/layouts/FrameLayout; +accessible field net/minecraft/client/gui/layouts/FrameLayout children Ljava/util/List; +accessible class net/minecraft/client/gui/layouts/FrameLayout$ChildContainer +accessible field net/minecraft/client/gui/layouts/LinearLayout wrapped Lnet/minecraft/client/gui/layouts/GridLayout; +accessible method net/minecraft/client/gui/components/debug/DebugScreenEntries register (Ljava/lang/String;Lnet/minecraft/client/gui/components/debug/DebugScreenEntry;)Lnet/minecraft/resources/Identifier; + +# hacky stuff +accessible field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore; +mutable field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore; + + diff --git a/compile.sh b/compile.sh deleted file mode 100644 index 9cc9a628b..000000000 --- a/compile.sh +++ /dev/null @@ -1,31 +0,0 @@ -#!/bin/sh - -publish_version() -{ - if [[ "$2" == "all" || "$1" == "$2" ]] - then - docker run --name=dh-build-$1 --rm -v /${PWD}:/home/build -e MC_VER=$1 dh-eclipse-temurin - cp ./fabric/build/libs/*$1.jar ./buildAllJars/fabric/ - cp ./forge/build/libs/*$1.jar ./buildAllJars/forge/ - cp ./Merged/*.jar ./buildAllJars/merged/ - fi -} - - -if [ -z "$1" ] -then - echo "Build target is undefined! [all] [1.20.1] [1.19.4] [1.19.2] [1.18.2] [1.17.1] [1.16.5]" - exit 1 -fi - -docker build --tag=dh-eclipse-temurin -q . - -mkdir -p buildAllJars/fabric -mkdir -p buildAllJars/forge -mkdir -p buildAllJars/merged -publish_version 1.20.1 $1 -publish_version 1.19.4 $1 -publish_version 1.19.2 $1 -publish_version 1.18.2 $1 -publish_version 1.17.1 $1 -publish_version 1.16.5 $1 diff --git a/fabric/build.gradle b/fabric/build.gradle index dbe0e6311..24460d713 100644 --- a/fabric/build.gradle +++ b/fabric/build.gradle @@ -1,106 +1,24 @@ plugins { - id "fabric-loom" version "1.10-SNAPSHOT" -} - -loom { - accessWidenerPath = project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener") - - // "runs" isn't required, but when we do need it then it can be useful - runs { - client { - client() - setConfigName("Fabric Client") - ideConfigGenerated(true) // When true a run configuration file will be generated for IDE's. By default only set to true for the root project. - runDir("../run/client") - vmArgs( - // https://github.com/FabricMC/fabric-loom/issues/915#issuecomment-1609154390 - "-Dminecraft.api.auth.host=https://nope.invalid", - "-Dminecraft.api.account.host=https://nope.invalid", - "-Dminecraft.api.session.host=https://nope.invalid", - "-Dminecraft.api.services.host=https://nope.invalid", - // https://netty.io/wiki/reference-counted-objects.html#leak-detection-levels - "-Dio.netty.leakDetection.level=advanced", - "-XX:+UseZGC", - "-XX:+ZGenerational" - ) - // "--renderDebugLabels" is a Mojang command to show render names in RenderDoc - programArgs("--username", "Dev", "--renderDebugLabels") - } - server { - server() - setConfigName("Fabric Server") - ideConfigGenerated(true) - runDir("../run/server") - vmArgs("-Dio.netty.leakDetection.level=advanced") - } - } -} - -remapJar { - inputFile = shadowJar.archiveFile - dependsOn shadowJar + id 'unimined-fabric' } -configurations { - // The addModJar basically embeds the mod to the built jar - addModJar - include.extendsFrom addModJar - modImplementation.extendsFrom addModJar -} - - +// ==================== Dependencies ==================== def addMod(path, enabled) { if (enabled == "2") dependencies { modImplementation(path) } else if (enabled == "1") - dependencies { modCompileOnly(path) } + dependencies { compileOnly(path) } } dependencies { - minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" - mappings loom.layered() { - // Mojmap mappings - officialMojangMappings() - // Parchment mappings (it adds parameter mappings & javadoc) - parchment("org.parchmentmc.data:parchment-${rootProject.parchment_version}@zip") - } // Fabric loader modImplementation "net.fabricmc:fabric-loader:${rootProject.fabric_loader_version}" - -// annotationProcessor "javax.annotation:javax.annotation-api:1.3.2" -// implementation("javax.annotation:javax.annotation-api:1.3.2") -// runtimeOnly "javax.annotation:javax.annotation-api:1.3.2" -// compileOnly "javax.annotation:javax.annotation-api:1.3.2" -// modImplementation "javax.annotation:javax.annotation-api:1.3.2" - - // Fabric API - addModJar(fabricApi.module("fabric-api-base", rootProject.fabric_api_version)) - addModJar(fabricApi.module("fabric-lifecycle-events-v1", rootProject.fabric_api_version)) - if (buildVersionBefore(minecraft_version, "1.21.9")) - { - addModJar(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) - } - else // > 1.21.9 - { - addModJar(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version)) - addModJar(fabricApi.module("fabric-resource-loader-v1", rootProject.fabric_api_version)) - } - addModJar(fabricApi.module("fabric-events-interaction-v0", rootProject.fabric_api_version)) - addModJar(fabricApi.module("fabric-rendering-v1", rootProject.fabric_api_version)) - addModJar(fabricApi.module("fabric-networking-api-v1", rootProject.fabric_api_version)) - addModJar(fabricApi.module("fabric-entity-events-v1", rootProject.fabric_api_version)) - if (buildVersionBefore(minecraft_version, "1.19.2")) - addModJar(fabricApi.module("fabric-command-api-v1", rootProject.fabric_api_version)) - else - addModJar(fabricApi.module("fabric-command-api-v2", rootProject.fabric_api_version)) - - // used by mod menu in MC 1.20.6+ - addModJar(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version)) - addModJar(fabricApi.module("fabric-key-binding-api-v1", rootProject.fabric_api_version)) - + // Fabric API (bundled as jar-in-jar so users don't need to install it separately) + modImplementation "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}" + include "net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}" // Mod Menu addMod("com.terraformersmc:modmenu:${rootProject.modmenu_version}", rootProject.enable_mod_menu) @@ -113,11 +31,7 @@ dependencies { // Sodium addMod("maven.modrinth:sodium:${rootProject.sodium_version}", rootProject.enable_sodium) - if (rootProject.enable_sodium == "2") { - implementation "org.joml:joml:1.10.2" - modImplementation(fabricApi.module("fabric-rendering-data-attachment-v1", rootProject.fabric_api_version)) - modImplementation(fabricApi.module("fabric-rendering-fluids-v1", rootProject.fabric_api_version)) - } + if (rootProject.enable_sodium == "2") implementation "org.joml:joml:1.10.2" // Lithium addMod("maven.modrinth:lithium:${rootProject.lithium_version}", rootProject.enable_lithium) @@ -129,65 +43,32 @@ dependencies { addMod("com.github.quiqueck:BCLib:${rootProject.bclib_version}", rootProject.enable_bclib) // Canvas - addMod("io.vram:canvas-fabric-${project.canvas_version}", rootProject.enable_canvas) + addMod("io.vram:canvas-fabric-${rootProject.canvas_version}", rootProject.enable_canvas) // Immersive Portals if (rootProject.enable_immersive_portals == "1") { - modCompileOnly("com.github.iPortalTeam.ImmersivePortalsMod:imm_ptl_core:${project.immersive_portals_version}") + modCompileOnly("com.github.iPortalTeam.ImmersivePortalsMod:imm_ptl_core:${rootProject.immersive_portals_version}") } else if (rootProject.enable_immersive_portals == "2") { - modImplementation ("com.github.iPortalTeam.ImmersivePortalsMod:imm_ptl_core:${project.immersive_portals_version}") { + modImplementation ("com.github.iPortalTeam.ImmersivePortalsMod:imm_ptl_core:${rootProject.immersive_portals_version}") { exclude(group: "net.fabricmc.fabric-api") transitive(false) } - modImplementation ("com.github.iPortalTeam.ImmersivePortalsMod:q_misc_util:${project.immersive_portals_version}") { + modImplementation("com.github.iPortalTeam.ImmersivePortalsMod:q_misc_util:${rootProject.immersive_portals_version}") { exclude(group: "net.fabricmc.fabric-api") transitive(false) } - modImplementation ("com.github.iPortalTeam.ImmersivePortalsMod:build:${project.immersive_portals_version}") { + modImplementation("com.github.iPortalTeam.ImmersivePortalsMod:build:${rootProject.immersive_portals_version}") { exclude(group: "net.fabricmc.fabric-api") transitive(false) } - modImplementation("net.fabricmc.fabric-api:fabric-api:${rootProject.fabric_api_version}") api("com.github.LlamaLad7:MixinExtras:0.2.0-beta.4") annotationProcessor("com.github.LlamaLad7:MixinExtras:0.2.0-beta.4") } } -private static boolean buildVersionBefore(String minecraft_version, String compareVersion) -{ - int sortValue = sortSemanticVersionOldestToNewest(minecraft_version, compareVersion); - return sortValue == -1; -} -/** - * input format: "major.minor.patch" - * needed so we can sort versions with different length strings - * IE: 1.21.1 should come before 1.21.10 - */ -private static int sortSemanticVersionOldestToNewest(String version1, String version2) -{ - String[] parts1 = version1.split("\\."); - String[] parts2 = version2.split("\\."); - - int major1 = Integer.parseInt(parts1[0]); - int major2 = Integer.parseInt(parts2[0]); - if (major1 != major2) - { - return Integer.compare(major1, major2); - } - - int minor1 = Integer.parseInt(parts1[1]); - int minor2 = Integer.parseInt(parts2[1]); - if (minor1 != minor2) - { - return Integer.compare(minor1, minor2); - } - - int patch1 = Integer.parseInt(parts1[2]); - int patch2 = Integer.parseInt(parts2[2]); - return Integer.compare(patch1, patch2); -} +// ==================== Tasks ==================== task deleteResources(type: Delete) { delete file("build/resources/main") @@ -195,17 +76,15 @@ task deleteResources(type: Delete) { processResources { dependsOn(copyCoreResources) - dependsOn(copyCommonLoaderResources) + // dependsOn(copyCommonLoaderResources) } -runClient { +tasks.named('runClient') { dependsOn(copyCoreResources) - dependsOn(copyCommonLoaderResources) -// jvmArgs([ "-XX:-OmitStackTraceInFastThrow", minecraftMemoryJavaArg ]) + // dependsOn(copyCommonLoaderResources) finalizedBy(deleteResources) } - sourcesJar { def commonSources = project(":common").sourcesJar dependsOn commonSources diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java index 8ec0cfb54..e7ebac4a0 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricClientProxy.java @@ -66,6 +66,13 @@ import java.util.concurrent.AbstractExecutorService; import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents; #endif +#if MC_VER <= MC_1_21_11 +#else +import net.fabricmc.fabric.api.client.rendering.v1.level.LevelRenderContext; +import net.fabricmc.fabric.api.client.rendering.v1.level.LevelRenderEvents; +import net.fabricmc.fabric.api.client.rendering.v1.level.LevelTerrainRenderContext; +#endif + import com.mojang.blaze3d.platform.InputConstants; import net.minecraft.client.multiplayer.ClientLevel; @@ -91,6 +98,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy private static final MinecraftClientWrapper MC = MinecraftClientWrapper.INSTANCE; private static final AbstractPluginPacketSender PACKET_SENDER = (AbstractPluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class); + @Deprecated // just use the static reference private static final ClientApi clientApi = ClientApi.INSTANCE; HashSet previouslyPressKeyCodes = new HashSet<>(); @@ -108,17 +116,10 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy - //========================// - // register mod accessors // - //========================// - - SodiumAccessor sodiumAccessor = (SodiumAccessor) ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class); - - - //==============// // chunk events // //==============// + //region // ClientChunkLoadEvent ClientChunkEvents.CHUNK_LOAD.register((level, chunk) -> @@ -214,12 +215,14 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy return InteractionResult.PASS; }); + //endregion + //==============// // render event // //==============// - + //region #if MC_VER < MC_1_21_9 WorldRenderEvents.AFTER_SETUP.register((renderContext) -> @@ -298,6 +301,27 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy }); #endif + #if MC_VER <= MC_1_21_11 + #else + LevelRenderEvents.AFTER_TRANSLUCENT_TERRAIN.register((LevelRenderContext levelRenderContext) -> + { + ClientApi.INSTANCE.renderFadeTransparent(); + }); + + LevelRenderEvents.AFTER_OPAQUE_TERRAIN.register((LevelTerrainRenderContext levelTerrainRenderContext) -> + { + ClientApi.INSTANCE.renderFadeOpaque(); + }); + #endif + + //endregion + + + + //=================// + // keyboard events // + //=================// + //region // Debug keyboard event // FIXME: Use better hooks so it doesn't trigger key press events in text boxes @@ -309,13 +333,25 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy } }); + //endregion + //==================// // networking event // //==================// + //region - #if MC_VER >= MC_1_20_6 + #if MC_VER < MC_1_20_6 + ClientPlayNetworking.registerGlobalReceiver(AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE, (client, handler, buffer, packetSender) -> + { + AbstractNetworkMessage message = PACKET_SENDER.decodeMessage(buffer); + if (message != null) + { + ClientApi.INSTANCE.pluginMessageReceived(message); + } + }); + #elif MC_VER <= MC_1_21_11 PayloadTypeRegistry.playS2C().register(CommonPacketPayload.TYPE, new CommonPacketPayload.Codec()); ClientPlayNetworking.registerGlobalReceiver(CommonPacketPayload.TYPE, (payload, context) -> { @@ -326,15 +362,21 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy ClientApi.INSTANCE.pluginMessageReceived(payload.message()); }); #else - ClientPlayNetworking.registerGlobalReceiver(AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE, (client, handler, buffer, packetSender) -> + PayloadTypeRegistry.clientboundPlay().register(CommonPacketPayload.TYPE, new CommonPacketPayload.Codec()); + ClientPlayNetworking.registerGlobalReceiver(CommonPacketPayload.TYPE, (payload, context) -> { - AbstractNetworkMessage message = PACKET_SENDER.decodeMessage(buffer); - if (message != null) + if (payload.message() == null) { - ClientApi.INSTANCE.pluginMessageReceived(message); + return; } + ClientApi.INSTANCE.pluginMessageReceived(payload.message()); }); #endif + + //endregion + + + } public void onKeyInput() diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricMain.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricMain.java index 61b97e835..00b995a6c 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricMain.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricMain.java @@ -25,7 +25,7 @@ import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.util.NativeDialogUtil; +import com.seibel.distanthorizons.common.wrappers.gui.NativeDialogUtil; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.*; diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricServerProxy.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricServerProxy.java index 0888f045c..3781fd8c8 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricServerProxy.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/FabricServerProxy.java @@ -1,6 +1,8 @@ package com.seibel.distanthorizons.fabric; import com.seibel.distanthorizons.api.DhApi; +import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBlockColorOverrideEvent; +import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBlockStateWrapperCreatedEvent; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkProcessingEvent; import com.seibel.distanthorizons.api.methods.events.DhApiEventRegister; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent; @@ -16,13 +18,13 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; +import com.seibel.distanthorizons.fabric.testing.TestBlockWrapperCreatedEvent; import com.seibel.distanthorizons.fabric.testing.TestChunkInputReplacerEvent; +import com.seibel.distanthorizons.fabric.testing.TestCustomColorEvent; import com.seibel.distanthorizons.fabric.testing.TestWorldGenBindingEvent; -import net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; -import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents; import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents; import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking; import net.minecraft.client.Minecraft; @@ -39,6 +41,14 @@ import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry; import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage; #endif +#if MC_VER <= MC_1_21_11 +import net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents; +#else +import net.fabricmc.fabric.api.entity.event.v1.ServerEntityLevelChangeEvents; +import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLevelEvents; +#endif + /** * This handles all events sent to the server, * and is the starting point for most of the mod. @@ -81,11 +91,21 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy /* Register the mod needed event callbacks */ - // can be enabled to test overrides/events without having to build a separate API project - if (false) + // can be enabled to test overrides/events without having to build a separate API project { - DhApiEventRegister.on(DhApiLevelLoadEvent.class, new TestWorldGenBindingEvent()); - DhApi.events.bind(DhApiChunkProcessingEvent.class, new TestChunkInputReplacerEvent()); + // test custom world gen + if (false) + { + DhApiEventRegister.on(DhApiLevelLoadEvent.class, new TestWorldGenBindingEvent()); + DhApi.events.bind(DhApiChunkProcessingEvent.class, new TestChunkInputReplacerEvent()); + } + + // test custom colors + if (false) + { + DhApi.events.bind(DhApiBlockColorOverrideEvent.class, new TestCustomColorEvent()); + DhApi.events.bind(DhApiBlockStateWrapperCreatedEvent.class, new TestBlockWrapperCreatedEvent()); + } } @@ -101,18 +121,31 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy }); // ServerLevelLoadEvent + #if MC_VER <= MC_1_21_11 ServerWorldEvents.LOAD.register((server, level) -> + #else + ServerLevelEvents.LOAD.register((server, level) -> + #endif { ServerApi.INSTANCE.serverLevelLoadEvent(this.getServerLevelWrapper(level)); }); + // ServerLevelUnloadEvent + #if MC_VER <= MC_1_21_11 ServerWorldEvents.UNLOAD.register((server, level) -> + #else + ServerLevelEvents.UNLOAD.register((server, level) -> + #endif { ServerApi.INSTANCE.serverLevelUnloadEvent(this.getServerLevelWrapper(level)); }); // ServerChunkLoadEvent + #if MC_VER <= MC_1_21_11 ServerChunkEvents.CHUNK_LOAD.register((server, chunk) -> + #else + ServerChunkEvents.CHUNK_LOAD.register((server, chunk, generated) -> + #endif { ILevelWrapper level = this.getServerLevelWrapper((ServerLevel) chunk.getLevel()); ServerApi.INSTANCE.serverChunkLoadEvent( @@ -129,7 +162,12 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy { ServerApi.INSTANCE.serverPlayerDisconnectEvent(this.getServerPlayerWrapper(handler.player)); }); + + #if MC_VER <= MC_1_21_11 ServerEntityWorldChangeEvents.AFTER_PLAYER_CHANGE_WORLD.register((player, originLevel, destinationLevel) -> + #else + ServerEntityLevelChangeEvents.AFTER_PLAYER_CHANGE_LEVEL.register((player, originLevel, destinationLevel) -> + #endif { ServerApi.INSTANCE.serverPlayerLevelChangeEvent( this.getServerPlayerWrapper(player), @@ -138,7 +176,16 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy ); }); - #if MC_VER >= MC_1_20_6 + #if MC_VER < MC_1_20_6 + ServerPlayNetworking.registerGlobalReceiver(AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE, (server, serverPlayer, handler, buffer, packetSender) -> + { + AbstractNetworkMessage message = PACKET_SENDER.decodeMessage(buffer); + if (message != null) + { + ServerApi.INSTANCE.pluginMessageReceived(ServerPlayerWrapper.getWrapper(serverPlayer), message); + } + }); + #elif MC_VER <= MC_1_21_11 PayloadTypeRegistry.playC2S().register(CommonPacketPayload.TYPE, new CommonPacketPayload.Codec()); if (this.isDedicatedServer) { @@ -154,13 +201,19 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy ServerApi.INSTANCE.pluginMessageReceived(ServerPlayerWrapper.getWrapper(context.player()), payload.message()); }); #else - ServerPlayNetworking.registerGlobalReceiver(AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE, (server, serverPlayer, handler, buffer, packetSender) -> + PayloadTypeRegistry.serverboundPlay().register(CommonPacketPayload.TYPE, new CommonPacketPayload.Codec()); + if (this.isDedicatedServer) { - AbstractNetworkMessage message = PACKET_SENDER.decodeMessage(buffer); - if (message != null) + PayloadTypeRegistry.clientboundPlay().register(CommonPacketPayload.TYPE, new CommonPacketPayload.Codec()); + } + + ServerPlayNetworking.registerGlobalReceiver(CommonPacketPayload.TYPE, (payload, context) -> + { + if (payload.message() == null) { - ServerApi.INSTANCE.pluginMessageReceived(ServerPlayerWrapper.getWrapper(serverPlayer), message); + return; } + ServerApi.INSTANCE.pluginMessageReceived(ServerPlayerWrapper.getWrapper(context.player()), payload.message()); }); #endif } diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinChunkSectionsToRender.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinChunkSectionsToRender.java index 2581b5322..4c9791bdc 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinChunkSectionsToRender.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinChunkSectionsToRender.java @@ -47,6 +47,11 @@ import com.mojang.blaze3d.textures.GpuSampler; public class MixinChunkSectionsToRender { + //===========// + // Pre MC 26 // + //===========// + #if MC_VER <= MC_1_21_11 + //region #if MC_VER <= MC_1_21_10 // needs to fire at HEAD with a lower than normal order (less than 1000) @@ -73,6 +78,38 @@ public class MixinChunkSectionsToRender ClientApi.INSTANCE.renderFadeTransparent(); } } + //endregion + #else + + + + //============// + // post MC 26 // + //============// + //region + + // needs to fire at HEAD with a lower than normal order (less than 1000) + // otherwise it will be canceled by Sodium + @Inject(at = @At("HEAD"), method = "renderGroup", order = 800) + private void renderDeferredLayerHead(ChunkSectionLayerGroup chunkSectionLayerGroup, GpuSampler gpuSampler, CallbackInfo ci) + { + ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, Minecraft.getInstance().levelRenderer.level); + + + ClientApi.RENDER_STATE.canRenderOrThrow(); + + if (chunkSectionLayerGroup == ChunkSectionLayerGroup.TRANSLUCENT) + { + ClientApi.INSTANCE.renderDeferredLodsForShaders(); + } + else if (chunkSectionLayerGroup == ChunkSectionLayerGroup.OPAQUE) + { + ClientApi.INSTANCE.renderLods(); + } + } + + //endregion + #endif diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinFogRenderer.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinFogRenderer.java index d5c433ae5..31fb7615a 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinFogRenderer.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinFogRenderer.java @@ -19,19 +19,13 @@ package com.seibel.distanthorizons.fabric.mixins.client; +import com.seibel.distanthorizons.common.commonMixins.MixinVanillaFogCommon; import com.seibel.distanthorizons.core.api.internal.ClientApi; -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; -import net.minecraft.client.Minecraft; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import net.minecraft.client.Camera; -import net.minecraft.world.effect.MobEffects; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; #if MC_VER < MC_1_17_1 import net.minecraft.world.level.material.FluidState; @@ -42,7 +36,6 @@ import com.mojang.blaze3d.systems.RenderSystem; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; #elif MC_VER < MC_1_21_3 -import net.minecraft.world.level.material.FogType; import net.minecraft.client.renderer.FogRenderer; import net.minecraft.client.renderer.FogRenderer.FogMode; import com.mojang.blaze3d.systems.RenderSystem; @@ -96,11 +89,11 @@ public class MixinFogRenderer #endif { #if MC_VER < MC_1_21_6 - boolean cancelFog = cancelFog(camera, fogMode); + boolean cancelFog = MixinVanillaFogCommon.cancelFog(camera, fogMode); #elif MC_VER < MC_1_21_6 - boolean cancelFog = cancelFog(camera); + boolean cancelFog = MixinVanillaFogCommon.cancelFog(camera); #else - boolean cancelFog = cancelFog(); + boolean cancelFog = MixinVanillaFogCommon.cancelFog(); #endif if (cancelFog) @@ -142,7 +135,7 @@ public class MixinFogRenderer ) private void onSetRenderDistanceEnd(FogData instance, float value, Operation original) { - if (cancelFog()) + if (MixinVanillaFogCommon.cancelFog()) { instance.environmentalStart = A_REALLY_REALLY_BIG_VALUE; instance.environmentalEnd = A_EVEN_LARGER_VALUE; @@ -164,52 +157,5 @@ public class MixinFogRenderer #endif - @Unique - #if MC_VER < MC_1_21_6 - private static boolean cancelFog(Camera camera, FogMode fogMode) - #else - private static boolean cancelFog() - #endif - { - #if MC_VER < MC_1_21_6 - Entity entity = camera.getEntity(); - #elif MC_VER <= MC_1_21_10 - Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); - Entity entity = camera.getEntity(); - #else - Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); - Entity entity = camera.entity(); - #endif - - - boolean cameraNotInFluid = cameraNotInFluid(camera); - boolean isSpecialFog = (entity instanceof LivingEntity) && ((LivingEntity) entity).hasEffect(MobEffects.BLINDNESS); - - boolean cancelFog = !isSpecialFog; - cancelFog = cancelFog && cameraNotInFluid; - #if MC_VER < MC_1_21_6 - cancelFog = cancelFog && (fogMode == FogMode.FOG_TERRAIN); - #endif - cancelFog = cancelFog && !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial(); - cancelFog = cancelFog && !Config.Client.Advanced.Graphics.Fog.enableVanillaFog.get(); - - return cancelFog; - } - - @Unique - private static boolean cameraNotInFluid(Camera camera) - { - #if MC_VER < MC_1_17_1 - FluidState fluidState = camera.getFluidInCamera(); - boolean cameraNotInFluid = fluidState.isEmpty(); - #else - FogType fogTypes = camera.getFluidInCamera(); - boolean cameraNotInFluid = fogTypes == FogType.NONE; - #endif - - return cameraNotInFluid; - } - - } diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinGameRenderer.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinGameRenderer.java new file mode 100644 index 000000000..efd9c3a89 --- /dev/null +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinGameRenderer.java @@ -0,0 +1,77 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.fabric.mixins.client; + +#if MC_VER <= MC_1_21_11 +import net.minecraft.world.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(Entity.class) +public class MixinGameRenderer {} + +#else +import com.mojang.blaze3d.vertex.PoseStack; +import com.seibel.distanthorizons.common.wrappers.McObjectConverter; +import com.seibel.distanthorizons.core.api.internal.ClientApi; +import net.minecraft.client.DeltaTracker; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.state.OptionsRenderState; +import net.minecraft.client.renderer.state.level.CameraRenderState; +import net.minecraft.util.profiling.ProfilerFiller; +import org.joml.Matrix4f; +import org.joml.Matrix4fc; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(GameRenderer.class) +public class MixinGameRenderer +{ + // get the modified projection matrix right before it's uploaded to the GPU + @Inject( + method = "renderLevel", + at = @At( + value = "INVOKE", + target = "Lcom/mojang/blaze3d/systems/RenderSystem;setProjectionMatrix(Lcom/mojang/blaze3d/buffers/GpuBufferSlice;Lcom/mojang/blaze3d/ProjectionType;)V", + shift = At.Shift.BEFORE + ), + locals = LocalCapture.CAPTURE_FAILHARD + ) + private void renderLevel( + final DeltaTracker deltaTracker, + final CallbackInfo callback, + final float partialTickTime, + final float cameraEntityPartialTicks, + final LocalPlayer player, + final ProfilerFiller profiler, + final boolean renderBlockOutline, + final OptionsRenderState options, + final CameraRenderState camera, + final Matrix4fc modelViewMatrix, + final Matrix4f projectionMatrix, + final PoseStack poseStack) + { + ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix); + } +} +#endif diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinLevelRenderer.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinLevelRenderer.java index d84e5b61d..4fe893927 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinLevelRenderer.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinLevelRenderer.java @@ -47,10 +47,9 @@ import org.joml.Matrix4fc; import org.joml.Vector4f; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; -#else +#elif MC_VER <= MC_1_21_11 import com.mojang.blaze3d.buffers.GpuBufferSlice; import com.mojang.blaze3d.resource.GraphicsResourceAllocator; -import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper; import net.minecraft.client.Camera; import net.minecraft.client.DeltaTracker; import net.minecraft.client.renderer.chunk.ChunkSectionsToRender; @@ -59,18 +58,29 @@ import org.joml.Matrix4fc; import org.joml.Vector4f; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +#else +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.resource.GraphicsResourceAllocator; +import net.minecraft.client.DeltaTracker; +import net.minecraft.client.renderer.chunk.ChunkSectionsToRender; +import org.joml.Matrix4fc; +import org.joml.Vector4f; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +import net.minecraft.client.renderer.state.level.CameraRenderState; #endif +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper; import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; -import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper; import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import net.minecraft.client.Minecraft; + import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.renderer.LevelRenderer; + import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Unique; @@ -91,6 +101,13 @@ public class MixinLevelRenderer private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + //===========// + // Pre MC 26 // + //===========// + //region + #if MC_VER <= MC_1_21_11 + #if MC_VER < MC_1_17_1 @Inject(at = @At("HEAD"), method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDD)V", @@ -128,7 +145,7 @@ public class MixinLevelRenderer Vector4f skyColor, boolean thinFog, CallbackInfo callback) #endif { - #if MC_VER == MC_1_16_5 + #if MC_VER <= MC_1_16_5 // get the matrices from the OpenGL fixed pipeline float[] mcProjMatrixRaw = new float[16]; GL32.glGetFloatv(GL32.GL_PROJECTION_MATRIX, mcProjMatrixRaw); @@ -190,6 +207,42 @@ public class MixinLevelRenderer } #endif + #endif + //endregion + + + + //============// + // post MC 26 // + //============// + //region + + #if MC_VER <= MC_1_21_11 + #else + + @Inject(at = @At("HEAD"), method = "prepareChunkRenders") + private void prepareChunkRenders(final Matrix4fc modelViewMatrix, CallbackInfoReturnable callback) + { + ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, this.level); + } + + @Inject(at = @At("HEAD"), method = "renderLevel") + public void renderLevel( + final GraphicsResourceAllocator resourceAllocator, final DeltaTracker deltaTracker, + final boolean renderBlockOutline, final CameraRenderState camera, + final Matrix4fc modelViewMatrix, final GpuBufferSlice terrainFog, + final Vector4f fogColor, final boolean shouldRenderSky, + final ChunkSectionsToRender chunkSectionsToRender, + CallbackInfo callback) + { + ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrix); + + ClientApi.RENDER_STATE.partialTickTime = MinecraftRenderWrapper.INSTANCE.getPartialTickTime(); + + } + + #endif + //endregion diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinLightTexture.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinLightTexture.java index 8329d4148..203052ddc 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinLightTexture.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinLightTexture.java @@ -24,7 +24,6 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; -import net.minecraft.client.renderer.LightTexture; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; @@ -43,8 +42,19 @@ import com.mojang.blaze3d.opengl.GlTexture; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.GpuTexture; #endif +#if MC_VER <= MC_1_21_11 +import net.minecraft.client.renderer.LightTexture; +#else +import net.minecraft.client.renderer.state.LightmapRenderState; +import net.minecraft.client.renderer.Lightmap; +#endif + +#if MC_VER <= MC_1_21_11 @Mixin(LightTexture.class) +#else +@Mixin(Lightmap.class) +#endif public class MixinLightTexture { @@ -67,8 +77,13 @@ public class MixinLightTexture + #if MC_VER <= MC_1_21_11 @Inject(method = "updateLightTexture(F)V", at = @At("RETURN")) public void updateLightTexture(float partialTicks, CallbackInfo ci) + #else + @Inject(method = "render(Lnet/minecraft/client/renderer/state/LightmapRenderState;)V", at = @At("RETURN")) + public void render(LightmapRenderState renderState, CallbackInfo ci) + #endif { IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); if (mc == null) diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinSharedConstants.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinSharedConstants.java index c747d8335..eff393c7f 100644 --- a/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinSharedConstants.java +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/mixins/client/MixinSharedConstants.java @@ -1,25 +1,23 @@ package com.seibel.distanthorizons.fabric.mixins.client; -import com.seibel.distanthorizons.common.commonMixins.DhUpdateScreenBase; -import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; -import com.seibel.distanthorizons.core.api.internal.ClientApi; -import com.seibel.distanthorizons.core.jar.updater.SelfUpdater; +#if MC_VER <= MC_1_21_10 +import net.minecraft.world.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(Entity.class) +public class MixinSharedConstants +{ /* not present in older MC versions */ } +#else + import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.coreapi.ModInfo; import net.minecraft.SharedConstants; -import net.minecraft.client.Minecraft; -import net.minecraft.client.multiplayer.ClientLevel; import org.spongepowered.asm.mixin.*; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; -import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; -/** - * At the moment this is only used for the auto updater - * - * @author coolGi - */ @Mixin(SharedConstants.class) public abstract class MixinSharedConstants { @@ -29,7 +27,35 @@ public abstract class MixinSharedConstants @Inject(method = "", at = @At("TAIL")) private static void setIsRunningInIde(CallbackInfo ci) { - IS_RUNNING_IN_IDE = true; + DhLogger logger = new DhLoggerBuilder().name("SharedConstants").build(); + + // setting IS_RUNNING_IN_IDE to true enables + // additional validation on Mojang's side which + // helps catch errors when developing for Blaze3D + + boolean irisPresent; + #if MC_VER <= MC_1_21_11 + IS_RUNNING_IN_IDE = ModInfo.IS_DEV_BUILD; + #else + try + { + // Iris has a bug for MC 26 and newer where it doesn't have + // a "sampler1" bound, causing a renderer crash if + // Blaze3D validation is enabled (which is enabled by if + // IS_RUNNING_IN_IDE is true) + ModInfo.class.getClassLoader().loadClass("net.irisshaders.iris.api.v0.IrisApi"); + irisPresent = true; + } + catch (ClassNotFoundException ignore) + { + irisPresent = false; + } + + IS_RUNNING_IN_IDE = ModInfo.IS_DEV_BUILD && !irisPresent; + #endif + + logger.info("Setting Minecraft's SharedConstants.IS_RUNNING_IN_IDE to ["+IS_RUNNING_IN_IDE+"]"); } } +#endif diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/testing/TestBlockWrapperCreatedEvent.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/testing/TestBlockWrapperCreatedEvent.java new file mode 100644 index 000000000..d703fff5b --- /dev/null +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/testing/TestBlockWrapperCreatedEvent.java @@ -0,0 +1,43 @@ +package com.seibel.distanthorizons.fabric.testing; + +import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBlockStateWrapperCreatedEvent; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.util.LodUtil; + +import java.util.Random; + +/** + * @see TestCustomColorEvent + */ +public class TestBlockWrapperCreatedEvent extends DhApiBlockStateWrapperCreatedEvent +{ + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + + + + @Override + public void blockStateWrapperCreated(DhApiEventParam event) + { + EventParam eventParam = event.value; + + // can be enabled to flip the opacity of transparent/opaque blocks + if (false) + { + if (eventParam.getBlockStateWrapper().getOpacity() == LodUtil.BLOCK_FULLY_OPAQUE) + { + eventParam.setOpacity(LodUtil.BLOCK_FULLY_TRANSPARENT); + } + else + { + eventParam.setOpacity(LodUtil.BLOCK_FULLY_OPAQUE); + } + } + + // needed for TestCustomColorEvent + eventParam.setAllowApiColorOverride(true); + } + +} diff --git a/fabric/src/main/java/com/seibel/distanthorizons/fabric/testing/TestCustomColorEvent.java b/fabric/src/main/java/com/seibel/distanthorizons/fabric/testing/TestCustomColorEvent.java new file mode 100644 index 000000000..7c08334ff --- /dev/null +++ b/fabric/src/main/java/com/seibel/distanthorizons/fabric/testing/TestCustomColorEvent.java @@ -0,0 +1,86 @@ +package com.seibel.distanthorizons.fabric.testing; + +import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBlockColorOverrideEvent; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam; +import com.seibel.distanthorizons.core.logging.DhLogger; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.util.LodUtil; +import com.seibel.distanthorizons.coreapi.util.ColorUtil; + +import java.awt.*; + +/** + * @see TestBlockWrapperCreatedEvent + */ +public class TestCustomColorEvent extends DhApiBlockColorOverrideEvent +{ + private static final DhLogger LOGGER = new DhLoggerBuilder().build(); + + + + @Override + public void blockStateWrapperCreated(DhApiEventParam event) + { + EventParam eventParam = event.value; + + //randomDatapointColors(eventParam); + //randomPerBlockColors(eventParam); + //blackWhitePositionStripe(eventParam); + positionRainbow(eventParam); + } + + /** each datapoint has a random color */ + private void randomDatapointColors(EventParam eventParam) + { + // random colors for each datapoint + int a = eventParam.getAlpha(); + int r = eventParam.getRed(); + int g = eventParam.getGreen(); + int b = eventParam.getBlue(); + + if (eventParam.getBlockStateWrapper().getOpacity() == LodUtil.BLOCK_FULLY_OPAQUE) + { + eventParam.setColor(255,r,g,b); + } + else + { + eventParam.setColor(60,r,g,b); + } + } + + /** each block has a different color */ + private void randomPerBlockColors(EventParam eventParam) + { + // random colors per block + int r = Math.abs(eventParam.getBlockStateWrapper().hashCode() % 255); + int g = Math.abs((eventParam.getBlockStateWrapper().hashCode() << 4) % 255); + int b = Math.abs((eventParam.getBlockStateWrapper().hashCode() << 8) % 255); + eventParam.setColor(r,g,b); + } + + private void blackWhitePositionStripe(EventParam eventParam) + { + // black-white stripes + int r = Math.abs(eventParam.getBlockPosX() % 255); + int g = r; + int b = r; + eventParam.setColor(r,g,b); + } + + /** rainbow along the X axis repeating every 255 blocks */ + private void positionRainbow(EventParam eventParam) + { + float[] ahsv = ColorUtil.argbToAhsv(ColorUtil.RED); + float a = ahsv[0]; + + int xModPos = Math.abs(eventParam.getBlockPosX() % 510); + float h = xModPos < 255 ? xModPos : 510 - xModPos; + float s = ahsv[2]; + float v = ahsv[3]; + int colorInt = ColorUtil.ahsvToArgb(a,h,s,v); + eventParam.setColor(ColorUtil.getRed(colorInt),ColorUtil.getGreen(colorInt),ColorUtil.getBlue(colorInt)); + } + + + +} diff --git a/fabric/src/main/resources/DistantHorizons.fabric.mixins.json b/fabric/src/main/resources/DistantHorizons.fabric.mixins.json index 92f07d61c..1339e0f7a 100644 --- a/fabric/src/main/resources/DistantHorizons.fabric.mixins.json +++ b/fabric/src/main/resources/DistantHorizons.fabric.mixins.json @@ -16,6 +16,7 @@ "client.MixinClientPacketListener", "client.MixinDebugScreenOverlay", "client.MixinFogRenderer", + "client.MixinGameRenderer", "client.MixinLevelRenderer", "client.MixinChunkSectionsToRender", "client.MixinLightTexture", diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index db15ae4bf..36d0c1ec3 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -19,7 +19,7 @@ "license": "LGPL-3", "icon": "assets/distanthorizons/icon.png", - "accessWidener": "distanthorizons.accesswidener", + "accessWidener": "${accessWidenerVersion}.distanthorizons.accesswidener", "environment": "*", "entrypoints": { diff --git a/forge/build.gradle b/forge/build.gradle index e9fc94579..39913fb5b 100644 --- a/forge/build.gradle +++ b/forge/build.gradle @@ -1,114 +1,48 @@ plugins { - // Note: This is only needed for multi-loader projects - // The main architectury loom version is set at the start of the root build.gradle - id "architectury-plugin" version "3.4-SNAPSHOT" + id 'unimined-forge' } -sourceCompatibility = targetCompatibility = JavaVersion.VERSION_21 -architectury { - platformSetupLoomIde() - forge() -} - -//loom { -// forge { -// convertAccessWideners.set(true) -// extraAccessWideners.add("lod.accesswidener") -// mixinConfigs("DistantHorizons.mixins.json") -// } -//} - -loom { - silentMojangMappingsLicense() // Shut the licencing warning - accessWidenerPath = project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener") - - forge { - convertAccessWideners = true - extraAccessWideners.add loom.accessWidenerPath.get().asFile.name - - mixinConfigs = [ - "DistantHorizons.forge.mixins.json" - ] - } - - // "runs" isn't required, but when we do need it then it can be useful - runs { - client { - client() - setConfigName("Forge Client") - ideConfigGenerated(false) // When true a run configuration file will be generated for IDE's. By default only set to true for the root project. - runDir("../run/client") - vmArgs( - // https://github.com/FabricMC/fabric-loom/issues/915#issuecomment-1609154390 - "-Dminecraft.api.auth.host=https://nope.invalid", - "-Dminecraft.api.account.host=https://nope.invalid", - "-Dminecraft.api.session.host=https://nope.invalid", - "-Dminecraft.api.services.host=https://nope.invalid", - // https://netty.io/wiki/reference-counted-objects.html#leak-detection-levels - "-Dio.netty.leakDetection.level=advanced" - ) - programArgs("--username", "Dev") - } - server { - server() - setConfigName("Forge Server") - ideConfigGenerated(false) - runDir("../run/server") - vmArgs("-Dio.netty.leakDetection.level=advanced") - } - } -} - -remapJar { - inputFile = shadowJar.archiveFile - dependsOn shadowJar -} +// ==================== Mod Dependency Helper ==================== def addMod(path, enabled) { if (enabled == "2") - dependencies { implementation(path) } + dependencies { modImplementation(path) } else if (enabled == "1") - dependencies { modCompileOnly(path) } + dependencies { compileOnly(path) } } + +// ==================== Dependencies ==================== + dependencies { - minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" - mappings loom.layered() { - // Mojmap mappings - officialMojangMappings() - // Parchment mappings (it adds parameter mappings & javadoc) - parchment("org.parchmentmc.data:parchment-${rootProject.parchment_version}@zip") - } - - // Forge - forge "net.minecraftforge:forge:${rootProject.minecraft_version}-${rootProject.forge_version}" - + // TerraForged addMod("curse.maven:TerraForged-363820:${rootProject.terraforged_version}", rootProject.enable_terraforged) - - addMod("curse.maven:TerraFirmaCraft-302973:4616004", rootProject.enable_terrafirmacraft) - - if ( // Only run on MC 1.20.6 or later - // FIXME: Add an environment variable for the Major, Minor, and Patch version number of Minecraft - minecraft_version.split("\\.")[1].toInteger() >= 20 && - ( - minecraft_version.split("\\.").length > 1 && // Incase there isn't a minor version - minecraft_version.split("\\.")[2].toInteger() >= 6 - ) - ) { - // (potential) hack fix, force jopt-simple to be exactly 5.0.4 because Mojang ships that version, but some transitive dependencies request 6.0+ - implementation('net.sf.jopt-simple:jopt-simple:5.0.4') //{ version { strictly '5.0.4' } } + // TerraFirmaCraft + addMod("curse.maven:TerraFirmaCraft-302973:4616004", rootProject.enable_terrafirmacraft) + + // Oculus (Iris port) + addMod("maven.modrinth:oculus:${rootProject.oculus_version}", rootProject.enable_oculus) + + // TODO: Check if this is still needed and if so ensure this code works for MC 26.1+ + // (potential) hack fix for MC 1.20.6 and later, force jopt-simple to be exactly 5.0.4 because Mojang ships that version, but some transitive dependencies request 6.0+ + def mcParts = rootProject.minecraft_version.split("\\.") + if (mcParts[1].toInteger() >= 20 && (mcParts.length > 2 && mcParts[2].toInteger() >= 6)) { + implementation('net.sf.jopt-simple:jopt-simple:5.0.4') } } + +// ==================== Tasks ==================== + task deleteResources(type: Delete) { delete file("build/resources/main") } tasks.register('copyAllResources') { dependsOn(copyCoreResources) - dependsOn(copyCommonLoaderResources) + // dependsOn(copyCommonLoaderResources) } processResources { @@ -119,4 +53,3 @@ tasks.named('runClient') { dependsOn(tasks.named('copyAllResources')) finalizedBy(deleteResources) } - diff --git a/forge/gradle.properties b/forge/gradle.properties deleted file mode 100644 index 32f842a63..000000000 --- a/forge/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -loom.platform=forge \ No newline at end of file diff --git a/forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinFogRenderer.java b/forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinFogRenderer.java index 7899d4a14..589d50bdf 100644 --- a/forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinFogRenderer.java +++ b/forge/src/main/java/com/seibel/distanthorizons/forge/mixins/client/MixinFogRenderer.java @@ -19,10 +19,8 @@ package com.seibel.distanthorizons.forge.mixins.client; +import com.seibel.distanthorizons.common.commonMixins.MixinVanillaFogCommon; import com.seibel.distanthorizons.core.api.internal.ClientApi; -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; @@ -33,14 +31,6 @@ import com.mojang.blaze3d.systems.RenderSystem; import net.minecraft.client.Camera; import net.minecraft.client.renderer.FogRenderer; import net.minecraft.client.renderer.FogRenderer.FogMode; -import net.minecraft.world.effect.MobEffects; -import net.minecraft.world.entity.Entity; -import net.minecraft.world.entity.LivingEntity; -#if MC_VER < MC_1_17_1 -import net.minecraft.world.level.material.FluidState; -#else -import net.minecraft.world.level.material.FogType; -#endif @Mixin(FogRenderer.class) public class MixinFogRenderer @@ -55,29 +45,25 @@ public class MixinFogRenderer remap = #if MC_VER == MC_1_17_1 || MC_VER == MC_1_18_2 false #else true #endif ) // Remap messiness due to this being weird in forge private static void disableSetupFog(Camera camera, FogMode fogMode, float f, boolean bl, float partTick, CallbackInfo callback) { - #if MC_VER < MC_1_17_1 - FluidState fluidState = camera.getFluidInCamera(); - boolean cameraNotInFluid = fluidState.isEmpty(); + #if MC_VER < MC_1_21_6 + boolean cancelFog = MixinVanillaFogCommon.cancelFog(camera, fogMode); + #elif MC_VER < MC_1_21_6 + boolean cancelFog = MixinVanillaFogCommon.cancelFog(camera); #else - FogType fogTypes = camera.getFluidInCamera(); - boolean cameraNotInFluid = fogTypes == FogType.NONE; + boolean cancelFog = MixinVanillaFogCommon.cancelFog(); #endif - - Entity entity = camera.getEntity(); - boolean isSpecialFog = (entity instanceof LivingEntity) && ((LivingEntity) entity).hasEffect(MobEffects.BLINDNESS); - if (!isSpecialFog - && cameraNotInFluid - && fogMode == FogMode.FOG_TERRAIN - && !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial() - && !Config.Client.Advanced.Graphics.Fog.enableVanillaFog.get()) + if (cancelFog) { #if MC_VER < MC_1_17_1 RenderSystem.fogStart(A_REALLY_REALLY_BIG_VALUE); RenderSystem.fogEnd(A_EVEN_LARGER_VALUE); - #else + #elif MC_VER < MC_1_21_3 RenderSystem.setShaderFogStart(A_REALLY_REALLY_BIG_VALUE); RenderSystem.setShaderFogEnd(A_EVEN_LARGER_VALUE); + #elif MC_VER < MC_1_21_6 + callback.setReturnValue(FogParameters.NO_FOG); + #else #endif ClientApi.RENDER_STATE.vanillaFogEnabled = false; @@ -86,6 +72,7 @@ public class MixinFogRenderer { ClientApi.RENDER_STATE.vanillaFogEnabled = true; } + } } diff --git a/forge/src/main/java/com/seibel/distanthorizons/forge/wrappers/modAccessor/OculusAccessor.java b/forge/src/main/java/com/seibel/distanthorizons/forge/wrappers/modAccessor/OculusAccessor.java index e0a031b66..adadd2ad8 100644 --- a/forge/src/main/java/com/seibel/distanthorizons/forge/wrappers/modAccessor/OculusAccessor.java +++ b/forge/src/main/java/com/seibel/distanthorizons/forge/wrappers/modAccessor/OculusAccessor.java @@ -23,35 +23,50 @@ import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor; +#if MC_VER == MC_1_20_1 +import net.irisshaders.iris.Iris; +import net.irisshaders.iris.api.v0.IrisApi; +#else +#endif + public class OculusAccessor implements IIrisAccessor { protected static final DhLogger LOGGER = new DhLoggerBuilder().build(); public OculusAccessor() - { - LOGGER.warn("Partial Oculus support enabled. Some DH features may be disabled or behave strangely, use Iris instead if possible."); - } + { LOGGER.warn("Partial Oculus support enabled. Some DH features may be disabled or behave strangely, use Iris instead if possible."); } @Override public String getModName() { - return "oculus"; + #if MC_VER == MC_1_20_1 + return Iris.MODID; + #else + return "iris"; // Oculus doesn't support this MC version + #endif } @Override public boolean isShaderPackInUse() { - // assume shaders are always active - return true; + #if MC_VER == MC_1_20_1 + return IrisApi.getInstance().isShaderPackInUse(); + #else + return true; // Oculus doesn't support this MC version + #endif } @Override public boolean isRenderingShadowPass() { - return false; + #if MC_VER == MC_1_20_1 + return IrisApi.getInstance().isRenderingShadowPass(); + #else + return false; // Oculus doesn't support this MC version + #endif } } diff --git a/forge/src/main/resources/pack.mcmeta b/forge/src/main/resources/pack.mcmeta index 37621f6f9..f48d006ff 100644 --- a/forge/src/main/resources/pack.mcmeta +++ b/forge/src/main/resources/pack.mcmeta @@ -1,10 +1,12 @@ { "pack": { - "pack_format": 7, + "pack_format": 64, "supported_formats": { - "min_inclusive": 16, + "min_inclusive": 64, "max_inclusive": 90000 }, - "description": "Distant Horizons" + "description": "Distant Horizons", + "min_format": 64, + "max_format": 90000 } } diff --git a/gradle.properties b/gradle.properties index 7cc0dc5db..1287140ff 100644 --- a/gradle.properties +++ b/gradle.properties @@ -5,8 +5,9 @@ org.gradle.caching=true # Mod Info mod_name=DistantHorizons -mod_version=3.0.0-b-dev -api_version=6.0.0 +api_name=DistantHorizonsApi +mod_version=3.0.4-b-dev +api_version=6.1.1 maven_group=com.seibel.distanthorizons mod_readable_name=Distant Horizons mod_id=distanthorizons @@ -14,14 +15,14 @@ mod_description=This mod generates and renders simplified terrain beyond the nor # Note: In forge's mods.toml this is hard coded because Architectury throws an error with setting it as a variable mod_authors=["James Seibel", "Leonardo Amato", "Cola", "coolGi", "Ran", "Leetom", "pshsh"] mod_homepage=https://modrinth.com/mod/distanthorizons -mod_source=https://gitlab.com/jeseibel/distant-horizons -mod_issues=https://gitlab.com/jeseibel/distant-horizons/-/issues +mod_source=https://gitlab.com/distant-horizons-team/distant-horizons/ +mod_issues=https://gitlab.com/distant-horizons-team/distant-horizons/-/issues mod_discord=https://discord.gg/xAB8G4cENx # Global Plugin Versions -manifold_version=2025.1.31 +manifold_version=2026.1.6 # 2023.1.17 can be used if there are mystery Java compiler issues -nightconfig_version=3.6.6 +nightconfig_version=3.8.3 lz4_version=1.8.0 xz_version=1.9 zstd_version=1.5.7-6 @@ -35,11 +36,6 @@ fastutil_version=8.2.1 log4j_version=2.23.1 joml_version=1.10.2 -# Architectury config -# this is necessary for MC 1.21.3 because including Sodium and Iris throws "Mod was built with a newer version of Loom (1.8.9), you are using Loom (1.7.415)" -loom.ignoreDependencyLoomVersionValidation=true - - # These are here so they can be changed with cmd arguments # If they are null, they would be automatically set # (This is mainly used for the CI) @@ -52,7 +48,10 @@ versionStr= # This defines what MC version Intellij will use for the preprocessor # and what version is used automatically by build and run commands -mcVer=1.21.11 +mcVer=26.1.2 # Defines the maximum amount of memory Minecraft is allowed when run in a development environment -#minecraftMemoryJavaArg="-Xmx4G" +minecraftMemoryJavaArg=-Xmx6G +# can be enabled for use with the Tracy profiler, disabled by default since it causes constant memory growth when running +minecraftEnableTracy=false + diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index cea7a793a..c61a118f7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/neoforge/build.gradle b/neoforge/build.gradle index b623e98cc..707e4ea21 100644 --- a/neoforge/build.gradle +++ b/neoforge/build.gradle @@ -1,104 +1,43 @@ plugins { - // Note: This is only needed for multi-loader projects - // The main architectury loom version is set at the start of the root build.gradle - id "architectury-plugin" version "3.4-SNAPSHOT" -} - -sourceCompatibility = targetCompatibility = JavaVersion.VERSION_17 - -architectury { - platformSetupLoomIde() - neoForge() -} - -loom { - silentMojangMappingsLicense() // Shut the licencing warning - accessWidenerPath = project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener") - - neoForge { - // Access wideners are defined in the `remapJar.atAccessWideners` - - // Mixins are defined in the `mods.toml` - } - mixin { - // Mixins are defined in the `mods.toml` - } - - // "runs" isn't required, but when we do need it then it can be useful - runs { - client { - client() - setConfigName("NeoForge Client") - ideConfigGenerated(false) // When true a run configuration file will be generated for IDE's. By default only set to true for the root project. - runDir("../run/client") - vmArgs("-Dio.netty.leakDetection.level=advanced") // https://netty.io/wiki/reference-counted-objects.html#leak-detection-levels - // "--renderDebugLabels" is a Mojang command to show render names in RenderDoc - programArgs("--username", "Dev", "--renderDebugLabels") - } - server { - server() - setConfigName("NeoForge Server") - ideConfigGenerated(false) - runDir("../run/server") - vmArgs("-Dio.netty.leakDetection.level=advanced") - } - } + id 'unimined-neoforge' } +// ==================== Mod Dependency Helper ==================== def addMod(path, enabled) { if (enabled == "2") - dependencies { implementation(path) } + dependencies { modImplementation(path) } else if (enabled == "1") - dependencies { modCompileOnly(path) } + dependencies { compileOnly(path) } } -dependencies { - minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" - mappings loom.layered() - { - // Mojmap mappings - officialMojangMappings() - // Parchment mappings (it adds parameter mappings & javadoc) - parchment("org.parchmentmc.data:parchment-${rootProject.parchment_version}@zip") - } - - - // Neoforge - neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}" + +// ==================== Dependencies ==================== + +dependencies { // Iris addMod("maven.modrinth:iris:${rootProject.neo_iris_version}", rootProject.neo_enable_iris) - } +// ==================== Tasks ==================== task deleteResources(type: Delete) { delete file("build/resources/main") } -tasks.register('copyAllResources') { - dependsOn(copyCoreResources) - dependsOn(copyCommonLoaderResources) -} - processResources { - dependsOn(tasks.named('copyAllResources')) + dependsOn(copyCoreResources) + // dependsOn(copyCommonLoaderResources) } tasks.named('runClient') { - dependsOn(tasks.named('copyAllResources')) + dependsOn(copyCoreResources) + // dependsOn(copyCommonLoaderResources) finalizedBy(deleteResources) } -remapJar { - inputFile = shadowJar.archiveFile - dependsOn shadowJar - - atAccessWideners.add("distanthorizons.accesswidener") -} - sourcesJar { def commonSources = project(":common").sourcesJar dependsOn commonSources diff --git a/neoforge/gradle.properties b/neoforge/gradle.properties deleted file mode 100644 index 85e1db4b7..000000000 --- a/neoforge/gradle.properties +++ /dev/null @@ -1 +0,0 @@ -loom.platform=neoForge \ No newline at end of file diff --git a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/NeoforgeClientProxy.java b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/NeoforgeClientProxy.java index 83de73fee..741d7732b 100644 --- a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/NeoforgeClientProxy.java +++ b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/NeoforgeClientProxy.java @@ -218,13 +218,20 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy #else + #if MC_VER <= MC_1_21_11 @SubscribeEvent public void afterLevelEntityRenderEvent(RenderLevelStageEvent.AfterEntities event) + #else + @SubscribeEvent + public void afterLevelEntityRenderEvent(RenderLevelStageEvent.AfterOpaqueFeatures event) + #endif { #if MC_VER < MC_1_21_9 ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, (ClientLevel)event.getLevel()); - #else + #elif MC_VER <= MC_1_21_11 ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, event.getLevelRenderer().level); + #else + // handled via the same mixin as fabric for consistency #endif ClientApi.INSTANCE.renderFadeOpaque(); @@ -236,11 +243,11 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy { #if MC_VER < MC_1_21_9 ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, (ClientLevel)event.getLevel()); - #else + #elif MC_VER <= MC_1_21_11 ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, event.getLevelRenderer().level); + #else + // handled via the same mixin as fabric for consistency #endif - - ClientApi.INSTANCE.renderDeferredLodsForShaders(); } @SubscribeEvent @@ -248,8 +255,10 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy { #if MC_VER < MC_1_21_9 ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, (ClientLevel)event.getLevel()); - #else + #elif MC_VER <= MC_1_21_11 ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, event.getLevelRenderer().level); + #else + // handled via the same mixin as fabric for consistency #endif diff --git a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinChunkSectionsToRender.java b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinChunkSectionsToRender.java new file mode 100644 index 000000000..7d638f340 --- /dev/null +++ b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinChunkSectionsToRender.java @@ -0,0 +1,82 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.neoforge.mixins.client; + +#if MC_VER <= MC_1_21_11 +import net.minecraft.world.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(Entity.class) +public class MixinChunkSectionsToRender +{ /* rendering before was handled via Fabric API events */ } +#else + +import com.mojang.blaze3d.textures.GpuSampler; +import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; +import com.seibel.distanthorizons.core.api.internal.ClientApi; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.chunk.ChunkSectionLayerGroup; +import net.minecraft.client.renderer.chunk.ChunkSectionsToRender; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(ChunkSectionsToRender.class) +public class MixinChunkSectionsToRender +{ + + //============// + // post MC 26 // + //============// + //region + + #if MC_VER <= MC_1_21_11 + #else + + // needs to fire at HEAD with a lower than normal order (less than 1000) + // otherwise it will be canceled by Sodium + @Inject(at = @At("HEAD"), method = "renderGroup", order = 800) + private void renderDeferredLayerHead(ChunkSectionLayerGroup chunkSectionLayerGroup, GpuSampler gpuSampler, CallbackInfo ci) + { + ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, Minecraft.getInstance().levelRenderer.level); + + + ClientApi.RENDER_STATE.canRenderOrThrow(); + + if (chunkSectionLayerGroup == ChunkSectionLayerGroup.TRANSLUCENT) + { + ClientApi.INSTANCE.renderDeferredLodsForShaders(); + } + else if (chunkSectionLayerGroup == ChunkSectionLayerGroup.OPAQUE) + { + ClientApi.INSTANCE.renderLods(); + } + } + + //endregion + #endif + + + +} + +#endif + diff --git a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinFogRenderer.java b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinFogRenderer.java index ce00aa4e7..72aa56096 100644 --- a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinFogRenderer.java +++ b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinFogRenderer.java @@ -19,11 +19,8 @@ package com.seibel.distanthorizons.neoforge.mixins.client; +import com.seibel.distanthorizons.common.commonMixins.MixinVanillaFogCommon; import com.seibel.distanthorizons.core.api.internal.ClientApi; -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; -import net.minecraft.client.Minecraft; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; @@ -96,11 +93,11 @@ public class MixinFogRenderer #endif { #if MC_VER < MC_1_21_6 - boolean cancelFog = cancelFog(camera, fogMode); + boolean cancelFog = MixinVanillaFogCommon.cancelFog(camera, fogMode); #elif MC_VER < MC_1_21_6 - boolean cancelFog = cancelFog(camera); + boolean cancelFog = MixinVanillaFogCommon.cancelFog(camera); #else - boolean cancelFog = cancelFog(); + boolean cancelFog = MixinVanillaFogCommon.cancelFog(); #endif if (cancelFog) @@ -142,7 +139,7 @@ public class MixinFogRenderer ) private void onSetRenderDistanceEnd(FogData instance, float value, Operation original) { - if (cancelFog()) + if (MixinVanillaFogCommon.cancelFog()) { instance.environmentalStart = A_REALLY_REALLY_BIG_VALUE; instance.environmentalEnd = A_EVEN_LARGER_VALUE; @@ -164,52 +161,5 @@ public class MixinFogRenderer #endif - @Unique - #if MC_VER < MC_1_21_6 - private static boolean cancelFog(Camera camera, FogMode fogMode) - #else - private static boolean cancelFog() - #endif - { - #if MC_VER < MC_1_21_6 - Entity entity = camera.getEntity(); - #elif MC_VER <= MC_1_21_10 - Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); - Entity entity = camera.getEntity(); - #else - Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera(); - Entity entity = camera.entity(); - #endif - - - boolean cameraNotInFluid = cameraNotInFluid(camera); - boolean isSpecialFog = (entity instanceof LivingEntity) && ((LivingEntity) entity).hasEffect(MobEffects.BLINDNESS); - - boolean cancelFog = !isSpecialFog; - cancelFog = cancelFog && cameraNotInFluid; - #if MC_VER < MC_1_21_6 - cancelFog = cancelFog && (fogMode == FogMode.FOG_TERRAIN); - #endif - cancelFog = cancelFog && !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial(); - cancelFog = cancelFog && !Config.Client.Advanced.Graphics.Fog.enableVanillaFog.get(); - - return cancelFog; - } - - @Unique - private static boolean cameraNotInFluid(Camera camera) - { - #if MC_VER < MC_1_17_1 - FluidState fluidState = camera.getFluidInCamera(); - boolean cameraNotInFluid = fluidState.isEmpty(); - #else - FogType fogTypes = camera.getFluidInCamera(); - boolean cameraNotInFluid = fogTypes == FogType.NONE; - #endif - - return cameraNotInFluid; - } - - } diff --git a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinGameRenderer.java b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinGameRenderer.java new file mode 100644 index 000000000..7c27622fc --- /dev/null +++ b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinGameRenderer.java @@ -0,0 +1,77 @@ +/* + * This file is part of the Distant Horizons mod + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020 James Seibel + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, version 3. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this program. If not, see . + */ + +package com.seibel.distanthorizons.neoforge.mixins.client; + +#if MC_VER <= MC_1_21_11 +import net.minecraft.world.entity.Entity; +import org.spongepowered.asm.mixin.Mixin; + +@Mixin(Entity.class) +public class MixinGameRenderer {} + +#else +import com.mojang.blaze3d.vertex.PoseStack; +import com.seibel.distanthorizons.common.wrappers.McObjectConverter; +import com.seibel.distanthorizons.core.api.internal.ClientApi; +import net.minecraft.client.DeltaTracker; +import net.minecraft.client.player.LocalPlayer; +import net.minecraft.client.renderer.GameRenderer; +import net.minecraft.client.renderer.state.OptionsRenderState; +import net.minecraft.client.renderer.state.level.CameraRenderState; +import net.minecraft.util.profiling.ProfilerFiller; +import org.joml.Matrix4f; +import org.joml.Matrix4fc; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import org.spongepowered.asm.mixin.injection.callback.LocalCapture; + +@Mixin(GameRenderer.class) +public class MixinGameRenderer +{ + // get the modified projection matrix right before it's uploaded to the GPU + @Inject( + method = "renderLevel", + at = @At( + value = "INVOKE", + target = "Lcom/mojang/blaze3d/systems/RenderSystem;setProjectionMatrix(Lcom/mojang/blaze3d/buffers/GpuBufferSlice;Lcom/mojang/blaze3d/ProjectionType;)V", + shift = At.Shift.BEFORE + ), + locals = LocalCapture.CAPTURE_FAILHARD + ) + private void renderLevel( + final DeltaTracker deltaTracker, + final CallbackInfo callback, + final float partialTickTime, + final float cameraEntityPartialTicks, + final LocalPlayer player, + final ProfilerFiller profiler, + final boolean renderBlockOutline, + final OptionsRenderState options, + final CameraRenderState camera, + final Matrix4fc modelViewMatrix, + final Matrix4f projectionMatrix, + final PoseStack poseStack) + { + ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix); + } +} +#endif diff --git a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinLevelRenderer.java b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinLevelRenderer.java index 091468719..1a466e2e1 100644 --- a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinLevelRenderer.java +++ b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinLevelRenderer.java @@ -26,7 +26,7 @@ import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.RenderType; import net.neoforged.neoforge.client.event.RenderLevelStageEvent; import org.joml.Matrix4f; -#else +#elif MC_VER <= MC_1_21_11 import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.chunk.ChunkSectionsToRender; @@ -42,6 +42,26 @@ import org.joml.Vector4f; import com.mojang.blaze3d.buffers.GpuBufferSlice; import com.mojang.blaze3d.resource.GraphicsResourceAllocator; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; +#else +import com.mojang.blaze3d.textures.GpuSampler; +import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.chunk.ChunkSectionLayerGroup; +import net.minecraft.client.renderer.chunk.ChunkSectionsToRender; +import net.minecraft.client.Camera; +import net.minecraft.client.DeltaTracker; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.client.renderer.LevelRenderer; + +import net.minecraft.client.renderer.state.level.CameraRenderState; +import org.joml.Matrix4f; +import org.joml.Matrix4fc; +import org.joml.Vector4f; + +import com.mojang.blaze3d.buffers.GpuBufferSlice; +import com.mojang.blaze3d.resource.GraphicsResourceAllocator; + import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; #endif @@ -77,6 +97,12 @@ public class MixinLevelRenderer + //===========// + // Pre MC 26 // + //===========// + //region + #if MC_VER <= MC_1_21_11 + #if MC_VER < MC_1_21_6 @Inject(at = @At("HEAD"), method = "renderSectionLayer") private void renderChunkLayer(RenderType renderType, double x, double y, double z, Matrix4f modelViewMatrix, Matrix4f projectionMatrix, CallbackInfo callback) @@ -165,6 +191,42 @@ public class MixinLevelRenderer } #endif + #endif + //endregion + + + + //============// + // post MC 26 // + //============// + //region + + #if MC_VER <= MC_1_21_11 + #else + + @Inject(at = @At("HEAD"), method = "prepareChunkRenders") + private void prepareChunkRenders(final Matrix4fc modelViewMatrix, CallbackInfoReturnable callback) + { + ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, this.level); + } + + @Inject(at = @At("HEAD"), method = "renderLevel") + public void renderLevel( + final GraphicsResourceAllocator resourceAllocator, final DeltaTracker deltaTracker, + final boolean renderBlockOutline, final CameraRenderState camera, + final Matrix4fc modelViewMatrix, final GpuBufferSlice terrainFog, + final Vector4f fogColor, final boolean shouldRenderSky, + final ChunkSectionsToRender chunkSectionsToRender, + CallbackInfo callback) + { + ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrix); + + ClientApi.RENDER_STATE.partialTickTime = MinecraftRenderWrapper.INSTANCE.getPartialTickTime(); + + } + + #endif + //endregion diff --git a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinLightTexture.java b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinLightTexture.java index 417b030c1..5612b9f76 100644 --- a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinLightTexture.java +++ b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/mixins/client/MixinLightTexture.java @@ -25,11 +25,11 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftCli import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.neoforge.wrappers.NeoforgeTextureUnwrapper; -import net.minecraft.client.renderer.LightTexture; import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; @@ -43,12 +43,23 @@ import com.mojang.blaze3d.opengl.GlTexture; import com.mojang.blaze3d.systems.RenderSystem; import com.mojang.blaze3d.textures.GpuTexture; #else -import net.neoforged.neoforge.client.blaze3d.validation.ValidationGpuTexture; import com.mojang.blaze3d.opengl.GlTexture; import com.mojang.blaze3d.textures.GpuTexture; #endif +#if MC_VER <= MC_1_21_11 +import net.minecraft.client.renderer.LightTexture; +#else +import net.minecraft.client.renderer.Lightmap; +import net.minecraft.client.renderer.state.LightmapRenderState; +#endif + + +#if MC_VER <= MC_1_21_11 @Mixin(LightTexture.class) +#else +@Mixin(Lightmap.class) +#endif public class MixinLightTexture { #if MC_VER < MC_1_21_3 @@ -65,8 +76,19 @@ public class MixinLightTexture private GpuTexture texture; #endif + @Unique + private MinecraftRenderWrapper renderWrapper = null; + + + + + #if MC_VER <= MC_1_21_11 @Inject(method = "updateLightTexture(F)V", at = @At("RETURN")) public void updateLightTexture(float partialTicks, CallbackInfo ci) + #else + @Inject(method = "render(Lnet/minecraft/client/renderer/state/LightmapRenderState;)V", at = @At("RETURN")) + public void render(LightmapRenderState renderState, CallbackInfo ci) + #endif { IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); if (mc == null || mc.getWrappedClientLevel() == null) @@ -74,9 +96,18 @@ public class MixinLightTexture return; } - IClientLevelWrapper clientLevel = mc.getWrappedClientLevel(); - MinecraftRenderWrapper renderWrapper = (MinecraftRenderWrapper)SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + if (clientLevel == null) + { + return; + } + + // lazy initialization to make sure we don't call this too early + if (this.renderWrapper == null) + { + this.renderWrapper = (MinecraftRenderWrapper)SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); + } + #if MC_VER < MC_1_21_3 renderWrapper.updateLightmap(this.lightPixels, clientLevel); @@ -87,11 +118,11 @@ public class MixinLightTexture renderWrapper.setLightmapId(glTexture.glId(), clientLevel); #elif MC_VER <= MC_1_21_10 GlTexture glTexture = (GlTexture) this.texture; - this.renderWrapper.setLightmapId(glTexture.glId(), clientLevel); + renderWrapper.setLightmapId(glTexture.glId(), clientLevel); #else // both options are available since the renderer can be changed to either Blaze3D or OpenGL - GlTexture glTexture = (GlTexture) this.texture; - renderWrapper.setLightmapId(glTexture.glId(), clientLevel); + int id = NeoforgeTextureUnwrapper.getGlTextureIdFromGpuTexture(this.texture); + renderWrapper.setLightmapId(id, clientLevel); renderWrapper.setLightmapGpuTexture(this.texture, clientLevel); #endif diff --git a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/wrappers/NeoforgeMinecraftRenderWrapper.java b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/wrappers/NeoforgeMinecraftRenderWrapper.java index d0da316f0..60e91805e 100644 --- a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/wrappers/NeoforgeMinecraftRenderWrapper.java +++ b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/wrappers/NeoforgeMinecraftRenderWrapper.java @@ -9,7 +9,6 @@ import com.seibel.distanthorizons.core.logging.DhLogger; #else import com.mojang.blaze3d.opengl.GlTexture; import com.mojang.blaze3d.textures.GpuTexture; -import net.neoforged.neoforge.client.blaze3d.validation.ValidationGpuTexture; #endif import java.lang.invoke.MethodHandles; diff --git a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/wrappers/NeoforgeTextureUnwrapper.java b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/wrappers/NeoforgeTextureUnwrapper.java index 6e0a23bd1..9ff3e025c 100644 --- a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/wrappers/NeoforgeTextureUnwrapper.java +++ b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/wrappers/NeoforgeTextureUnwrapper.java @@ -7,7 +7,12 @@ public class NeoforgeTextureUnwrapper import com.mojang.blaze3d.opengl.GlTexture; import com.mojang.blaze3d.textures.GpuTexture; + +#if MC_VER <= MC_1_21_11 import net.neoforged.neoforge.client.blaze3d.validation.ValidationGpuTexture; +#else +#endif + public class NeoforgeTextureUnwrapper { @@ -21,6 +26,7 @@ public class NeoforgeTextureUnwrapper { GlTexture glTexture; + #if MC_VER <= MC_1_21_11 if (gpuTexture instanceof ValidationGpuTexture) { ValidationGpuTexture validationTexture = (ValidationGpuTexture) gpuTexture; @@ -30,6 +36,9 @@ public class NeoforgeTextureUnwrapper { glTexture = (GlTexture) gpuTexture; } + #else + glTexture = (GlTexture) gpuTexture; + #endif int id = glTexture.glId(); return id; diff --git a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/wrappers/modAccessor/IrisAccessor.java b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/wrappers/modAccessor/IrisAccessor.java index c9d301455..74fb4e33d 100644 --- a/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/wrappers/modAccessor/IrisAccessor.java +++ b/neoforge/src/main/java/com/seibel/distanthorizons/neoforge/wrappers/modAccessor/IrisAccessor.java @@ -43,7 +43,7 @@ public class IrisAccessor implements IIrisAccessor @Override public String getModName() { - #if MC_VER == MC_1_21_9 || MC_VER == MC_1_21_11 + #if MC_VER == MC_1_21_9 return "iris"; // Iris doesn't support this MC version #else return Iris.MODID; diff --git a/neoforge/src/main/resources/DistantHorizons.neoforge.mixins.json b/neoforge/src/main/resources/DistantHorizons.neoforge.mixins.json index b4f85ecb1..c4b8d8d3c 100644 --- a/neoforge/src/main/resources/DistantHorizons.neoforge.mixins.json +++ b/neoforge/src/main/resources/DistantHorizons.neoforge.mixins.json @@ -15,6 +15,8 @@ "client.MixinClientPacketListener", "client.MixinDebugScreenOverlay", "client.MixinFogRenderer", + "client.MixinChunkSectionsToRender", + "client.MixinGameRenderer", "client.MixinLevelRenderer", "client.MixinLightTexture", "client.MixinMinecraft", diff --git a/settings.gradle b/settings.gradle index b87d2bdf5..f7ca39e42 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,71 +1,5 @@ -pluginManagement { - - resolutionStrategy { - eachPlugin { - if (requested.id.id == "dev.architectury.loom") { - def mcVer = settings.startParameter.projectProperties.get("mcVer") ?: settings.providers.gradleProperty("mcVer").getOrElse("1.21.11") - if (mcVer == "1.12.2") { - useVersion("1.0-SNAPSHOT") - } - } - } - } - - repositories { - maven { - name "Fabric" - url "https://maven.fabricmc.net/" - } - maven { - name "Forge" - url "https://maven.minecraftforge.net/" - } - maven { - name "NeoForge Releases" - url "https://maven.neoforged.net/releases/" - } - maven { - name "NeoForge Snapshot" - url "https://maven.neoforged.net/snapshots/" - } - maven { - name "Architectury" - url "https://maven.architectury.dev/" - } - maven { - name "Quilt" - url "https://maven.quiltmc.org/repository/release" - } - maven { // Used for Vanilla Minecraft's libraries - name "Sponge" - url "https://repo.spongepowered.org/repository/maven-public/" - } - maven { - name "ParchmentMC" - url "https://maven.parchmentmc.org" - } - maven { - url = "https://maven.wagyourtail.xyz/releases" - } - maven { - url = "https://maven.outlands.top/releases" - } - maven { - url = "https://maven.wagyourtail.xyz/snapshots" - } - - mavenCentral() - gradlePluginPortal() - - // Not needed, but useful for debugging gradle plugins - mavenLocal() - } -} - - - -// Throw an error and a little help message if the user forgot to clone the core sub-project -if (!file("./coreSubProjects/LICENSE.txt").exists()) { // the LICENCE.txt file should always, and only exist if the core-sub-project was cloned +// Throw an error if the core sub-project wasn't cloned +if (!file("./coreSubProjects/LICENSE.txt").exists()) { println(''' It seems that the core sub project was not included... please make sure that when you were cloning the repo, you were using the `--recurse-submodules` flag on git. @@ -77,98 +11,55 @@ If you still need help with compiling, please read the Readme.md } +def loadVersionProperties() { + def mcVers = fileTree("versionProperties").files.collect { it.name.replaceAll("\\.properties", "") } + .sort { a, b -> + def aParts = a.tokenize('.'); def bParts = b.tokenize('.') + for (int i = 0; i < Math.min(aParts.size(), bParts.size()); i++) { + def aNum = aParts[i].isInteger() ? aParts[i].toInteger() : aParts[i] + def bNum = bParts[i].isInteger() ? bParts[i].toInteger() : bParts[i] + def compare = aNum <=> bNum + if (compare != 0) return compare + } + return aParts.size() <=> bParts.size() + } + def mcVersion = hasProperty("mcVer") ? mcVer : "" + def mcIndex = mcVers.indexOf(mcVersion) -/** Loads the VersionProperties fiel for the currently selected Minecraft version. */ -def loadProperties() -{ - def defaultMcVersion = "1.20.1" // 1.20.1 is our current most stable version so we use that if no version was defined - - def mcVersion = "" - def mcVers = fileTree("versionProperties").files.name // Get all the files in "versionProperties" - for (int i = 0; i < mcVers.size(); i++) - { - String version = mcVers[i]; - version = version.replaceAll("\\.properties", "") // As we are getting the file names, we should remove the ".properties" at the end to get the versions - mcVers[i] = version; + if (mcIndex == -1) { + def defaultVersion = "1.20.1" + println "No mcVer set or invalid. Defaulting to ${defaultVersion}." + println "Tip: Use -PmcVer=\"${defaultVersion}\" to set the MC version." + mcVersion = defaultVersion + mcIndex = mcVers.indexOf(defaultVersion) + assert mcIndex != -1 : "Default MC version ${defaultVersion} not found in ${mcVers}" } - mcVers.sort((a,b) -> sortSemanticVersionOldestToNewest(a,b)) // Sort so it always goes from oldest to newest + println "Available MC versions: ${mcVers}" + println "Loading properties file: ${mcVersion}.properties" - int mcIndex = -1 - println "Avalible MC versions: ${mcVers}" - if (hasProperty("mcVer")) - { - mcVersion = mcVer - mcIndex = mcVers.indexOf(mcVer) - } - - if (mcIndex == -1) - { - println "No mcVer set or the set mcVer is invalid! Defaulting to ${defaultMcVersion}." - println "Tip: Use -PmcVer=\"${defaultMcVersion}\" in cmd arg to set mcVer." - mcVersion = defaultMcVersion - mcIndex = mcVers.indexOf(defaultMcVersion) - assert mcIndex != -1 - } - - println "Loading properties file at " + mcVersion + ".properties" def props = new Properties() - props.load(new FileInputStream("$rootDir/versionProperties/"+"$mcVersion"+".properties")) + props.load(new FileInputStream("$rootDir/versionProperties/${mcVersion}.properties")) - props.each { prop -> - gradle.ext.set(prop.key, prop.value) - // println "Added prop [key:" + prop.key + ", value:" + prop.value + "]" - } + props.each { key, value -> gradle.ext.set(key, value) } gradle.ext.mcVers = mcVers gradle.ext.mcIndex = mcIndex } -/** - * input format: "major.minor.patch" - * needed so we can sort versions with different length strings - * IE: 1.21.1 should come before 1.21.10 - */ -private static int sortSemanticVersionOldestToNewest(String version1, String version2) -{ - String[] parts1 = version1.split("\\."); - String[] parts2 = version2.split("\\."); - - int major1 = Integer.parseInt(parts1[0]); - int major2 = Integer.parseInt(parts2[0]); - if (major1 != major2) - { - return Integer.compare(major1, major2); - } - - int minor1 = Integer.parseInt(parts1[1]); - int minor2 = Integer.parseInt(parts2[1]); - if (minor1 != minor2) - { - return Integer.compare(minor1, minor2); - } - - int patch1 = Integer.parseInt(parts1[2]); - int patch2 = Integer.parseInt(parts2[2]); - return Integer.compare(patch1, patch2); -} - -loadProperties() +loadVersionProperties() - - -// Minecraft independent sub-projects +// Minecraft-independent sub-projects include("core") project(":core").projectDir = file('coreSubProjects/core') include("api") project(":api").projectDir = file('coreSubProjects/api') -// Minecraft dependent sub-projects +// Minecraft-dependent sub-projects include("common") -// Enables or disables the subprojects depending on whats in the versionProperties/mcVer.properties -for (loader in ((String) gradle.builds_for).split(",")) { - def loaderName = loader.strip() // Strip it in case a space is added before or after the comma - println "Adding loader " + loaderName +((String) gradle.builds_for).split(",").each { loader -> + def loaderName = loader.trim() + println "Adding loader: ${loaderName}" include(loaderName) } diff --git a/versionProperties/1.16.5.properties b/versionProperties/1.16.5.properties index c749e2131..63850b9fb 100644 --- a/versionProperties/1.16.5.properties +++ b/versionProperties/1.16.5.properties @@ -54,6 +54,7 @@ forge_version=36.2.39 # Forge mod versions terraforged_version=4044290 + oculus_version= # Forge mod run # 0 = Don't enable and don't run @@ -62,3 +63,4 @@ forge_version=36.2.39 enable_starlight_forge=0 enable_terraforged=1 enable_terrafirmacraft=0 + enable_oculus=0 diff --git a/versionProperties/1.17.1.properties b/versionProperties/1.17.1.properties index 7414adc27..641d1b160 100644 --- a/versionProperties/1.17.1.properties +++ b/versionProperties/1.17.1.properties @@ -53,6 +53,7 @@ forge_version=37.1.1 # Forge mod versions terraforged_version= + oculus_version= # Forge mod run # 0 = Don't enable and don't run @@ -61,3 +62,4 @@ forge_version=37.1.1 enable_starlight_forge=0 enable_terraforged=0 enable_terrafirmacraft=0 + enable_oculus=0 diff --git a/versionProperties/1.18.2.properties b/versionProperties/1.18.2.properties index d745c474f..31ae756cb 100644 --- a/versionProperties/1.18.2.properties +++ b/versionProperties/1.18.2.properties @@ -62,6 +62,7 @@ forge_version=40.2.10 # Forge mod versions terraforged_version= + oculus_version= # Forge mod run # 0 = Don't enable and don't run @@ -69,4 +70,5 @@ forge_version=40.2.10 # 2 = Can be referenced in code and runs in client enable_starlight_forge=0 enable_terraforged=0 - enable_terrafirmacraft=0 \ No newline at end of file + enable_terrafirmacraft=0 + enable_oculus=0 diff --git a/versionProperties/1.19.2.properties b/versionProperties/1.19.2.properties index e50c5e617..2761724fc 100644 --- a/versionProperties/1.19.2.properties +++ b/versionProperties/1.19.2.properties @@ -52,6 +52,7 @@ forge_version=43.3.2 # Forge mod versions terraforged_version= + oculus_version= # Forge mod run # 0 = Don't enable and don't run @@ -60,3 +61,4 @@ forge_version=43.3.2 enable_starlight_forge=0 enable_terraforged=0 enable_terrafirmacraft=0 + enable_oculus=0 diff --git a/versionProperties/1.19.4.properties b/versionProperties/1.19.4.properties index 3945a43f9..ca7a31037 100644 --- a/versionProperties/1.19.4.properties +++ b/versionProperties/1.19.4.properties @@ -51,6 +51,7 @@ forge_version=45.2.4 # Forge mod versions terraforged_version= + oculus_version= # Forge mod run # 0 = Don't enable and don't run @@ -59,3 +60,4 @@ forge_version=45.2.4 enable_starlight_forge=0 enable_terraforged=0 enable_terrafirmacraft=0 + enable_oculus=0 diff --git a/versionProperties/1.20.1.properties b/versionProperties/1.20.1.properties index af43eb93b..d5e08040f 100644 --- a/versionProperties/1.20.1.properties +++ b/versionProperties/1.20.1.properties @@ -51,6 +51,7 @@ forge_version=47.2.1 # Forge mod versions terraforged_version= + oculus_version=1.20.1-1.8.0 # Forge mod run # 0 = Don't enable and don't run @@ -59,3 +60,4 @@ forge_version=47.2.1 enable_starlight_forge=0 enable_terraforged=0 enable_terrafirmacraft=0 + enable_oculus=1 diff --git a/versionProperties/1.20.2.properties b/versionProperties/1.20.2.properties index 1adff5e3b..3cc8c641b 100644 --- a/versionProperties/1.20.2.properties +++ b/versionProperties/1.20.2.properties @@ -51,6 +51,7 @@ forge_version=48.0.13 # Forge mod versions terraforged_version= + oculus_version= # Forge mod run # 0 = Don't enable and don't run @@ -59,3 +60,4 @@ forge_version=48.0.13 enable_starlight_forge=0 enable_terraforged=0 enable_terrafirmacraft=0 + enable_oculus=0 diff --git a/versionProperties/1.20.4.properties b/versionProperties/1.20.4.properties index 7d74fe957..907afb57d 100644 --- a/versionProperties/1.20.4.properties +++ b/versionProperties/1.20.4.properties @@ -52,6 +52,7 @@ neoforge_version= # Forge mod versions terraforged_version= + oculus_version= # Forge mod run # 0 = Don't enable and don't run @@ -60,3 +61,4 @@ neoforge_version= enable_starlight_forge=0 enable_terraforged=0 enable_terrafirmacraft=0 + enable_oculus=0 diff --git a/versionProperties/1.20.6.properties b/versionProperties/1.20.6.properties index e3096bd7c..5693b1b3a 100644 --- a/versionProperties/1.20.6.properties +++ b/versionProperties/1.20.6.properties @@ -47,7 +47,7 @@ fabric_api_version=0.97.8+1.20.6 # NeoForge loader forge_version= -neoforge_version=20.6.136 +neoforge_version=136 neoforge_version_range=[*,) # NeoForge mod versions diff --git a/versionProperties/1.21.1.properties b/versionProperties/1.21.1.properties index a1535ff55..9e8134115 100644 --- a/versionProperties/1.21.1.properties +++ b/versionProperties/1.21.1.properties @@ -15,7 +15,7 @@ lwjgl_version=3.3.3 # Fabric loader fabric_loader_version=0.16.9 -fabric_api_version=0.115.0+1.21.1 +fabric_api_version=0.116.11+1.21.1 # Fabric mod versions modmenu_version=11.0.0-beta.1 starlight_version_fabric= @@ -47,7 +47,7 @@ fabric_api_version=0.115.0+1.21.1 # NeoForge loader forge_version= -neoforge_version=21.1.216 +neoforge_version=216 neoforge_version_range=[*,) # NeoForge mod versions diff --git a/versionProperties/1.21.10.properties b/versionProperties/1.21.10.properties index 0904bac2f..759d1675d 100644 --- a/versionProperties/1.21.10.properties +++ b/versionProperties/1.21.10.properties @@ -15,7 +15,7 @@ lwjgl_version=3.2.3 # Fabric loader fabric_loader_version=0.17.3 -fabric_api_version=0.138.3+1.21.10 +fabric_api_version=0.138.4+1.21.10 modmenu_version=16.0.0-rc.1 starlight_version_fabric= phosphor_version_fabric= @@ -45,7 +45,7 @@ fabric_api_version=0.138.3+1.21.10 # NeoForge loader forge_version= -neoforge_version=21.10.56-beta +neoforge_version=64 neoforge_version_range=[21.10.6-beta,) # NeoForge mod versions diff --git a/versionProperties/1.21.11.properties b/versionProperties/1.21.11.properties index 24be330c0..5e73642ed 100644 --- a/versionProperties/1.21.11.properties +++ b/versionProperties/1.21.11.properties @@ -45,11 +45,11 @@ fabric_api_version=0.139.4+1.21.11 # NeoForge loader forge_version= -neoforge_version=21.11.38-beta +neoforge_version=38-beta neoforge_version_range=[*,) # NeoForge mod versions - neo_iris_version=1.10.2+1.21.11-neoforge + neo_iris_version=1.10.7+1.21.11-neoforge # (Neo)Forge mod run # 0 = Don't enable and don't run diff --git a/versionProperties/1.21.3.properties b/versionProperties/1.21.3.properties index a6dedbe66..b0c4e751c 100644 --- a/versionProperties/1.21.3.properties +++ b/versionProperties/1.21.3.properties @@ -4,7 +4,7 @@ minecraft_version=1.21.3 parchment_version=1.21:2024.07.28 compatible_minecraft_versions=["1.21.3"] accessWidenerVersion=1_21_3 -builds_for=neoforge,fabric +builds_for=fabric,neoforge # forge is broken due to gradle/build script issues # Netty @@ -47,7 +47,7 @@ fabric_api_version=0.110.0+1.21.3 # NeoForge loader forge_version= -neoforge_version=21.3.86 +neoforge_version=86 neoforge_version_range=[*,) # NeoForge mod versions diff --git a/versionProperties/1.21.4.properties b/versionProperties/1.21.4.properties index a753d179d..417d4c8c8 100644 --- a/versionProperties/1.21.4.properties +++ b/versionProperties/1.21.4.properties @@ -4,7 +4,7 @@ minecraft_version=1.21.4 parchment_version=1.21:2024.07.28 compatible_minecraft_versions=["1.21.4"] accessWidenerVersion=1_21_4 -builds_for=neoforge,fabric +builds_for=fabric,neoforge # forge is broken due to gradle/build script issues # Netty @@ -46,7 +46,7 @@ fabric_api_version=0.110.5+1.21.4 # NeoForge loader forge_version= -neoforge_version=21.4.147 +neoforge_version=147 # version range may not be necessary, but having compiled DH for an older version caused issues with shaders neoforge_version_range=[21.4.147,) diff --git a/versionProperties/1.21.5.properties b/versionProperties/1.21.5.properties index 7c306a476..2d3308215 100644 --- a/versionProperties/1.21.5.properties +++ b/versionProperties/1.21.5.properties @@ -46,7 +46,7 @@ fabric_api_version=0.119.5+1.21.5 # NeoForge loader forge_version= -neoforge_version=21.5.87 +neoforge_version=87 neoforge_version_range=[*,) # NeoForge mod versions diff --git a/versionProperties/1.21.6.properties b/versionProperties/1.21.6.properties index 11bbe88a6..45692e302 100644 --- a/versionProperties/1.21.6.properties +++ b/versionProperties/1.21.6.properties @@ -45,7 +45,7 @@ fabric_api_version=0.127.0+1.21.6 # NeoForge loader forge_version= -neoforge_version=21.6.20-beta +neoforge_version=20-beta # around 6.19 neo changed how their render API works, failing to meet this causes the game to crash neoforge_version_range=[21.6.19-beta,) diff --git a/versionProperties/1.21.8.properties b/versionProperties/1.21.8.properties index 5f8dd7e04..6330dff60 100644 --- a/versionProperties/1.21.8.properties +++ b/versionProperties/1.21.8.properties @@ -45,7 +45,7 @@ fabric_api_version=0.133.4+1.21.8 # NeoForge loader forge_version= -neoforge_version=21.8.52 +neoforge_version=52 # around 6.19 neo changed how their render API works, failing to meet this causes the game to crash neoforge_version_range=[*,) diff --git a/versionProperties/1.21.9.properties b/versionProperties/1.21.9.properties index c177f4caa..82ff98a6a 100644 --- a/versionProperties/1.21.9.properties +++ b/versionProperties/1.21.9.properties @@ -45,7 +45,7 @@ fabric_api_version=0.134.0+1.21.9 # NeoForge loader forge_version= -neoforge_version=21.9.15-beta +neoforge_version=15-beta # sometime before 21.9.15-beta Neoforge changed how their rendering API events handle levels # so we can't support both versions at once neoforge_version_range=[21.9.15-beta,) diff --git a/versionProperties/26.1.2.properties b/versionProperties/26.1.2.properties new file mode 100644 index 000000000..dfcff0f4c --- /dev/null +++ b/versionProperties/26.1.2.properties @@ -0,0 +1,59 @@ +# 26.1.2 version +java_version=25 +minecraft_version=26.1.2 +parchment_version=1.21:2024.07.28 +# version range should be used instead of individual versions due to how NeoForge handles version loading +compatible_minecraft_versions=["26.1.0", "26.1.2"] +accessWidenerVersion=26_1 +builds_for=fabric,neoforge +# forge is broken due to gradle/build script issues + +# Netty +netty_version=4.1.97.Final + +# LWJGL +lwjgl_version=3.4.1 + +# Fabric loader +fabric_loader_version=0.18.5 +fabric_api_version=0.145.4+26.1.2 +modmenu_version=18.0.0-alpha.8 +starlight_version_fabric= +phosphor_version_fabric= +lithium_version= +sodium_version=mc26.1.1-0.8.9-fabric +iris_version=1.10.9+26.1-fabric +bclib_version= +immersive_portals_version= +canvas_version= + +fabric_incompatibility_list={ } +fabric_recommend_list={} + +# Fabric mod run +# 0 = Don't enable and don't run +# 1 = Can be referenced in code but doesn't run +# 2 = Can be referenced in code and runs in client +enable_mod_menu=2 +enable_starlight=0 +enable_phosphor=0 +enable_sodium=1 +enable_lithium=0 +enable_iris=1 +enable_bclib=0 +enable_immersive_portals=0 +enable_canvas=0 + +# NeoForge loader +forge_version= +neoforge_version=15-beta +neoforge_version_range=[*,) + +# NeoForge mod versions +neo_iris_version=1.10.9+26.1-neoforge + +# (Neo)Forge mod run +# 0 = Don't enable and don't run +# 1 = Can be referenced in code but doesn't run +# 2 = Can be referenced in code and runs in client +neo_enable_iris=1