Compare commits

..

31 Commits

Author SHA1 Message Date
s809 4a5b669245 Move identical client mixins under common project 2024-08-04 00:40:50 +05:00
James Seibel a37e105434 Add (disabled) test API world generator 2024-08-03 09:52:06 -05:00
James Seibel aeea0c00c3 Allow DhApiChunk to accept top down or bottom up data point orders 2024-08-03 09:33:05 -05:00
James Seibel 137352674e Fix off by 1 error in Render data transformer 2024-08-02 18:30:59 -05:00
James Seibel 4734552954 Fix MC 1.16 compiling 2024-08-02 18:21:47 -05:00
James Seibel 879c2f1ec4 Fix out of bounds exception in Full Data Transformer 2024-08-02 17:56:33 -05:00
James Seibel 7dc9d2a352 Clean up faster world gen and fix even offset gen events 2024-08-02 08:25:32 -05:00
James Seibel cabc470ebd Temporary Test removing world gen boarder chunks 2024-08-01 07:44:46 -05:00
James Seibel 0bf1f493aa Change some world gen info logs to debug 2024-08-01 07:06:47 -05:00
James Seibel 705bd14ee4 Fix cave culling affecting floating islands and add LOD reload to some configs 2024-07-31 19:06:47 -05:00
James Seibel 155955e49b Mark Iris 1.7.4 and lower as incompatible (as recommended by IMS) 2024-07-30 17:13:54 -05:00
James Seibel c76a793b18 Remove deprecated methods and move method to StringUtil 2024-07-30 17:07:16 -05:00
James Seibel 50cc8501a0 Remove unused sodium and McRenderWrapper methods
Removed methods were originally used to cull LODs if MC had loaded chunks, however this turned out to be more trouble than it was worth and caused more problems than it solved.
2024-07-30 17:01:09 -05:00
James Seibel 209279e3e4 Merge branch 'distant-horizons-m2' 2024-07-30 16:06:39 -05:00
James Seibel 41239572a5 Fix presets only using "custom" after any value was changed 2024-07-30 15:47:52 -05:00
James Seibel 106ab47c3d Fix default logging debug to file 2024-07-29 20:40:54 -05:00
James Seibel a84f9b60e5 Fix rapidly changing dimensions causing the game to crash 2024-07-29 07:29:56 -05:00
James Seibel 4481e8634a Fix incorrect DhApiChunk create constructor parameter order (again) 2024-07-28 20:18:31 -05:00
James Seibel 3e432682fb fix incorrect positions being fed into biome color code 2024-07-28 09:34:15 -05:00
James Seibel 05569c03a4 Revert and Deprecate DhApiChunk and DhApiTerrainDataPoint constructors 2024-07-28 08:56:26 -05:00
James Seibel 2d567b84be Fix holes in LODs boarding different detail levels 2024-07-27 21:06:55 -05:00
James Seibel e2a378250f Fix LOD upload warning 2024-07-27 20:25:58 -05:00
James Seibel e2083a1836 Fix LODs flashing twice when changing configs 2024-07-27 20:11:49 -05:00
James Seibel 334946ab59 Potentially fix thread warnings in ClientBlockStateColorCache 2024-07-27 19:15:00 -05:00
James Seibel 8c9bb98125 Update IDhApiRenderProxy.clearRenderDataCache() to also clear cached block colors 2024-07-27 17:36:57 -05:00
James Seibel 726f0f3d3c Remove unused ServerBlockStateCache 2024-07-27 16:51:14 -05:00
James Seibel 50e5898692 Rename ClientBlockStateCache -> ClientBlockStateColorCache
And do some additional cleanup
2024-07-27 16:44:47 -05:00
James Seibel de05a5f674 Refactor and cleanup ClientBlockStateCache 2024-07-27 16:25:27 -05:00
James Seibel 31b57fae50 fix 1.16.5 compiling 2024-07-27 16:24:31 -05:00
James Seibel 2f686057f3 Fix ice/water vertical LOD lighting 2024-07-27 09:30:51 -05:00
IMS212 3aaab94b39 Support both Sodium 0.5 and 0.6 with reflection 2024-07-10 21:02:53 -07:00
69 changed files with 951 additions and 1385 deletions
+79 -52
View File
@@ -12,8 +12,6 @@ plugins {
// Architectury is used here only as a replacement for forge's own loom // Architectury is used here only as a replacement for forge's own loom
id "dev.architectury.loom" version "1.6-SNAPSHOT" apply false id "dev.architectury.loom" version "1.6-SNAPSHOT" apply false
id 'xyz.wagyourtail.jvmdowngrader' version '0.9.1' apply true
} }
@@ -29,17 +27,17 @@ def writeBuildGradlePredefine(List<String> mcVers, int mcIndex)
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
sb.append("# DON'T TOUCH THIS FILE, This is handled by the build script\n"); sb.append("# DON'T TOUCH THIS FILE, This is handled by the build script\n");
for (int i = 0; i < mcVers.size(); i++) for (int i = 0; i < mcVers.size(); i++)
{ {
String verStr = mcVers[i].replace(".", "_"); String verStr = mcVers[i].replace(".", "_");
sb.append("MC_" + verStr + "=" + i.toString() + "\n"); sb.append("MC_" + verStr + "=" + i.toString() + "\n");
if (mcIndex == i) if (mcIndex == i)
sb.append("MC_VER=" + i.toString() + "\n"); sb.append("MC_VER=" + i.toString() + "\n");
} }
// Check if this is a development build // Check if this is a development build
if (mod_version.toLowerCase().contains("dev")) if (mod_version.toLowerCase().contains("dev"))
@@ -104,20 +102,23 @@ subprojects { p ->
// Apply plugins // Apply plugins
apply plugin: "java" apply plugin: "java"
apply plugin: "com.github.johnrengelman.shadow" apply plugin: "com.github.johnrengelman.shadow"
apply plugin: "xyz.wagyourtail.jvmdowngrader"
if (isMinecraftSubProject) if (isMinecraftSubProject)
apply plugin: "systems.manifold.manifold-gradle-plugin" apply plugin: "systems.manifold.manifold-gradle-plugin"
// Apply forge's loom // Apply forge's loom
if ( (findProject(":forge") && p == project(":forge")) || (findProject(":neoforge") && p == project(":neoforge")) ) if ((findProject(":forge") && p == project(":forge")) ||
(findProject(":neoforge") && p == project(":neoforge"))
)
{
apply plugin: "dev.architectury.loom" apply plugin: "dev.architectury.loom"
}
// Set the manifold version (may not be required tough) // Set the manifold version (may not be required tough)
manifold { manifold {
manifoldVersion = rootProject.manifold_version manifoldVersion = rootProject.manifold_version
} }
// set up custom configurations (configurations are a way to handle dependencies) // set up custom configurations (configurations are a way to handle dependencies)
configurations { configurations {
@@ -173,12 +174,12 @@ subprojects { p ->
//=====================// //=====================//
// shared dependencies // // shared dependencies //
//=====================// //=====================//
// Manifold // Manifold
if (isMinecraftSubProject) { if (isMinecraftSubProject) {
annotationProcessor("systems.manifold:manifold-preprocessor:${rootProject.manifold_version}") annotationProcessor("systems.manifold:manifold-preprocessor:${rootProject.manifold_version}")
} }
// Log4j // Log4j
// TODO: Change to shadowMe later to work in the standalone jar // TODO: Change to shadowMe later to work in the standalone jar
// We cannot do this now as it would break Quilt // We cannot do this now as it would break Quilt
@@ -190,7 +191,7 @@ subprojects { p ->
forgeShadowMe("org.joml:joml:${rootProject.joml_version}") forgeShadowMe("org.joml:joml:${rootProject.joml_version}")
else else
implementation("org.joml:joml:${rootProject.joml_version}") implementation("org.joml:joml:${rootProject.joml_version}")
// JUnit tests // JUnit tests
implementation("org.junit.jupiter:junit-jupiter:5.8.2") implementation("org.junit.jupiter:junit-jupiter:5.8.2")
implementation("org.junit.jupiter:junit-jupiter-engine:5.8.2") implementation("org.junit.jupiter:junit-jupiter-engine:5.8.2")
@@ -200,22 +201,22 @@ subprojects { p ->
// Note: MC 1.16 uses 8.2.1, and versions after use 8.5.12 // 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 // We cannot relocate this library since we call some MC classes that reference it
implementation("it.unimi.dsi:fastutil:${rootProject.fastutil_version}") implementation("it.unimi.dsi:fastutil:${rootProject.fastutil_version}")
// Compression // Compression
forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}") // LZ4 forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}") // LZ4
forgeShadowMe("org.tukaani:xz:${rootProject.xz_version}") // LZMA forgeShadowMe("org.tukaani:xz:${rootProject.xz_version}") // LZMA
// Sqlite Database // Sqlite Database
forgeShadowMe("org.xerial:sqlite-jdbc:${rootProject.sqlite_jdbc_version}") forgeShadowMe("org.xerial:sqlite-jdbc:${rootProject.sqlite_jdbc_version}")
// NightConfig (includes Toml & Json) // NightConfig (includes Toml & Json)
forgeShadowMe("com.electronwill.night-config:toml:${rootProject.nightconfig_version}") forgeShadowMe("com.electronwill.night-config:toml:${rootProject.nightconfig_version}")
forgeShadowMe("com.electronwill.night-config:json:${rootProject.nightconfig_version}") forgeShadowMe("com.electronwill.night-config:json:${rootProject.nightconfig_version}")
// SVG (not needed atm) // SVG (not needed atm)
// forgeShadowMe("com.formdev:svgSalamander:${rootProject.svgSalamander_version}") // forgeShadowMe("com.formdev:svgSalamander:${rootProject.svgSalamander_version}")
// Netty // Netty
// Breaks 1.16.5 // Breaks 1.16.5
//forgeShadowMe("io.netty:netty-all:${rootProject.netty_version}") //forgeShadowMe("io.netty:netty-all:${rootProject.netty_version}")
@@ -224,9 +225,9 @@ subprojects { p ->
forgeShadowMe("org.lwjgl:lwjgl-jawt:${rootProject.lwjgl_version}") { forgeShadowMe("org.lwjgl:lwjgl-jawt:${rootProject.lwjgl_version}") {
exclude group: "org.lwjgl", module: "lwjgl" // This module is imported by Minecraft so exclude it exclude group: "org.lwjgl", module: "lwjgl" // This module is imported by Minecraft so exclude it
} }
//==========================// //==========================//
// conditional dependencies // // conditional dependencies //
//==========================// //==========================//
@@ -276,7 +277,7 @@ subprojects { p ->
if (isMinecraftSubProject && p != project(":common")) { if (isMinecraftSubProject && p != project(":common")) {
configurations.push(project.configurations.shadowCommon) // Shadow the common subproject 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 relocate "com.seibel.distanthorizons.common", "loaderCommon.${p.name}.com.seibel.distanthorizons.common" // Move the loader files to a different location
if (findProject(":fabricLike") && p != project(":fabricLike")) { if (findProject(":fabricLike") && p != project(":fabricLike")) {
configurations.push(project.configurations.shadowFabricLike) // Shadow the fabricLike subproject configurations.push(project.configurations.shadowFabricLike) // Shadow the fabricLike subproject
relocate "com.seibel.distanthorizons.fabriclike", "loaderCommon.${p.name}.com.seibel.distanthorizons.fabriclike" // Move the loader files to a different location relocate "com.seibel.distanthorizons.fabriclike", "loaderCommon.${p.name}.com.seibel.distanthorizons.fabriclike" // Move the loader files to a different location
@@ -290,11 +291,11 @@ subprojects { p ->
// Compression (LZ4) // Compression (LZ4)
relocate "net.jpountz", "${librariesLocation}.jpountz" relocate "net.jpountz", "${librariesLocation}.jpountz"
// Sqlite Database // Sqlite Database
//At the moment, there is a bug in this library which doesnt allow it to be relocated //At the moment, there is a bug in this library which doesnt allow it to be relocated
// relocate "org.sqlite", "${librariesLocation}.sqlite" // relocate "org.sqlite", "${librariesLocation}.sqlite"
// JOML // JOML
if (project.hasProperty("embed_joml") && embed_joml == "true") if (project.hasProperty("embed_joml") && embed_joml == "true")
relocate "org.joml", "${librariesLocation}.joml" relocate "org.joml", "${librariesLocation}.joml"
@@ -304,27 +305,14 @@ subprojects { p ->
// SVG (not needed atm) // SVG (not needed atm)
// relocate "com.kitfox.svg", "${librariesLocation}.kitfox.svg" // relocate "com.kitfox.svg", "${librariesLocation}.kitfox.svg"
// Netty // Netty
relocate "io.netty", "${librariesLocation}.netty" relocate "io.netty", "${librariesLocation}.netty"
mergeServiceFiles() mergeServiceFiles()
} }
// Using jar.finalizedBy(shadowJar) causes issues so we do this scuffed bypass
// For downgrading the Java version of our project to match the minimum Java version required by the selected Minecraft version. jar.dependsOn(shadowJar)
task jarDowngrade(type: xyz.wagyourtail.jvmdg.gradle.task.DowngradeJar) {
inputFile = tasks.shadowJar.archiveFile
downgradeTo = JavaVersion.toVersion(rootProject.java_version as Integer)
archiveClassifier = "downgraded-${rootProject.java_version}"
}
task apiDowngrade(type: xyz.wagyourtail.jvmdg.gradle.task.ShadeJar) {
inputFile = jarDowngrade.archiveFile
downgradeTo = JavaVersion.toVersion(rootProject.java_version as Integer)
archiveClassifier = "downgraded-${rootProject.java_version}-shaded-java-api"
}
// We're using a custom downgrade task so we disable the original downgrade tasks
downgradeJar.enabled = false
shadeDowngradedApi.enabled = false
// Put stuff from gradle.properties into the mod info // Put stuff from gradle.properties into the mod info
@@ -364,7 +352,7 @@ subprojects { p ->
// TODO: Find something we can use so we can basically re-map only when the jar is shadowed and relocated // TODO: Find something we can use so we can basically re-map only when the jar is shadowed and relocated
// println p.tasks.findByName('shadowJar') // println p.tasks.findByName('shadowJar')
// These "hasProperty"'s are so that they can be passed through the cli (ie in the CI) // These "hasProperty"'s are so that they can be passed through the cli (ie in the CI)
try { try {
if (infoGitCommit == "null") if (infoGitCommit == "null")
@@ -400,14 +388,14 @@ subprojects { p ->
fabric_incompatibility_list : fabric_incompatibility_list, fabric_incompatibility_list : fabric_incompatibility_list,
fabric_recommend_list : fabric_recommend_list, fabric_recommend_list : fabric_recommend_list,
] ]
// replace any properties in the sub-projects with the values defined here // replace any properties in the sub-projects with the values defined here
inputs.properties replaceProperties inputs.properties replaceProperties
replaceProperties.put "project", project replaceProperties.put "project", project
filesMatching(resourceTargets) { filesMatching(resourceTargets) {
expand replaceProperties expand replaceProperties
} }
intoTargets.each { target -> intoTargets.each { target ->
if (file(target).exists()) { if (file(target).exists()) {
copy { copy {
@@ -443,9 +431,11 @@ subprojects { p ->
jar { jar {
from "LICENSE.txt" from "LICENSE.txt"
manifest { manifest {
attributes 'Implementation-Title': rootProject.mod_name, attributes(
'Implementation-Version': rootProject.mod_version, 'Implementation-Title': rootProject.mod_name,
'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain' // When changing the main of the jar change this line 'Implementation-Version': rootProject.mod_version,
'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain' // When changing the main of the jar change this line
)
} }
} }
@@ -475,11 +465,11 @@ allprojects { p ->
apply plugin: "java" apply plugin: "java"
apply plugin: "maven-publish" apply plugin: "maven-publish"
archivesBaseName = rootProject.mod_name archivesBaseName = rootProject.mod_name
version = project.name + "-" + rootProject.versionStr version = project.name + "-" + rootProject.versionStr
group = rootProject.maven_group group = rootProject.maven_group
// this is the text that appears at the top of the overview (home) page // this is the text that appears at the top of the overview (home) page
// and is used when bookmarking a page // and is used when bookmarking a page
javadoc.title = rootProject.mod_name + "-" + project.name javadoc.title = rootProject.mod_name + "-" + project.name
@@ -567,6 +557,44 @@ allprojects { p ->
// TODO: If neoforged is ever needed, should we use that, or call it a forge mod? // TODO: If neoforged is ever needed, should we use that, or call it a 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 it as we arent importing Minecraft in the core
// Imports most of lwjgl's libraries (well, only the ones that we need)
implementation platform("org.lwjgl:lwjgl-bom:${rootProject.lwjgl_version}") // TODO: Use Minecraft's version for lwjgl_version (which changes in nearly every version) instead of a hard defined version for all versions
// REMEMBER: Dont shadow stuff here, these are just the libs that are included in Minecraft so that the core can use
implementation "org.lwjgl:lwjgl"
implementation "org.lwjgl:lwjgl-assimp"
implementation "org.lwjgl:lwjgl-glfw"
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")
}
}
task copyCommonLoaderResources(type: Copy) { task copyCommonLoaderResources(type: Copy) {
from project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener") from project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener")
into(file(p.file("build/resources/main"))) into(file(p.file("build/resources/main")))
@@ -588,17 +616,16 @@ allprojects { p ->
tasks.withType(JavaCompile) { tasks.withType(JavaCompile) {
if (isMinecraftSubProject) { if (isMinecraftSubProject) {
options.release = rootProject.java_version as Integer // Neoforge complains without this options.release = rootProject.java_version as Integer
options.compilerArgs += ["-Xplugin:Manifold"] options.compilerArgs += ["-Xplugin:Manifold"]
} else {
options.release = 8; // Core & Api should use Java 8 no matter what
//options.release = rootProject.java_version as Integer // But if you want to test some stuff, then this can be enabled
} }
options.encoding = "UTF-8" options.encoding = "UTF-8"
} }
// Sets the project's actual Java version (it's recommended to use this over the `options.release` method above)
java { java {
sourceCompatibility = rootProject.java_version
targetCompatibility = rootProject.java_version
withSourcesJar() withSourcesJar()
} }
} }
@@ -103,7 +103,7 @@ public abstract class AbstractModInitializer
LOGGER.info(ModInfo.READABLE_NAME + " Initialized"); LOGGER.info(ModInfo.READABLE_NAME + " Initialized");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null); ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
this.subscribeRegisterCommandsEvent(dispatcher -> this.commandDispatcher = dispatcher); this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandDispatcher = dispatcher; });
this.subscribeServerStartingEvent(server -> this.subscribeServerStartingEvent(server ->
{ {
@@ -1,4 +1,4 @@
package com.seibel.distanthorizons.fabric.mixins.client; package com.seibel.distanthorizons.common.mixins.client;
import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import net.minecraft.client.gui.components.DebugScreenOverlay; import net.minecraft.client.gui.components.DebugScreenOverlay;
@@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.seibel.distanthorizons.fabric.mixins.client; package com.seibel.distanthorizons.common.mixins.client;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
@@ -1,4 +1,4 @@
package com.seibel.distanthorizons.fabric.mixins.client; package com.seibel.distanthorizons.common.mixins.client;
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck; import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
@@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.seibel.distanthorizons.fabric.mixins.client; package com.seibel.distanthorizons.common.mixins.client;
import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.platform.NativeImage;
@@ -26,6 +26,6 @@ public class DependencySetupDoneCheck
// TODO move to DependencySetup // TODO move to DependencySetup
public static boolean isDone = false; public static boolean isDone = false;
// TODO why is this here and what is its purpose? // TODO why is this here and what is its purpose?
public static Supplier<Boolean> getIsCurrentThreadDistantGeneratorThread = (() -> false); public static Supplier<Boolean> getIsCurrentThreadDistantGeneratorThread = (() -> { return false; });
} }
@@ -102,17 +102,33 @@ public class McObjectConverter
lodDirections = new EDhDirection[lodDirs.length]; lodDirections = new EDhDirection[lodDirs.length];
for (EDhDirection lodDir : lodDirs) for (EDhDirection lodDir : lodDirs)
{ {
Direction dir = switch (lodDir.name().toUpperCase()) { Direction dir;
case "DOWN" -> Direction.DOWN; switch (lodDir.name().toUpperCase())
case "UP" -> Direction.UP; {
case "NORTH" -> Direction.NORTH; case "DOWN":
case "SOUTH" -> Direction.SOUTH; dir = Direction.DOWN;
case "WEST" -> Direction.WEST; break;
case "EAST" -> Direction.EAST; case "UP":
default -> null; dir = Direction.UP;
}; break;
case "NORTH":
if (dir == null) dir = Direction.NORTH;
break;
case "SOUTH":
dir = Direction.SOUTH;
break;
case "WEST":
dir = Direction.WEST;
break;
case "EAST":
dir = Direction.EAST;
break;
default:
dir = null;
break;
}
if (dir == null)
{ {
throw new IllegalArgumentException("Invalid direction on init mapping: " + lodDir); throw new IllegalArgumentException("Invalid direction on init mapping: " + lodDir);
} }
@@ -161,17 +161,19 @@ public class WrapperFactory implements IWrapperFactory
// correct number of parameters from the API // correct number of parameters from the API
// chunk // chunk
if (!(objectArray[0] instanceof ChunkAccess chunk)) if (!(objectArray[0] instanceof ChunkAccess))
{ {
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray)); throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
} }
ChunkAccess chunk = (ChunkAccess) objectArray[0];
// level / light source // level / light source
if (!(objectArray[1] instanceof Level level)) if (!(objectArray[1] instanceof Level))
{ {
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray)); throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
} }
// the level is needed for the DH level wrapper... // the level is needed for the DH level wrapper...
Level level = (Level) objectArray[1];
// ...the LevelReader is needed for chunk lighting // ...the LevelReader is needed for chunk lighting
LevelReader lightSource = level; LevelReader lightSource = level;
@@ -243,10 +245,11 @@ public class WrapperFactory implements IWrapperFactory
public IDhApiBiomeWrapper getBiomeWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper) public IDhApiBiomeWrapper getBiomeWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper)
{ {
// confirm the API level wrapper is also a Core wrapper // confirm the API level wrapper is also a Core wrapper
if (!(levelWrapper instanceof ILevelWrapper coreLevelWrapper)) if (!(levelWrapper instanceof ILevelWrapper))
{ {
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"]."); throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
} }
ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper;
@@ -301,10 +304,11 @@ public class WrapperFactory implements IWrapperFactory
public IDhApiBlockStateWrapper getBlockStateWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper) public IDhApiBlockStateWrapper getBlockStateWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper)
{ {
// confirm the API level wrapper is also a Core wrapper // confirm the API level wrapper is also a Core wrapper
if (!(levelWrapper instanceof ILevelWrapper coreLevelWrapper)) if (!(levelWrapper instanceof ILevelWrapper))
{ {
throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"]."); throw new ClassCastException("Invalid ["+IDhApiLevelWrapper.class.getSimpleName()+"] value given. Level wrapper object must be one given by the DH API (it can't be a custom implementation), specifically of type ["+ILevelWrapper.class.getName()+"].");
} }
ILevelWrapper coreLevelWrapper = (ILevelWrapper) levelWrapper;
@@ -313,11 +317,12 @@ public class WrapperFactory implements IWrapperFactory
{ {
throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray)); throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray));
} }
if (!(objectArray[0] instanceof BlockState blockState)) if (!(objectArray[0] instanceof BlockState))
{ {
throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray)); throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray));
} }
BlockState blockState = (BlockState) objectArray[0];
return BlockStateWrapper.fromBlockState(blockState, coreLevelWrapper); return BlockStateWrapper.fromBlockState(blockState, coreLevelWrapper);
#else #else
// See preprocessor comment in createChunkWrapper() for full documentation (not a typo, check createChunkWrapper()'s else statement for full documentation) // See preprocessor comment in createChunkWrapper() for full documentation (not a typo, check createChunkWrapper()'s else statement for full documentation)
@@ -266,7 +266,7 @@ public class BiomeWrapper implements IBiomeWrapper
} }
return EMPTY_WRAPPER; return EMPTY_WRAPPER;
} }
else if (resourceLocationString.trim().isEmpty() || resourceLocationString.isEmpty()) else if (resourceLocationString.trim().isEmpty() || resourceLocationString.equals(""))
{ {
LOGGER.warn("Null biome string deserialized."); LOGGER.warn("Null biome string deserialized.");
return EMPTY_WRAPPER; return EMPTY_WRAPPER;
@@ -34,7 +34,6 @@ import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.SoundType; import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.Property;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.awt.*; import java.awt.*;
@@ -234,7 +233,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
String ignoreBlockCsv = config.get(); String ignoreBlockCsv = config.get();
if (ignoreBlockCsv != null) if (ignoreBlockCsv != null)
{ {
blockStringList.addAll(List.of(ignoreBlockCsv.split(","))); blockStringList.addAll(Arrays.asList(ignoreBlockCsv.split(",")));
} }
return getBlockWrappers(blockStringList, levelWrapper); return getBlockWrappers(blockStringList, levelWrapper);
@@ -462,7 +461,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
// we need the final string for the concurrent hash map later // we need the final string for the concurrent hash map later
final String finalResourceStateString = resourceStateString; final String finalResourceStateString = resourceStateString;
if (finalResourceStateString.equals(AIR_STRING) || finalResourceStateString.isEmpty()) // the empty string shouldn't normally happen, but just in case if (finalResourceStateString.equals(AIR_STRING) || finalResourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case
{ {
return AIR; return AIR;
} }
@@ -603,7 +602,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
// alphabetically sort the list so they are always in the same order // alphabetically sort the list so they are always in the same order
List<net.minecraft.world.level.block.state.properties.Property<?>> sortedBlockPropteryList = new ArrayList<>(blockPropertyCollection); List<net.minecraft.world.level.block.state.properties.Property<?>> sortedBlockPropteryList = new ArrayList<>(blockPropertyCollection);
sortedBlockPropteryList.sort(Comparator.comparing(Property::getName)); sortedBlockPropteryList.sort((a, b) -> a.getName().compareTo(b.getName()));
StringBuilder stringBuilder = new StringBuilder(); StringBuilder stringBuilder = new StringBuilder();
@@ -17,7 +17,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>. * along with this program. If not, see <https://www.gnu.org/licenses/>.
*/ */
package com.seibel.distanthorizons.common.wrappers.block.cache; package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper; import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.TextureAtlasSpriteWrapper; import com.seibel.distanthorizons.common.wrappers.block.TextureAtlasSpriteWrapper;
@@ -31,7 +31,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapp
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BakedQuad; import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite; import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.world.level.LevelReader; import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.Block;
@@ -48,73 +47,70 @@ import org.apache.logging.log4j.Logger;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
/** /**
* @version 2022-9-16 * This stores and calculates the colors
* the given {@link BlockState} should have based
* on the given {@link IClientLevelWrapper}.
*
* @see ColorUtil
*/ */
public class ClientBlockStateCache public class ClientBlockStateColorCache
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>(); private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>();
private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>(); private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>();
/**
* Methods using MC's "RandomSource" object aren't thread safe <br>
* so we need to put locks around that logic. <br>
* specifically:
* <code>
* getBlockModel(this.blockState).getQuads(this.blockState, direction, RANDOM)
* </code>
*/
private static final ReentrantLock RESOLVE_LOCK = new ReentrantLock();
/** This is the order each direction on a block is processed when attempting to get the texture/color */
private static final Direction[] COLOR_RESOLUTION_DIRECTION_ORDER = { Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN };
private static final int FLOWER_COLOR_SCALE = 5;
#if MC_VER < MC_1_19_2 #if MC_VER < MC_1_19_2
public static final Random random = new Random(0); private static final Random RANDOM = new Random(0);
#else #else
public static final RandomSource random = RandomSource.create(); /** Note: this object isn't thread safe and must be put in a lock */
private static final RandomSource RANDOM = RandomSource.create();
#endif #endif
public final IClientLevelWrapper levelWrapper; private final IClientLevelWrapper levelWrapper;
public final BlockState blockState; private final BlockState blockState;
public final LevelReader level; private final LevelReader level;
public final BlockPos pos;
private boolean isColorResolved = false;
private int baseColor = 0;
private boolean needShade = true;
private boolean needPostTinting = false;
private int tintIndex = 0;
public ClientBlockStateCache(BlockState blockState, IClientLevelWrapper samplingLevel, DhBlockPos samplingPos) //===========//
{ // constants //
this.blockState = blockState; //===========//
this.levelWrapper = samplingLevel;
this.level = (LevelReader) samplingLevel.getWrappedMcObject();
this.pos = McObjectConverter.Convert(samplingPos);
this.resolveColors();
//LOGGER.info("ClientBlocKCache created for {}", blockState);
}
boolean isColorResolved = false; private static final int MIN_SRGB_BITS = 0x39000000; // 2^(-13)
int baseColor = 0; //TODO: Impl per-face color private static final int MAX_SRGB_BITS = 0x3f7fffff; // 1.0 - f32::EPSILON
boolean needShade = true; private static final float MIN_SRGB_BOUND = Float.intBitsToFloat(MIN_SRGB_BITS);
boolean needPostTinting = false; private static final float MAX_SRGB_BOUND = Float.intBitsToFloat(MAX_SRGB_BITS);
int tintIndex = 0;
private static final int[] linearToSrgbTable = new int[]
public static final int FLOWER_COLOR_SCALE = 5;
enum ColorMode
{
Default,
Flower,
Leaves,
Chisel,
Glass;
static ColorMode getColorMode(Block b)
{ {
if (b instanceof LeavesBlock) return Leaves;
if (b instanceof FlowerBlock) return Flower;
if (b.toString().contains("glass")) return Glass;
if (b.toString().equals("Block{chiselsandbits:chiseled}")) return Chisel;
return Default;
}
}
//Way to efficiently do this was suggested by IMS from sodium. This is where those numbers and support code was lifted from.
private static final int MIN_BITS = 0x39000000; // 2^(-13)
private static final int MAX_BITS = 0x3f7fffff; // 1.0 - f32::EPSILON
private static final float MIN_BOUND = Float.intBitsToFloat(MIN_BITS);
private static final float MAX_BOUND = Float.intBitsToFloat(MAX_BITS);
private static final int[] linearToSrgbTable = new int[] {
0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d, 0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d,
0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a, 0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033, 0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
@@ -128,81 +124,139 @@ public class ClientBlockStateCache
0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401, 0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559, 0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723, 0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
}; };
private static final float[] srgbToLinearTable = new float[] { private static final float[] srgbToLinearTable = new float[]
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, 0.0f, 0.000303527f, 0.000607054f, 0.00091058103f, 0.001214108f, 0.001517635f, 0.0018211621f, 0.002124689f,
0.0047769533f, 0.005181517f, 0.0056053917f, 0.0060488326f, 0.006512091f, 0.00699541f, 0.0074990317f, 0.002428216f, 0.002731743f, 0.00303527f, 0.0033465356f, 0.003676507f, 0.004024717f, 0.004391442f,
0.008023192f, 0.008568125f, 0.009134057f, 0.009721218f, 0.010329823f, 0.010960094f, 0.011612245f, 0.0047769533f, 0.005181517f, 0.0056053917f, 0.0060488326f, 0.006512091f, 0.00699541f, 0.0074990317f,
0.012286487f, 0.012983031f, 0.013702081f, 0.014443844f, 0.015208514f, 0.015996292f, 0.016807375f, 0.008023192f, 0.008568125f, 0.009134057f, 0.009721218f, 0.010329823f, 0.010960094f, 0.011612245f,
0.017641952f, 0.018500218f, 0.019382361f, 0.020288562f, 0.02121901f, 0.022173883f, 0.023153365f, 0.012286487f, 0.012983031f, 0.013702081f, 0.014443844f, 0.015208514f, 0.015996292f, 0.016807375f,
0.02415763f, 0.025186857f, 0.026241222f, 0.027320892f, 0.028426038f, 0.029556843f, 0.03071345f, 0.03189604f, 0.017641952f, 0.018500218f, 0.019382361f, 0.020288562f, 0.02121901f, 0.022173883f, 0.023153365f,
0.033104774f, 0.03433981f, 0.035601325f, 0.036889452f, 0.038204376f, 0.039546248f, 0.04091521f, 0.042311423f, 0.02415763f, 0.025186857f, 0.026241222f, 0.027320892f, 0.028426038f, 0.029556843f, 0.03071345f, 0.03189604f,
0.043735042f, 0.045186214f, 0.046665095f, 0.048171833f, 0.049706575f, 0.051269468f, 0.052860655f, 0.05448028f, 0.033104774f, 0.03433981f, 0.035601325f, 0.036889452f, 0.038204376f, 0.039546248f, 0.04091521f, 0.042311423f,
0.056128494f, 0.057805434f, 0.05951124f, 0.06124607f, 0.06301003f, 0.06480328f, 0.06662595f, 0.06847818f, 0.043735042f, 0.045186214f, 0.046665095f, 0.048171833f, 0.049706575f, 0.051269468f, 0.052860655f, 0.05448028f,
0.07036011f, 0.07227186f, 0.07421358f, 0.07618539f, 0.07818743f, 0.08021983f, 0.082282715f, 0.084376216f, 0.056128494f, 0.057805434f, 0.05951124f, 0.06124607f, 0.06301003f, 0.06480328f, 0.06662595f, 0.06847818f,
0.086500466f, 0.088655606f, 0.09084173f, 0.09305898f, 0.095307484f, 0.09758736f, 0.09989874f, 0.10224175f, 0.07036011f, 0.07227186f, 0.07421358f, 0.07618539f, 0.07818743f, 0.08021983f, 0.082282715f, 0.084376216f,
0.10461649f, 0.10702311f, 0.10946172f, 0.111932434f, 0.11443538f, 0.116970696f, 0.11953845f, 0.12213881f, 0.086500466f, 0.088655606f, 0.09084173f, 0.09305898f, 0.095307484f, 0.09758736f, 0.09989874f, 0.10224175f,
0.12477186f, 0.12743773f, 0.13013652f, 0.13286836f, 0.13563336f, 0.13843165f, 0.14126332f, 0.1441285f, 0.10461649f, 0.10702311f, 0.10946172f, 0.111932434f, 0.11443538f, 0.116970696f, 0.11953845f, 0.12213881f,
0.1470273f, 0.14995982f, 0.15292618f, 0.1559265f, 0.15896086f, 0.16202943f, 0.16513224f, 0.16826946f, 0.12477186f, 0.12743773f, 0.13013652f, 0.13286836f, 0.13563336f, 0.13843165f, 0.14126332f, 0.1441285f,
0.17144115f, 0.17464745f, 0.17788847f, 0.1811643f, 0.18447503f, 0.1878208f, 0.19120172f, 0.19461787f, 0.1470273f, 0.14995982f, 0.15292618f, 0.1559265f, 0.15896086f, 0.16202943f, 0.16513224f, 0.16826946f,
0.19806935f, 0.2015563f, 0.20507877f, 0.2086369f, 0.21223079f, 0.21586053f, 0.21952623f, 0.22322798f, 0.17144115f, 0.17464745f, 0.17788847f, 0.1811643f, 0.18447503f, 0.1878208f, 0.19120172f, 0.19461787f,
0.22696589f, 0.23074007f, 0.23455065f, 0.23839766f, 0.2422812f, 0.2462014f, 0.25015837f, 0.25415218f, 0.19806935f, 0.2015563f, 0.20507877f, 0.2086369f, 0.21223079f, 0.21586053f, 0.21952623f, 0.22322798f,
0.2581829f, 0.26225072f, 0.26635566f, 0.27049786f, 0.27467737f, 0.27889434f, 0.2831488f, 0.2874409f, 0.22696589f, 0.23074007f, 0.23455065f, 0.23839766f, 0.2422812f, 0.2462014f, 0.25015837f, 0.25415218f,
0.2917707f, 0.29613832f, 0.30054384f, 0.30498737f, 0.30946895f, 0.31398875f, 0.31854683f, 0.32314324f, 0.2581829f, 0.26225072f, 0.26635566f, 0.27049786f, 0.27467737f, 0.27889434f, 0.2831488f, 0.2874409f,
0.32777813f, 0.33245158f, 0.33716366f, 0.34191445f, 0.3467041f, 0.3515327f, 0.35640025f, 0.36130688f, 0.2917707f, 0.29613832f, 0.30054384f, 0.30498737f, 0.30946895f, 0.31398875f, 0.31854683f, 0.32314324f,
0.3662527f, 0.37123778f, 0.37626222f, 0.3813261f, 0.38642952f, 0.39157256f, 0.3967553f, 0.40197787f, 0.32777813f, 0.33245158f, 0.33716366f, 0.34191445f, 0.3467041f, 0.3515327f, 0.35640025f, 0.36130688f,
0.4072403f, 0.4125427f, 0.41788515f, 0.42326775f, 0.42869055f, 0.4341537f, 0.43965724f, 0.44520125f, 0.3662527f, 0.37123778f, 0.37626222f, 0.3813261f, 0.38642952f, 0.39157256f, 0.3967553f, 0.40197787f,
0.45078585f, 0.45641106f, 0.46207705f, 0.46778384f, 0.47353154f, 0.47932023f, 0.48514998f, 0.4910209f, 0.4072403f, 0.4125427f, 0.41788515f, 0.42326775f, 0.42869055f, 0.4341537f, 0.43965724f, 0.44520125f,
0.49693304f, 0.5028866f, 0.50888145f, 0.5149178f, 0.5209957f, 0.52711535f, 0.5332766f, 0.5394797f, 0.45078585f, 0.45641106f, 0.46207705f, 0.46778384f, 0.47353154f, 0.47932023f, 0.48514998f, 0.4910209f,
0.5457247f, 0.5520116f, 0.5583406f, 0.5647117f, 0.57112503f, 0.57758063f, 0.5840786f, 0.590619f, 0.597202f, 0.49693304f, 0.5028866f, 0.50888145f, 0.5149178f, 0.5209957f, 0.52711535f, 0.5332766f, 0.5394797f,
0.60382754f, 0.61049575f, 0.61720675f, 0.62396055f, 0.63075733f, 0.637597f, 0.6444799f, 0.6514058f, 0.5457247f, 0.5520116f, 0.5583406f, 0.5647117f, 0.57112503f, 0.57758063f, 0.5840786f, 0.590619f, 0.597202f,
0.65837497f, 0.66538745f, 0.67244333f, 0.6795426f, 0.68668544f, 0.69387203f, 0.70110214f, 0.70837605f, 0.60382754f, 0.61049575f, 0.61720675f, 0.62396055f, 0.63075733f, 0.637597f, 0.6444799f, 0.6514058f,
0.7156938f, 0.72305536f, 0.730461f, 0.7379107f, 0.7454045f, 0.75294244f, 0.76052475f, 0.7681514f, 0.77582246f, 0.65837497f, 0.66538745f, 0.67244333f, 0.6795426f, 0.68668544f, 0.69387203f, 0.70110214f, 0.70837605f,
0.78353804f, 0.79129815f, 0.79910296f, 0.8069525f, 0.8148468f, 0.822786f, 0.8307701f, 0.83879924f, 0.84687346f, 0.7156938f, 0.72305536f, 0.730461f, 0.7379107f, 0.7454045f, 0.75294244f, 0.76052475f, 0.7681514f, 0.77582246f,
0.8549928f, 0.8631574f, 0.87136734f, 0.8796226f, 0.8879232f, 0.89626956f, 0.90466136f, 0.913099f, 0.92158204f, 0.78353804f, 0.79129815f, 0.79910296f, 0.8069525f, 0.8148468f, 0.822786f, 0.8307701f, 0.83879924f, 0.84687346f,
0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f 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
};
private static int linearToSrgb(float c) {
if (!(c > MIN_BOUND)) {
c = MIN_BOUND;
}
if (c > MAX_BOUND) {
c = MAX_BOUND;
}
int inputBits = Float.floatToRawIntBits(c);
int entry = linearToSrgbTable[((inputBits - MIN_BITS) >> 20)];
int bias = (entry >>> 16) << 9;
int scale = entry & 0xffff;
int t = (inputBits >>> 12) & 0xff;
return (bias + (scale * t)) >>> 16;
}
//////////////
private static int getWidth(TextureAtlasSprite texture)
//=============//
// constructor //
//=============//
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper samplingLevel)
{ {
#if MC_VER < MC_1_19_4 this.blockState = blockState;
return texture.getWidth(); this.levelWrapper = samplingLevel;
#else this.level = (LevelReader) samplingLevel.getWrappedMcObject();
return texture.contents().width(); this.resolveColors();
#endif
} }
private static int getHeight(TextureAtlasSprite texture)
//===================//
// color calculation //
//===================//
private void resolveColors()
{ {
#if MC_VER < MC_1_19_4 if (this.isColorResolved)
return texture.getHeight(); {
#else return;
return texture.contents().height(); }
#endif
try
{
// getQuads() isn't thread safe so we need to put this logic in a lock
RESOLVE_LOCK.lock();
if (this.blockState.getFluidState().isEmpty())
{
// look for the first non-empty direction
List<BakedQuad> quads = null;
for (Direction direction : COLOR_RESOLUTION_DIRECTION_ORDER)
{
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
getBlockModel(this.blockState).getQuads(this.blockState, direction, RANDOM);
if (quads != null && !quads.isEmpty()
&& !(
this.blockState.getBlock() instanceof RotatedPillarBlock
&& direction == Direction.UP
)
)
{
break;
}
}
if (quads == null || quads.isEmpty())
{
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
getBlockModel(this.blockState).getQuads(this.blockState, null, RANDOM);
}
if (quads != null && !quads.isEmpty())
{
this.needPostTinting = quads.get(0).isTinted();
this.needShade = quads.get(0).isShade();
this.tintIndex = quads.get(0).getTintIndex();
this.baseColor = calculateColorFromTexture(
#if MC_VER < MC_1_17_1 quads.get(0).sprite,
#else quads.get(0).getSprite(), #endif
ColorMode.getColorMode(this.blockState.getBlock()));
}
else
{
// Backup method.
this.needPostTinting = false;
this.needShade = false;
this.tintIndex = 0;
this.baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
ColorMode.getColorMode(this.blockState.getBlock()));
}
}
else
{
// Liquid Block
this.needPostTinting = true;
this.needShade = false;
this.tintIndex = 0;
this.baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
ColorMode.getColorMode(this.blockState.getBlock()));
}
this.isColorResolved = true;
}
finally
{
RESOLVE_LOCK.unlock();
}
} }
//TODO: Perhaps make this not just use the first frame? //TODO: Perhaps make this not just use the first frame?
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode) private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode)
{ {
@@ -212,13 +266,15 @@ public class ClientBlockStateCache
double green = 0; double green = 0;
double blue = 0; double blue = 0;
int tempColor; int tempColor;
//make Chiseled block not render. Since ColorMode is set per block, you only need to check it once
// don't render Chiseled blocks.
// Since ColorMode is set per block, you only need to check this once.
if (colorMode != ColorMode.Chisel) if (colorMode != ColorMode.Chisel)
{ {
// textures normally use u and v instead of x and y // textures normally use u and v instead of x and y
for (int v = 0; v < getHeight(texture); v++) for (int v = 0; v < getTextureHeight(texture); v++)
{ {
for (int u = 0; u < getWidth(texture); u++) for (int u = 0; u < getTextureWidth(texture); u++)
{ {
//note: Minecraft color format is: 0xAA BB GG RR //note: Minecraft color format is: 0xAA BB GG RR
//________ DH mod color format is: 0xAA RR GG BB //________ DH mod color format is: 0xAA RR GG BB
@@ -270,8 +326,10 @@ public class ClientBlockStateCache
} }
if (count == 0) if (count == 0)
{
// this block is entirely transparent // this block is entirely transparent
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255); tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
}
else else
{ {
// determine the average color // determine the average color
@@ -281,6 +339,7 @@ public class ClientBlockStateCache
linearToSrgb((float) (green / (double) alpha)), linearToSrgb((float) (green / (double) alpha)),
linearToSrgb((float) (blue / (double) alpha))); linearToSrgb((float) (blue / (double) alpha)));
} }
//check if not missing texture //check if not missing texture
if (tempColor == ColorUtil.rgbToInt(255, 182, 0, 182)) if (tempColor == ColorUtil.rgbToInt(255, 182, 0, 182))
{ {
@@ -289,61 +348,53 @@ public class ClientBlockStateCache
} }
return tempColor; return tempColor;
} }
private static final Direction[] DIRECTION_ORDER = {Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN}; private static int getTextureWidth(TextureAtlasSprite texture)
private void resolveColors()
{ {
if (isColorResolved) return; #if MC_VER < MC_1_19_4
if (blockState.getFluidState().isEmpty()) return texture.getWidth();
{ #else
List<BakedQuad> quads = null; return texture.contents().width();
for (Direction direction : DIRECTION_ORDER) #endif
{ }
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper(). private static int getTextureHeight(TextureAtlasSprite texture)
getBlockModel(blockState).getQuads(blockState, direction, random); // TODO getQuads sometimes throws a "makeThreadingException", is there anything we can do about that? Note: this isn't a critical issue, it just prints an ugly error and the render data will need to be regenered. {
if (quads != null && !quads.isEmpty() && #if MC_VER < MC_1_19_4
!(blockState.getBlock() instanceof RotatedPillarBlock && direction == Direction.UP)) return texture.getHeight();
break; #else
} ; return texture.contents().height();
if (quads == null || quads.isEmpty()) #endif
{ }
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper(). /**
getBlockModel(blockState).getQuads(blockState, null, random); * This method was suggested by IMS from the Iris/Sodium team.
} * That's where the numbers and code are based.
if (quads != null && !quads.isEmpty()) */
{ private static int linearToSrgb(float c)
needPostTinting = quads.getFirst().isTinted(); {
needShade = quads.getFirst().isShade(); if (!(c > MIN_SRGB_BOUND)) {
tintIndex = quads.getFirst().getTintIndex(); c = MIN_SRGB_BOUND;
baseColor = calculateColorFromTexture(
#if MC_VER < MC_1_17_1 quads.get(0).sprite,
#else quads.getFirst().getSprite(), #endif
ColorMode.getColorMode(blockState.getBlock()));
}
else
{ // Backup method.
needPostTinting = false;
needShade = false;
tintIndex = 0;
baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(blockState),
ColorMode.getColorMode(blockState.getBlock()));
}
} }
else
{ // Liquid Block if (c > MAX_SRGB_BOUND) {
needPostTinting = true; c = MAX_SRGB_BOUND;
needShade = false;
tintIndex = 0;
baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(blockState),
ColorMode.getColorMode(blockState.getBlock()));
} }
isColorResolved = true; int inputBits = Float.floatToRawIntBits(c);
int entry = linearToSrgbTable[((inputBits - MIN_SRGB_BITS) >> 20)];
int bias = (entry >>> 16) << 9;
int scale = entry & 0xffff;
int t = (inputBits >>> 12) & 0xff;
return (bias + (scale * t)) >>> 16;
} }
public int getAndResolveFaceColor(BiomeWrapper biome, DhBlockPos pos)
//===============//
// public getter //
//===============//
public int getColor(BiomeWrapper biome, DhBlockPos pos)
{ {
// FIXME: impl per-face colors
// only get the tint if the block needs to be tinted // only get the tint if the block needs to be tinted
if (!this.needPostTinting) if (!this.needPostTinting)
{ {
@@ -409,4 +460,30 @@ public class ClientBlockStateCache
} }
} }
//================//
// helper classes //
//================//
enum ColorMode
{
Default,
Flower,
Leaves,
Chisel,
Glass;
static ColorMode getColorMode(Block block)
{
if (block instanceof LeavesBlock) return Leaves;
if (block instanceof FlowerBlock) return Flower;
if (block.toString().contains("glass")) return Glass;
if (block.toString().equals("Block{chiselsandbits:chiseled}")) return Chisel;
return Default;
}
}
} }
@@ -1,48 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block.cache;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import net.minecraft.world.level.block.state.BlockState;
import java.util.concurrent.ConcurrentHashMap;
public class ClientBlockDetailMap
{
private final ConcurrentHashMap<BlockState, ClientBlockStateCache> blockCache = new ConcurrentHashMap<>();
//private final ConcurrentHashMap<#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
private final ClientLevelWrapper level;
public ClientBlockDetailMap(ClientLevelWrapper level) { this.level = level; }
public ClientBlockStateCache getBlockStateData(BlockState state, DhBlockPos pos)
{ //TODO: Allow a per pos unique setting
return blockCache.computeIfAbsent(state, (s) -> new ClientBlockStateCache(s, level, pos));
}
public void clear() { blockCache.clear(); }
public int getColor(BlockState state, BiomeWrapper biome, DhBlockPos pos)
{
return getBlockStateData(state, pos).getAndResolveFaceColor(biome, pos);
}
}
@@ -1,43 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block.cache;
import java.util.concurrent.ConcurrentHashMap;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import net.minecraft.world.level.block.state.BlockState;
public class ServerBlockDetailMap
{
private final ConcurrentHashMap<BlockState, ServerBlockStateCache> blockCache = new ConcurrentHashMap<>();
//private final ConcurrentHashMap<#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif, Biome> biomeMap = new ConcurrentHashMap<>();
private final ServerLevelWrapper level;
public ServerBlockDetailMap(ServerLevelWrapper level) { this.level = level; }
public ServerBlockStateCache getBlockStateData(BlockState state, DhBlockPos pos)
{ //TODO: Allow a per pos unique setting
return blockCache.computeIfAbsent(state, (s) -> new ServerBlockStateCache(s, level, new DhBlockPos(0, 0, 0)));
}
public void clear() { blockCache.clear(); }
}
@@ -1,104 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.common.wrappers.block.cache;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.apache.logging.log4j.Logger;
import java.util.Arrays;
/**
* @version 2022-9-16
*/
public class ServerBlockStateCache
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
public final BlockState state;
public final LevelReader level;
public final BlockPos pos;
public ServerBlockStateCache(BlockState blockState, ILevelWrapper samplingLevel, DhBlockPos samplingPos)
{
state = blockState;
level = (LevelReader) samplingLevel.getWrappedMcObject();
pos = McObjectConverter.Convert(samplingPos);
resolveShapes();
//LOGGER.info("ServerBlockState created for {}", blockState);
}
boolean noCollision = false;
boolean[] occludeFaces = null;
boolean[] fullFaces = null;
boolean isShapeResolved = false;
public void resolveShapes()
{
if (isShapeResolved) return;
if (state.getFluidState().isEmpty())
{
noCollision = state.getCollisionShape(level, pos).isEmpty();
occludeFaces = new boolean[6];
if (state.canOcclude())
{
for (Direction dir : Direction.values())
{
// Note: isEmpty() isn't quite correct... best would be a isFull() or something...
occludeFaces[McObjectConverter.Convert(dir).ordinal()]
= !state.getFaceOcclusionShape(level, pos, dir).isEmpty();
}
}
VoxelShape voxelShape = state.getShape(level, pos);
fullFaces = new boolean[6];
if (!voxelShape.isEmpty())
{
for (Direction dir : Direction.values())
{
VoxelShape faceShape = voxelShape.getFaceShape(dir);
AABB aabb = faceShape.bounds();
boolean xFull = aabb.minX <= 0.01 && aabb.maxX >= 0.99;
boolean yFull = aabb.minY <= 0.01 && aabb.maxY >= 0.99;
boolean zFull = aabb.minZ <= 0.01 && aabb.maxZ >= 0.99;
fullFaces[McObjectConverter.Convert(dir).ordinal()] =
(xFull || dir.getAxis().equals(Direction.Axis.X))
&& (yFull || dir.getAxis().equals(Direction.Axis.Y))
&& (zFull || dir.getAxis().equals(Direction.Axis.Z));
}
}
}
else
{ // Liquid Block. Treat as full block
occludeFaces = new boolean[6];
Arrays.fill(occludeFaces, true);
fullFaces = new boolean[6];
Arrays.fill(fullFaces, true);
}
}
}
@@ -83,7 +83,7 @@ public class ChunkWrapper implements IChunkWrapper
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final Logger LOGGER = DhLoggerBuilder.getLogger();
/** can be used for interactions with the underlying chunk where creating new BlockPos objects could cause issues for the garbage collector. */ /** can be used for interactions with the underlying chunk where creating new BlockPos objects could cause issues for the garbage collector. */
private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(BlockPos.MutableBlockPos::new); private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new BlockPos.MutableBlockPos());
private final ChunkAccess chunk; private final ChunkAccess chunk;
@@ -345,8 +345,9 @@ public class ChunkWrapper implements IChunkWrapper
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1 #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
return false; // MC's lighting engine doesn't work consistently enough to trust for 1.16 or 1.17 return false; // MC's lighting engine doesn't work consistently enough to trust for 1.16 or 1.17
#else #else
if (this.chunk instanceof LevelChunk levelChunk) if (this.chunk instanceof LevelChunk)
{ {
LevelChunk levelChunk = (LevelChunk) this.chunk;
if (levelChunk.getLevel() instanceof ClientLevel) if (levelChunk.getLevel() instanceof ClientLevel)
{ {
// connected to a server // connected to a server
@@ -473,7 +474,9 @@ public class ChunkWrapper implements IChunkWrapper
}); });
#else #else
this.chunk.findBlockLightSources((blockPos, blockState) -> this.chunk.findBlockLightSources((blockPos, blockState) ->
this.blockLightPosList.add(new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()))); {
this.blockLightPosList.add(new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ()));
});
#endif #endif
} }
@@ -504,8 +507,9 @@ public class ChunkWrapper implements IChunkWrapper
/** Should be called after client light updates are triggered. */ /** Should be called after client light updates are triggered. */
private void updateIsClientLightingCorrect() private void updateIsClientLightingCorrect()
{ {
if (this.chunk instanceof LevelChunk levelChunk && ((LevelChunk) this.chunk).getLevel() instanceof ClientLevel) if (this.chunk instanceof LevelChunk && ((LevelChunk) this.chunk).getLevel() instanceof ClientLevel)
{ {
LevelChunk levelChunk = (LevelChunk) this.chunk;
ClientChunkCache clientChunkCache = ((ClientLevel) levelChunk.getLevel()).getChunkSource(); ClientChunkCache clientChunkCache = ((ClientLevel) levelChunk.getLevel()).getChunkSource();
this.isMcClientLightingCorrect = clientChunkCache.getChunkForLighting(this.chunk.getPos().x, this.chunk.getPos().z) != null && this.isMcClientLightingCorrect = clientChunkCache.getChunkForLighting(this.chunk.getPos().x, this.chunk.getPos().z) != null &&
#if MC_VER <= MC_1_17_1 #if MC_VER <= MC_1_17_1
@@ -145,9 +145,9 @@ public class ClassicConfigGUI
case 0: case 0:
((EntryInfo) info.guiValue).error = null; break; ((EntryInfo) info.guiValue).error = null; break;
case -1: case -1:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMinimum length is " + ((ConfigEntry<?>) info).getMin())); break; ((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMinimum length is " + ((ConfigEntry) info).getMin())); break;
case 1: case 1:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMaximum length is " + ((ConfigEntry<?>) info).getMax())); break; ((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cMaximum length is " + ((ConfigEntry) info).getMax())); break;
case 2: case 2:
((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cValue is invalid")); break; ((EntryInfo) info.guiValue).error = new AbstractMap.SimpleEntry<>(editBox, TextOrTranslatable("§cValue is invalid")); break;
} }
@@ -235,7 +235,7 @@ public class ClassicConfigGUI
ConfigBase.INSTANCE.configFileINSTANCE.saveToFile(); ConfigBase.INSTANCE.configFileINSTANCE.saveToFile();
Objects.requireNonNull(this.minecraft).setScreen(this.parent); Objects.requireNonNull(this.minecraft).setScreen(this.parent);
CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach(Runnable::run); CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run());
} }
@Override @Override
@@ -319,7 +319,7 @@ public class ClassicConfigGUI
CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach(Runnable::run); CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run());
} }
@@ -332,7 +332,7 @@ public class ClassicConfigGUI
if (ConfigEntry.class.isAssignableFrom(info.getClass())) if (ConfigEntry.class.isAssignableFrom(info.getClass()))
{ {
Button.OnPress btnAction = button -> { Button.OnPress btnAction = button -> {
((ConfigEntry) info).uiSetWithoutSaving(((ConfigEntry<?>) info).getDefaultValue()); ((ConfigEntry) info).uiSetWithoutSaving(((ConfigEntry) info).getDefaultValue());
((EntryInfo) info.guiValue).index = 0; ((EntryInfo) info.guiValue).index = 0;
this.reload = true; this.reload = true;
Objects.requireNonNull(minecraft).setScreen(this); Objects.requireNonNull(minecraft).setScreen(this);
@@ -376,7 +376,9 @@ public class ClassicConfigGUI
} }
if (ConfigUIButton.class.isAssignableFrom(info.getClass())) if (ConfigUIButton.class.isAssignableFrom(info.getClass()))
{ {
Button widget = MakeBtn(name, this.width / 2 - 100, this.height - 28, 100 * 2, 20, (button -> ((ConfigUIButton) info).runAction())); Button widget = MakeBtn(name, this.width / 2 - 100, this.height - 28, 100 * 2, 20, (button -> {
((ConfigUIButton) info).runAction();
}));
this.list.addButton(widget, null, null, null); this.list.addButton(widget, null, null, null);
return; return;
} }
@@ -25,18 +25,19 @@ public class GetConfigScreen
// This shouldn't be here, but I need a way to test it after Minecraft inits its assets // This shouldn't be here, but I need a way to test it after Minecraft inits its assets
//System.out.println(ConfigBase.INSTANCE.generateLang(false, true)); //System.out.println(ConfigBase.INSTANCE.generateLang(false, true));
return switch (useScreen) switch (useScreen)
{ {
case Classic -> ClassicConfigGUI.getScreen(ConfigBase.INSTANCE, parent, "client"); case Classic:
case OpenGL -> return ClassicConfigGUI.getScreen(ConfigBase.INSTANCE, parent, "client");
{ case OpenGL:
MinecraftScreen.getScreen(parent, new OpenGLConfigScreen(), ModInfo.ID + ".title"); MinecraftScreen.getScreen(parent, new OpenGLConfigScreen(), ModInfo.ID + ".title");
yield null; return null;
}
// case JavaFX -> MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new JavaScreenHandlerScreen.ExampleScreen()), ModInfo.ID + ".title"); // case JavaFX -> MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new JavaScreenHandlerScreen.ExampleScreen()), ModInfo.ID + ".title");
case JavaFX -> MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new ConfigScreen()), ModInfo.ID + ".title"); case JavaFX:
default -> throw new IllegalArgumentException("No config screen implementation defined for [" + useScreen + "]."); return MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new ConfigScreen()), ModInfo.ID + ".title");
}; default:
throw new IllegalArgumentException("No config screen implementation defined for ["+useScreen+"].");
}
} }
} }
@@ -134,7 +134,9 @@ public class ChangelogScreen extends DhScreen
this.addBtn( // Close this.addBtn( // Close
MakeBtn(Translatable(ModInfo.ID + ".general.back"), 5, this.height - 25, 100, 20, (btn) -> this.onClose()) MakeBtn(Translatable(ModInfo.ID + ".general.back"), 5, this.height - 25, 100, 20, (btn) -> {
this.onClose();
})
); );
@@ -135,7 +135,9 @@ public class UpdateModScreen extends DhScreen
}) })
); );
this.addBtn( // Later (not now) this.addBtn( // Later (not now)
MakeBtn(Translatable(ModInfo.ID + ".updater.later"), this.width / 2 + 2, this.height / 2 + 70, 100, 20, (btn) -> this.onClose()) MakeBtn(Translatable(ModInfo.ID + ".updater.later"), this.width / 2 + 2, this.height / 2 + 70, 100, 20, (btn) -> {
this.onClose();
})
); );
this.addBtn( // Never this.addBtn( // Never
MakeBtn(Translatable(ModInfo.ID + ".updater.never"), this.width / 2 - 102, this.height / 2 + 70, 100, 20, (btn) -> { MakeBtn(Translatable(ModInfo.ID + ".updater.never"), this.width / 2 - 102, this.height / 2 + 70, 100, 20, (btn) -> {
@@ -127,12 +127,20 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
} }
case ENABLED: case ENABLED:
return switch (lodDirection) { switch (lodDirection)
case DOWN -> 0.5F; {
default -> 1.0F; case DOWN:
case NORTH, SOUTH -> 0.8F; return 0.5F;
case WEST, EAST -> 0.6F; default:
}; case UP:
return 1.0F;
case NORTH:
case SOUTH:
return 0.8F;
case WEST:
case EAST:
return 0.6F;
}
case DISABLED: case DISABLED:
return 1.0F; return 1.0F;
@@ -242,7 +250,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Override @Override
public ArrayList<ILevelWrapper> getAllServerWorlds() public ArrayList<ILevelWrapper> getAllServerWorlds()
{ {
ArrayList<ILevelWrapper> worlds = new ArrayList<>(); ArrayList<ILevelWrapper> worlds = new ArrayList<ILevelWrapper>();
Iterable<ServerLevel> serverWorlds = MINECRAFT.getSingleplayerServer().getAllLevels(); Iterable<ServerLevel> serverWorlds = MINECRAFT.getSingleplayerServer().getAllLevels();
for (ServerLevel world : serverWorlds) for (ServerLevel world : serverWorlds)
@@ -120,14 +120,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return new Vec3f(camera.getLookVector().x(), camera.getLookVector().y(), camera.getLookVector().z()); return new Vec3f(camera.getLookVector().x(), camera.getLookVector().y(), camera.getLookVector().z());
} }
@Override
public DhBlockPos getCameraBlockPosition()
{
Camera camera = MC.gameRenderer.getMainCamera();
BlockPos blockPos = camera.getBlockPosition();
return new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ());
}
@Override @Override
/** Unless you really need to know if the player is blind, use {@link MinecraftRenderWrapper#isFogStateSpecial()}/{@link IMinecraftRenderWrapper#isFogStateSpecial()} instead */ /** Unless you really need to know if the player is blind, use {@link MinecraftRenderWrapper#isFogStateSpecial()}/{@link IMinecraftRenderWrapper#isFogStateSpecial()} instead */
public boolean playerHasBlindingEffect() public boolean playerHasBlindingEffect()
@@ -148,43 +140,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return new Vec3d(projectedView.x, projectedView.y, projectedView.z); return new Vec3d(projectedView.x, projectedView.y, projectedView.z);
} }
@Override
public Mat4f getWorldViewMatrix()
{
Camera camera = MC.gameRenderer.getMainCamera();
Vector3f cameraVec3 = new Vector3f(
(float)camera.getPosition().x,
(float)camera.getPosition().y,
(float)camera.getPosition().z);
cameraVec3 = cameraVec3.negate();
Matrix4f matWorldView = new Matrix4f()
.rotateX((float)Math.toRadians(camera.getXRot()))
.rotateY((float)Math.toRadians(camera.getYRot() + 180f))
.translate(cameraVec3);
return new Mat4f(matWorldView);
}
@Override
public Mat4f getDefaultProjectionMatrix(float partialTicks)
{
#if MC_VER < MC_1_17_1
return McObjectConverter.Convert(Minecraft.getInstance().gameRenderer.getProjectionMatrix(Minecraft.getInstance().gameRenderer.getMainCamera(), partialTicks, true));
#else
return McObjectConverter.Convert(MC.gameRenderer.getProjectionMatrix(MC.gameRenderer.getFov(MC.gameRenderer.getMainCamera(), partialTicks, true)));
#endif
}
@Override
public double getGamma()
{
#if MC_VER < MC_1_19_2
return MC.options.gamma;
#else
return MC.options.gamma().get();
#endif
}
@Override @Override
public Color getFogColor(float partialTicks) public Color getFogColor(float partialTicks)
{ {
@@ -314,77 +269,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return getRenderTarget().viewHeight; return getRenderTarget().viewHeight;
} }
/**
* This method returns the ChunkPos of all chunks that Minecraft
* is going to render this frame. <br><br>
* <p>
*/
public boolean usingBackupGetVanillaRenderedChunks = false;
@Override
public HashSet<DhChunkPos> getVanillaRenderedChunks()
{
ISodiumAccessor sodium = ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class);
if (sodium != null)
{
return sodium.getNormalRenderedChunks();
}
IOptifineAccessor optifine = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class);
if (optifine != null)
{
HashSet<DhChunkPos> pos = optifine.getNormalRenderedChunks();
if (pos == null)
pos = getMaximumRenderedChunks();
return pos;
}
if (!usingBackupGetVanillaRenderedChunks)
{
try
{
#if MC_VER < MC_1_20_2
LevelRenderer levelRenderer = MC.levelRenderer;
Collection<LevelRenderer.RenderChunkInfo> chunks =
#if MC_VER < MC_1_18_2 levelRenderer.renderChunks;
#else levelRenderer.renderChunkStorage.get().renderChunks; #endif
return (chunks.stream().map((chunk) -> {
AABB chunkBoundingBox =
#if MC_VER < MC_1_18_2 chunk.chunk.bb;
#else chunk.chunk.getBoundingBox(); #endif
return new DhChunkPos(Math.floorDiv((int) chunkBoundingBox.minX, 16),
Math.floorDiv((int) chunkBoundingBox.minZ, 16));
}).collect(Collectors.toCollection(HashSet::new)));
#else
LevelRenderer levelRenderer = MC.levelRenderer;
Collection<SectionRenderDispatcher.RenderSection> chunks = levelRenderer.visibleSections;
return (chunks.stream().map((chunk) -> {
AABB chunkBoundingBox = chunk.getBoundingBox();
return new DhChunkPos(Math.floorDiv((int) chunkBoundingBox.minX, 16),
Math.floorDiv((int) chunkBoundingBox.minZ, 16));
}).collect(Collectors.toCollection(HashSet::new)));
#endif
}
catch (LinkageError e)
{
try
{
MinecraftClientWrapper.INSTANCE.sendChatMessage(
"\u00A7e\u00A7l\u00A7uWARNING: Distant Horizons: getVanillaRenderedChunks method failed."
+ " Using Backup Method.");
MinecraftClientWrapper.INSTANCE.sendChatMessage(
"\u00A7eOverdraw prevention will be worse than normal.");
}
catch (Exception e2)
{
}
LOGGER.error("getVanillaRenderedChunks Error: ", e);
usingBackupGetVanillaRenderedChunks = true;
}
}
return getMaximumRenderedChunks();
}
@Override @Override
public ILightMapWrapper getLightmapWrapper(ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); } public ILightMapWrapper getLightmapWrapper(ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); }
@@ -5,14 +5,15 @@ import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegist
import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper; import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper; import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.block.cache.ClientBlockDetailMap; import com.seibel.distanthorizons.common.wrappers.block.ClientBlockStateColorCache;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.*; import com.seibel.distanthorizons.core.level.*;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
@@ -22,6 +23,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapp
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource; import net.minecraft.world.level.chunk.ChunkSource;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
@@ -46,7 +48,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
private static final Minecraft MINECRAFT = Minecraft.getInstance(); private static final Minecraft MINECRAFT = Minecraft.getInstance();
private final ClientLevel level; private final ClientLevel level;
private final ClientBlockDetailMap blockMap = new ClientBlockDetailMap(this); private final ConcurrentHashMap<BlockState, ClientBlockStateColorCache> blockCache = new ConcurrentHashMap<>();
private BlockStateWrapper dirtBlockWrapper; private BlockStateWrapper dirtBlockWrapper;
private BiomeWrapper plainsBiomeWrapper; private BiomeWrapper plainsBiomeWrapper;
@@ -111,7 +113,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
} }
catch (Exception e) catch (Exception e)
{ {
LOGGER.error("Failed to get server side wrapper for client level: " + level); LOGGER.error("Failed to get server side wrapper for client level: " + this.level);
return null; return null;
} }
} }
@@ -123,9 +125,13 @@ public class ClientLevelWrapper implements IClientLevelWrapper
//====================// //====================//
@Override @Override
public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockState) public int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockWrapper)
{ {
return this.blockMap.getColor(((BlockStateWrapper) blockState).blockState, (BiomeWrapper) biome, pos); ClientBlockStateColorCache blockColorCache = this.blockCache.computeIfAbsent(
((BlockStateWrapper) blockWrapper).blockState,
(block) -> new ClientBlockStateColorCache(block, this));
return blockColorCache.getColor((BiomeWrapper) biome, pos);
} }
@Override @Override
@@ -145,9 +151,12 @@ public class ClientLevelWrapper implements IClientLevelWrapper
} }
} }
return this.blockMap.getColor(this.dirtBlockWrapper.blockState, BiomeWrapper.EMPTY_WRAPPER, DhBlockPos.ZERO); return this.getBlockColor(DhBlockPos.ZERO,BiomeWrapper.EMPTY_WRAPPER, this.dirtBlockWrapper);
} }
@Override
public void clearBlockColorCache() { this.blockCache.clear(); }
@Override @Override
public IBiomeWrapper getPlainsBiomeWrapper() public IBiomeWrapper getPlainsBiomeWrapper()
{ {
@@ -27,9 +27,7 @@ import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegist
import com.seibel.distanthorizons.common.wrappers.McObjectConverter; import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper; import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper; import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.block.cache.ServerBlockDetailMap;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhBlockPos;
@@ -37,7 +35,6 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
@@ -147,7 +147,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (index == -1) continue; if (index == -1) continue;
times.get(index).add(e.timeNs); times.get(index).add(e.timeNs);
} }
times.getFirst().add(event.getTotalTimeNs()); times.get(0).add(event.getTotalTimeNs());
} }
public String toString() public String toString()
@@ -224,8 +224,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
// constructors // // constructors //
//==============// //==============//
public static ImmutableMap<EDhApiWorldGenerationStep, Integer> BorderNeeded; public static final ImmutableMap<EDhApiWorldGenerationStep, Integer> WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP;
public static int MaxBorderNeeded; public static final int MAX_WORLD_GEN_CHUNK_BORDER_NEEDED;
static static
{ {
@@ -253,8 +253,13 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
builder.put(EDhApiWorldGenerationStep.LIQUID_CARVERS, 0); builder.put(EDhApiWorldGenerationStep.LIQUID_CARVERS, 0);
builder.put(EDhApiWorldGenerationStep.FEATURES, 0); builder.put(EDhApiWorldGenerationStep.FEATURES, 0);
builder.put(EDhApiWorldGenerationStep.LIGHT, 0); builder.put(EDhApiWorldGenerationStep.LIGHT, 0);
BorderNeeded = builder.build(); WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP = builder.build();
MaxBorderNeeded = BorderNeeded.values().stream().mapToInt(Integer::intValue).max().getAsInt();
// TODO this is a test to see if the additional boarder is actually necessary or not.
// If world generators end up having infinite loops or other unexplained issues,
// this should be set back to the commented out logic below
MAX_WORLD_GEN_CHUNK_BORDER_NEEDED = 0;
//MAX_WORLD_GEN_CHUNK_BORDER_NEEDED = WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP.values().stream().mapToInt(Integer::intValue).max().getAsInt();
} }
public BatchGenerationEnvironment(IDhServerLevel serverlevel) public BatchGenerationEnvironment(IDhServerLevel serverlevel)
@@ -383,171 +388,177 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
{ {
EVENT_LOGGER.debug("Lod Generate Event: " + genEvent.minPos); EVENT_LOGGER.debug("Lod Generate Event: " + genEvent.minPos);
ArrayGridList<ChunkWrapper> chunkWrapperList; // Minecraft's generation events expect odd chunk width areas (3x3, 7x7, or 11x11),
DhLitWorldGenRegion region; // but DH submits square generation events (4x4).
DummyLightEngine dummyLightEngine; // We handle this later, although that handling would need to change if the gen size ever changed.
LightGetterAdaptor adaptor; LodUtil.assertTrue(genEvent.size % 2 == 0, "Generation events are expected to be an evan number of chunks wide.");
int borderSize = MaxBorderNeeded;
int refSize = genEvent.size + borderSize * 2;
int borderSize = MAX_WORLD_GEN_CHUNK_BORDER_NEEDED;
// genEvent.size - 1 converts the even width size to an odd number for MC compatability
int refSize = (genEvent.size - 1) + (borderSize * 2);
int refPosX = genEvent.minPos.x - borderSize; int refPosX = genEvent.minPos.x - borderSize;
int refPosZ = genEvent.minPos.z - borderSize; int refPosZ = genEvent.minPos.z - borderSize;
try LightGetterAdaptor lightGetterAdaptor = new LightGetterAdaptor(this.params.level);
DummyLightEngine dummyLightEngine = new DummyLightEngine(lightGetterAdaptor);
//====================================//
// offset and generate odd width area //
//====================================//
// reused data between each offset
HashMap<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos = new HashMap<>();
HashMap<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos = new HashMap<>();
HashMap<DhChunkPos, ChunkAccess> generatedChunkByDhPos = new HashMap<>();
HashMap<DhChunkPos, ChunkWrapper> chunkWrappersByDhPos = new HashMap<>();
// offset 1 chunk in both X and Z direction so we can generate an even number of chunks wide
// while still submitting odd numbers to MC's internal generators
for (int xOffset = 0; xOffset < 2; xOffset++)
{ {
ArrayGridList<ChunkAccess> totalChunks; // final is so the offset can be used in lambdas
final int xOffsetFinal = xOffset;
adaptor = new LightGetterAdaptor(this.params.level); for (int zOffset = 0; zOffset < 2; zOffset++)
dummyLightEngine = new DummyLightEngine(adaptor);
//=============================//
// try getting existing chunks //
//=============================//
HashMap<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos = new HashMap<>();
HashMap<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos = new HashMap<>();
IEmptyChunkGeneratorFunc emptyChunkGeneratorFunc = (int x, int z) ->
{ {
ChunkPos chunkPos = new ChunkPos(x, z); final int zOffsetFinal = zOffset;
DhChunkPos dhChunkPos = new DhChunkPos(x, z);
ChunkAccess newChunk = null;
try
{
// get the chunk
CompoundTag chunkData = this.getChunkNbtData(chunkPos);
newChunk = this.loadOrMakeChunk(chunkPos, chunkData);
if (Config.Client.Advanced.LodBuilding.pullLightingForPregeneratedChunks.get())
{
// attempt to get chunk lighting
ChunkLoader.CombinedChunkLightStorage combinedLights = ChunkLoader.readLight(newChunk, chunkData);
if (combinedLights != null)
{
chunkSkyLightingByDhPos.put(dhChunkPos, combinedLights.skyLightStorage);
chunkBlockLightingByDhPos.put(dhChunkPos, combinedLights.blockLightStorage);
}
}
}
catch (RuntimeException loadChunkError)
{
// Continue...
}
if (newChunk == null)
//================//
// variable setup //
//================//
int radius = refSize / 2;
int centerX = refPosX + radius + xOffset;
int centerZ = refPosZ + radius + zOffset;
// get/create the list of chunks we're going to generate
ArrayGridList<ChunkAccess> regionChunks = new ArrayGridList<>(
refSize,
(x, z) -> this.generateEmptyChunk(
x + refPosX + xOffsetFinal,
z + refPosZ + zOffsetFinal,
chunkSkyLightingByDhPos, chunkBlockLightingByDhPos, generatedChunkByDhPos));
ChunkAccess centerChunk = regionChunks.stream().filter(chunk -> chunk.getPos().x == centerX && chunk.getPos().z == centerZ).findFirst().get();
genEvent.refreshTimeout();
DhLitWorldGenRegion region = new DhLitWorldGenRegion(
centerX, centerZ,
centerChunk,
this.params.level, dummyLightEngine, regionChunks,
ChunkStatus.STRUCTURE_STARTS, radius,
// this method shouldn't be necessary since we're passing in a pre-populated
// list of chunks, but just in case
(x, z) -> this.generateEmptyChunk(x, z, chunkSkyLightingByDhPos, chunkBlockLightingByDhPos, generatedChunkByDhPos)
);
lightGetterAdaptor.setRegion(region);
genEvent.threadedParam.makeStructFeat(region, this.params);
//=========================//
// create chunk wrappers //
// and get existing chunks //
//=========================//
ArrayGridList<ChunkWrapper> chunkWrapperList = new ArrayGridList<>(regionChunks.gridSize);
regionChunks.forEachPos((relX, relZ) ->
{ {
newChunk = new ProtoChunk(chunkPos, UpgradeData.EMPTY // ArrayGridList's use relative positions and don't have a center position
#if MC_VER >= MC_1_17_1 , this.params.level #endif // so we need to use the offsetFinal to select the correct position
#if MC_VER >= MC_1_18_2 , this.params.biomes, null #endif DhChunkPos chunkPos = new DhChunkPos(relX + xOffsetFinal, relZ + zOffsetFinal);
); ChunkAccess chunk = regionChunks.get(relX, relZ);
}
return newChunk;
};
totalChunks = new ArrayGridList<>(refSize, (x, z) -> emptyChunkGeneratorFunc.generate(x + refPosX, z + refPosZ));
int radius = refSize / 2;
int centerX = refPosX + radius;
int centerZ = refPosZ + radius;
ChunkAccess centerChunk = totalChunks.stream().filter(chunk -> chunk.getPos().x == centerX && chunk.getPos().z == centerZ).findFirst().get();
genEvent.refreshTimeout();
region = new DhLitWorldGenRegion(
centerX, centerZ,
centerChunk,
this.params.level, dummyLightEngine, totalChunks,
ChunkStatus.STRUCTURE_STARTS, radius, emptyChunkGeneratorFunc);
adaptor.setRegion(region);
genEvent.threadedParam.makeStructFeat(region, this.params);
//=======================//
// create chunk wrappers //
//=======================//
chunkWrapperList = new ArrayGridList<>(totalChunks.gridSize);
totalChunks.forEachPos((x, z) ->
{
ChunkAccess chunk = totalChunks.get(x, z);
if (chunk != null)
{
// wrap the chunk
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, region, this.serverlevel.getLevelWrapper());
chunkWrapperList.set(x, z, chunkWrapper);
// try setting the wrapper's lighting if (chunkWrappersByDhPos.containsKey(chunkPos))
if (chunkBlockLightingByDhPos.containsKey(chunkWrapper.getChunkPos()))
{ {
chunkWrapper.setBlockLightStorage(chunkBlockLightingByDhPos.get(chunkWrapper.getChunkPos())); chunkWrapperList.set(relX, relZ, chunkWrappersByDhPos.get(chunkPos));
chunkWrapper.setSkyLightStorage(chunkSkyLightingByDhPos.get(chunkWrapper.getChunkPos()));
chunkWrapper.setUseDhLighting(true);
chunkWrapper.setIsDhLightCorrect(true);
} }
} else if (chunk != null)
}); {
// wrap the chunk
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, region, this.serverlevel.getLevelWrapper());
chunkWrapperList.set(relX, relZ, chunkWrapper);
//=================//
// generate chunks // // try setting the wrapper's lighting
//=================// if (chunkBlockLightingByDhPos.containsKey(chunkWrapper.getChunkPos()))
{
this.generateDirect(genEvent, chunkWrapperList, borderSize, genEvent.targetGenerationStep, region); chunkWrapper.setBlockLightStorage(chunkBlockLightingByDhPos.get(chunkWrapper.getChunkPos()));
genEvent.timer.nextEvent("cleanup"); chunkWrapper.setSkyLightStorage(chunkSkyLightingByDhPos.get(chunkWrapper.getChunkPos()));
} chunkWrapper.setUseDhLighting(true);
catch (StepStructureStart.StructStartCorruptedException f) chunkWrapper.setIsDhLightCorrect(true);
{ }
genEvent.threadedParam.markAsInvalid();
throw (RuntimeException) f.getCause(); chunkWrappersByDhPos.put(chunkPos, chunkWrapper);
}
else //if (chunk == null)
{
LodUtil.assertNotReach("Programmer Error: No chunk found in grid list, position offset is likely wrong.");
}
});
//=================//
// generate chunks //
//=================//
this.generateDirect(genEvent, chunkWrapperList, borderSize, genEvent.targetGenerationStep, region);
genEvent.timer.nextEvent("cleanup");
}
} }
ArrayGridList<ChunkWrapper> finalGenChunks = GetCutoutFrom(chunkWrapperList, borderSize);
for (int offsetY = 0; offsetY < finalGenChunks.gridSize; offsetY++)
//=========================//
// submit generated chunks //
//=========================//
for (DhChunkPos dhChunkPos : chunkWrappersByDhPos.keySet())
{ {
for (int offsetX = 0; offsetX < finalGenChunks.gridSize; offsetX++) ChunkWrapper wrappedChunk = chunkWrappersByDhPos.get(dhChunkPos);
ChunkAccess target = wrappedChunk.getChunk();
if (target instanceof LevelChunk)
{ {
ChunkWrapper wrappedChunk = finalGenChunks.get(offsetX, offsetY); #if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
ChunkAccess target = wrappedChunk.getChunk(); ((LevelChunk) target).setLoaded(true);
if (target instanceof LevelChunk) #else
{ ((LevelChunk) target).loaded = true;
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
((LevelChunk) target).setLoaded(true);
#else
((LevelChunk) target).loaded = true;
#endif
}
if (!wrappedChunk.isLightCorrect())
{
throw new RuntimeException("The generated chunk somehow has isLightCorrect() returning false");
}
boolean isFull = ChunkWrapper.getStatus(target) == ChunkStatus.FULL || target instanceof LevelChunk;
#if MC_VER >= MC_1_18_2
boolean isPartial = target.isOldNoiseGeneration();
#endif #endif
if (isFull) }
{
LOAD_LOGGER.info("Detected full existing chunk at {}", target.getPos()); if (!wrappedChunk.isLightCorrect())
genEvent.resultConsumer.accept(wrappedChunk); {
} throw new RuntimeException("The generated chunk somehow has isLightCorrect() returning false");
#if MC_VER >= MC_1_18_2 }
else if (isPartial)
{ boolean isFull = ChunkWrapper.getStatus(target) == ChunkStatus.FULL || target instanceof LevelChunk;
LOAD_LOGGER.info("Detected old existing chunk at {}", target.getPos()); #if MC_VER >= MC_1_18_2
genEvent.resultConsumer.accept(wrappedChunk); boolean isPartial = target.isOldNoiseGeneration();
} #endif
#endif if (isFull)
else if (ChunkWrapper.getStatus(target) == ChunkStatus.EMPTY) {
{ LOAD_LOGGER.debug("Detected full existing chunk at {}", target.getPos());
genEvent.resultConsumer.accept(wrappedChunk); genEvent.resultConsumer.accept(wrappedChunk);
} }
else #if MC_VER >= MC_1_18_2
{ else if (isPartial)
genEvent.resultConsumer.accept(wrappedChunk); {
} LOAD_LOGGER.debug("Detected old existing chunk at {}", target.getPos());
genEvent.resultConsumer.accept(wrappedChunk);
}
#endif
else if (ChunkWrapper.getStatus(target) == ChunkStatus.EMPTY)
{
genEvent.resultConsumer.accept(wrappedChunk);
}
else
{
genEvent.resultConsumer.accept(wrappedChunk);
} }
} }
@@ -556,9 +567,58 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (PREF_LOGGER.canMaybeLog()) if (PREF_LOGGER.canMaybeLog())
{ {
genEvent.threadedParam.perf.recordEvent(genEvent.timer); genEvent.threadedParam.perf.recordEvent(genEvent.timer);
PREF_LOGGER.infoInc("{}", genEvent.timer); PREF_LOGGER.debugInc("{}", genEvent.timer);
} }
} }
private ChunkAccess generateEmptyChunk(
int x, int z,
HashMap<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos,
HashMap<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos,
HashMap<DhChunkPos, ChunkAccess> generatedChunkByDhPos)
{
ChunkPos chunkPos = new ChunkPos(x, z);
DhChunkPos dhChunkPos = new DhChunkPos(x, z);
if (generatedChunkByDhPos.containsKey(dhChunkPos))
{
return generatedChunkByDhPos.get(dhChunkPos);
}
ChunkAccess newChunk = null;
try
{
// get the chunk
CompoundTag chunkData = this.getChunkNbtData(chunkPos);
newChunk = this.loadOrMakeChunk(chunkPos, chunkData);
if (Config.Client.Advanced.LodBuilding.pullLightingForPregeneratedChunks.get())
{
// attempt to get chunk lighting
ChunkLoader.CombinedChunkLightStorage combinedLights = ChunkLoader.readLight(newChunk, chunkData);
if (combinedLights != null)
{
chunkSkyLightingByDhPos.put(dhChunkPos, combinedLights.skyLightStorage);
chunkBlockLightingByDhPos.put(dhChunkPos, combinedLights.blockLightStorage);
}
}
}
catch (RuntimeException loadChunkError)
{
// Continue...
}
if (newChunk == null)
{
newChunk = new ProtoChunk(chunkPos, UpgradeData.EMPTY
#if MC_VER >= MC_1_17_1 , this.params.level #endif
#if MC_VER >= MC_1_18_2 , this.params.biomes, null #endif
);
}
generatedChunkByDhPos.put(dhChunkPos, newChunk);
return newChunk;
}
private CompoundTag getChunkNbtData(ChunkPos chunkPos) private CompoundTag getChunkNbtData(ChunkPos chunkPos)
{ {
ServerLevel level = this.params.level; ServerLevel level = this.params.level;
@@ -609,7 +669,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
{ {
try try
{ {
LOAD_LOGGER.info("DistantHorizons: Loading chunk [" + chunkPos + "] from disk."); LOAD_LOGGER.debug("DistantHorizons: Loading chunk [" + chunkPos + "] from disk.");
return ChunkLoader.read(level, chunkPos, chunkData); return ChunkLoader.read(level, chunkPos, chunkData);
} }
catch (Exception e) catch (Exception e)
@@ -639,6 +699,9 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
); );
} }
public void generateDirect( public void generateDirect(
GenerationEvent genEvent, ArrayGridList<ChunkWrapper> chunksToGenerate, int border, GenerationEvent genEvent, ArrayGridList<ChunkWrapper> chunksToGenerate, int border,
EDhApiWorldGenerationStep step, DhLitWorldGenRegion region) throws InterruptedException EDhApiWorldGenerationStep step, DhLitWorldGenRegion region) throws InterruptedException
@@ -653,8 +716,9 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
chunksToGenerate.forEach((chunkWrapper) -> chunksToGenerate.forEach((chunkWrapper) ->
{ {
ChunkAccess chunk = chunkWrapper.getChunk(); ChunkAccess chunk = chunkWrapper.getChunk();
if (chunk instanceof ProtoChunk protoChunk) if (chunk instanceof ProtoChunk)
{ {
ProtoChunk protoChunk = ((ProtoChunk) chunk);
protoChunk.setLightEngine(region.getLightEngine()); protoChunk.setLightEngine(region.getLightEngine());
} }
@@ -769,7 +833,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
} }
} }
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, int border) { return new ArrayGridList<>(total, border, total.gridSize - border); } private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, int border) { return new ArrayGridList<>(total, border, total.gridSize - border); }
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, MaxBorderNeeded - BorderNeeded.get(step)); } //private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, MaxBorderNeeded - WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP.get(step)); }
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, 0); }
@Override @Override
@@ -43,6 +43,7 @@ public final class GenerationEvent
public final int id; public final int id;
public final ThreadedParameters threadedParam; public final ThreadedParameters threadedParam;
public final DhChunkPos minPos; public final DhChunkPos minPos;
/** the number of chunks wide this event is */
public final int size; public final int size;
public final EDhApiWorldGenerationStep targetGenerationStep; public final EDhApiWorldGenerationStep targetGenerationStep;
public EventTimer timer = null; public EventTimer timer = null;
@@ -73,10 +74,10 @@ public final class GenerationEvent
EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer, EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer,
ExecutorService worldGeneratorThreadPool) ExecutorService worldGeneratorThreadPool)
{ {
if (size % 2 == 0) //if (size % 2 == 0)
{ //{
size += 1; // size must be odd for vanilla world gen regions to work // size += 1; // size must be odd for vanilla world gen regions to work
} //}
GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, target, resultConsumer); GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, target, resultConsumer);
@@ -93,9 +94,7 @@ public final class GenerationEvent
//LOGGER.info("generating [{}]", event.minPos); //LOGGER.info("generating [{}]", event.minPos);
genEnvironment.generateLodFromList(generationEvent); genEnvironment.generateLodFromList(generationEvent);
} }
catch (InterruptedException ignored) catch (InterruptedException ignored) { }
{
}
finally finally
{ {
BatchGenerationEnvironment.isDistantGeneratorThread.remove(); BatchGenerationEnvironment.isDistantGeneratorThread.remove();
@@ -126,21 +125,6 @@ public final class GenerationEvent
return this.future.isCancelled(); return this.future.isCancelled();
} }
public boolean tooClose(int minX, int minZ, int width)
{
int aMinX = this.minPos.x;
int aMinZ = this.minPos.z;
int aSize = this.size;
// Account for required empty chunks in the border
aSize += 1;
width += 1;
// Do a AABB to AABB intersection test
return (aMinX + aSize >= minX &&
aMinX <= minX + width &&
aMinZ + aSize >= minZ &&
aMinZ <= minZ + width);
}
public void refreshTimeout() public void refreshTimeout()
{ {
this.timeoutTime = System.nanoTime(); this.timeoutTime = System.nanoTime();
@@ -294,7 +294,7 @@ public class ChunkLoader
#else #else
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null)) .getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null))
#endif #endif
: new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES); : new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
biomeContainer = tagSection.contains("biomes", 10) biomeContainer = tagSection.contains("biomes", 10)
@@ -309,7 +309,7 @@ public class ChunkLoader
#else #else
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null)) .getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null))
#endif #endif
: new PalettedContainer<>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES); : new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#endif #endif
#if MC_VER < MC_1_20_1 #if MC_VER < MC_1_20_1
@@ -434,7 +434,7 @@ public class ChunkLoader
for (int sectionIndex = 0; sectionIndex < chunkSectionListTag.size(); sectionIndex++) for (int sectionIndex = 0; sectionIndex < chunkSectionListTag.size(); sectionIndex++)
{ {
Tag chunkSectionTag = chunkSectionListTag.get(sectionIndex); Tag chunkSectionTag = chunkSectionListTag.get(sectionIndex);
if (!(chunkSectionTag instanceof CompoundTag chunkSectionCompoundTag)) if (!(chunkSectionTag instanceof CompoundTag))
{ {
if (!lightingSectionErrorLogged) if (!lightingSectionErrorLogged)
{ {
@@ -443,9 +443,10 @@ public class ChunkLoader
} }
return null; return null;
} }
CompoundTag chunkSectionCompoundTag = (CompoundTag) chunkSectionTag;
// if null all lights = 0
// if null all lights = 0
byte[] blockLightNibbleArray = chunkSectionCompoundTag.getByteArray("BlockLight"); byte[] blockLightNibbleArray = chunkSectionCompoundTag.getByteArray("BlockLight");
byte[] skyLightNibbleArray = chunkSectionCompoundTag.getByteArray("SkyLight"); byte[] skyLightNibbleArray = chunkSectionCompoundTag.getByteArray("SkyLight");
@@ -84,7 +84,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
private final ChunkPos firstPos; private final ChunkPos firstPos;
private final List<ChunkAccess> cache; private final List<ChunkAccess> cache;
private final Long2ObjectOpenHashMap<ChunkAccess> chunkMap = new Long2ObjectOpenHashMap<>(); private final Long2ObjectOpenHashMap<ChunkAccess> chunkMap = new Long2ObjectOpenHashMap<ChunkAccess>();
/** /**
* Present to reduce the chance that we accidentally break underlying MC code that isn't thread safe, * Present to reduce the chance that we accidentally break underlying MC code that isn't thread safe,
@@ -142,7 +142,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
centerChunk); centerChunk);
#endif #endif
this.firstPos = chunkList.getFirst().getPos(); this.firstPos = chunkList.get(0).getPos();
this.generator = generator; this.generator = generator;
this.lightEngine = lightEngine; this.lightEngine = lightEngine;
this.writeRadius = writeRadius; this.writeRadius = writeRadius;
@@ -231,9 +231,11 @@ public class WorldGenStructFeatManager extends #if MC_VER < MC_1_19_2 StructureF
Map<Structure, LongSet> map = chunk.getAllReferences(); Map<Structure, LongSet> map = chunk.getAllReferences();
ImmutableList.Builder<StructureStart> builder = ImmutableList.builder(); ImmutableList.Builder<StructureStart> builder = ImmutableList.builder();
Iterator<Map.Entry<Structure, LongSet>> var5 = map.entrySet().iterator();
for (Map.Entry<Structure, LongSet> entry : map.entrySet()) while (var5.hasNext())
{ {
Map.Entry<Structure, LongSet> entry = var5.next();
Structure configuredStructureFeature = entry.getKey(); Structure configuredStructureFeature = entry.getKey();
if (predicate.test(configuredStructureFeature)) if (predicate.test(configuredStructureFeature))
{ {
@@ -54,7 +54,7 @@ public final class StepStructureReference
List<ChunkWrapper> chunkWrappers) List<ChunkWrapper> chunkWrappers)
{ {
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>(); ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
for (ChunkWrapper chunkWrapper : chunkWrappers) for (ChunkWrapper chunkWrapper : chunkWrappers)
{ {
@@ -0,0 +1,14 @@
{
"required": true,
"minVersion": "0.8",
"package": "com.seibel.distanthorizons.common.mixins",
"mixins": [
],
"client": [
"client.MixinDebugScreenOverlay",
"client.MixinFogRenderer",
"client.MixinGameRenderer",
"client.MixinLightTexture"
],
"server": []
}
+2 -2
View File
@@ -23,8 +23,8 @@ loom {
} }
remapJar { remapJar {
inputFile = shadeDowngradedApi.archiveFile inputFile = shadowJar.archiveFile
dependsOn shadeDowngradedApi dependsOn shadowJar
} }
@@ -106,7 +106,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// tick events // // tick events //
//=============// //=============//
ClientTickEvents.START_CLIENT_TICK.register((client) -> ClientApi.INSTANCE.clientTickEvent()); ClientTickEvents.START_CLIENT_TICK.register((client) -> { ClientApi.INSTANCE.clientTickEvent(); });
@@ -124,7 +124,7 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
#if MC_VER >= MC_1_20_1 #if MC_VER >= MC_1_20_1
if (SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("sodium")) if (SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("sodium"))
ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class).setFogOcclusion(false); // FIXME: This is a tmp fix for sodium 0.5.0, and 0.5.1. This is fixed in sodium 0.5.2 ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class).setFogOcclusion(false);
#endif #endif
if (ConfigBase.INSTANCE == null) if (ConfigBase.INSTANCE == null)
@@ -1,5 +1,7 @@
package com.seibel.distanthorizons.fabric; package com.seibel.distanthorizons.fabric;
import com.seibel.distanthorizons.api.methods.events.DhApiEventRegister;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
import com.seibel.distanthorizons.common.AbstractModInitializer; import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper; import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
@@ -10,6 +12,7 @@ import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.fabric.testing.TestWorldGenBindingEvent;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents; 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.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
@@ -75,6 +78,14 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
// ServerTickEvent // ServerTickEvent
ServerTickEvents.END_SERVER_TICK.register((server) -> SERVER_API.serverTickEvent()); ServerTickEvents.END_SERVER_TICK.register((server) -> SERVER_API.serverTickEvent());
// can be enabled to test world gen overrides without having to build a separate API project
if (false)
{
DhApiEventRegister.on(DhApiLevelLoadEvent.class, new TestWorldGenBindingEvent());
}
// ServerWorldLoadEvent // ServerWorldLoadEvent
//TODO: Check if both of these use the correct timed events. (i.e. is it 'ed' or 'ing' one?) //TODO: Check if both of these use the correct timed events. (i.e. is it 'ed' or 'ing' one?)
ServerLifecycleEvents.SERVER_STARTING.register((server) -> ServerLifecycleEvents.SERVER_STARTING.register((server) ->
@@ -98,7 +98,7 @@ public class MixinMinecraft
} }
else else
{ {
versionId = GitlabGetter.INSTANCE.projectPipelines.getFirst().get("sha"); versionId = GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha");
} }
Minecraft.getInstance().setScreen(new UpdateModScreen( Minecraft.getInstance().setScreen(new UpdateModScreen(
@@ -104,14 +104,14 @@ public class MixinOptionsScreen extends Screen
// add the button to the correct location in the UI // add the button to the correct location in the UI
// TODO is there a better way to do this instead of using access transformers to inject into the exact UI elements? // TODO is there a better way to do this instead of using access transformers to inject into the exact UI elements?
LinearLayout layout = (LinearLayout) this.layout.headerFrame.children.getFirst().child; LinearLayout layout = (LinearLayout) this.layout.headerFrame.children.get(0).child;
// determine how wide the other option buttons are so we can put our botton to the left of them all // determine how wide the other option buttons are so we can put our botton to the left of them all
AtomicInteger width = new AtomicInteger(0); AtomicInteger width = new AtomicInteger(0);
layout.visitChildren(x -> width.addAndGet(x.getWidth())); layout.visitChildren(x -> { width.addAndGet(x.getWidth()); });
width.addAndGet(-10); // padding between the DH button and the FOV slider width.addAndGet(-10); // padding between the DH button and the FOV slider
layout.wrapped.addChild(this.getOptionsButton(), 1, 2, (settings) -> settings.paddingLeft(width.get() * -1)); layout.wrapped.addChild(this.getOptionsButton(), 1, 2, (settings) -> { settings.paddingLeft(width.get() * -1); });
layout.arrangeElements(); layout.arrangeElements();
#endif #endif
@@ -0,0 +1,28 @@
package com.seibel.distanthorizons.fabric.testing;
import com.mojang.logging.LogUtils;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.IDhApiWorldGenerator;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
import com.seibel.distanthorizons.fabric.FabricServerProxy;
import net.minecraft.server.level.ServerLevel;
public class TestWorldGenBindingEvent extends DhApiLevelLoadEvent
{
private static final org.slf4j.Logger LOGGER = LogUtils.getLogger();
@Override
public void onLevelLoad(DhApiEventParam<DhApiLevelLoadEvent.EventParam> event)
{
LOGGER.info("DH Level: ["+event.value.levelWrapper.getDimensionType()+"] loaded.");
// Note: whenever you use a wrapper method on a new Minecraft version it is recommended that you
// call wrapper.getClass() to determine which object the API will return before you try casting it.
ServerLevel level = (ServerLevel) event.value.levelWrapper.getWrappedMcObject();
// override the core DH world generator for this level
IDhApiWorldGenerator exampleWorldGen = new TestWorldGenerator(level);
DhApi.worldGenOverrides.registerWorldGeneratorOverride(event.value.levelWrapper, exampleWorldGen);
}
}
@@ -0,0 +1,82 @@
package com.seibel.distanthorizons.fabric.testing;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGeneratorReturnType;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBiomeWrapper;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
import com.seibel.distanthorizons.api.interfaces.override.worldGenerator.AbstractDhApiChunkWorldGenerator;
import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.distanthorizons.api.objects.data.DhApiChunk;
import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import java.util.ArrayList;
public class TestWorldGenerator extends AbstractDhApiChunkWorldGenerator
{
private final ServerLevel level;
private final IDhApiLevelWrapper levelWrapper;
public TestWorldGenerator(ServerLevel level)
{
this.level = level;
this.levelWrapper = ServerLevelWrapper.getWrapper(level);
}
@Override
public EDhApiWorldGeneratorReturnType getReturnType() { return EDhApiWorldGeneratorReturnType.API_CHUNKS; }
@Override
public boolean isBusy() { return false; }
@Override
public Object[] generateChunk(int chunkX, int chunkZ, EDhApiDistantGeneratorMode eDhApiDistantGeneratorMode)
{
ChunkAccess chunk = this.level.getChunk(chunkX, chunkZ);
return new Object[] { chunk, this.level };
}
@Override
public DhApiChunk generateApiChunk(int chunkPosX, int chunkPosZ, EDhApiDistantGeneratorMode generatorMode)
{
ChunkAccess chunk = this.level.getChunk(chunkPosX, chunkPosZ);
int minBuildHeight = chunk.getMinBuildHeight();
int maxBuildHeight = chunk.getMaxBuildHeight();
DhApiChunk apiChunk = DhApiChunk.create(chunkPosX, chunkPosZ, minBuildHeight, maxBuildHeight);
for (int x = 0; x < 16; x++)
{
for (int z = 0; z < 16; z++)
{
ArrayList<DhApiTerrainDataPoint> dataPoints = new ArrayList<>();
IDhApiBlockStateWrapper block = null;
IDhApiBiomeWrapper biome = null;
for (int y = minBuildHeight; y < maxBuildHeight; y++)
{
block = DhApi.Delayed.wrapperFactory.getBlockStateWrapper(new Object[]{chunk.getBlockState(new BlockPos(x, y, z))}, this.levelWrapper);
biome = DhApi.Delayed.wrapperFactory.getBiomeWrapper(new Object[]{chunk.getNoiseBiome(x, y, z)}, this.levelWrapper);
dataPoints.add(DhApiTerrainDataPoint.create((byte) 0, 0, 15, y, y + 1, block, biome));
}
apiChunk.setDataPoints(x, z, dataPoints);
}
}
return apiChunk;
}
@Override
public void preGeneratorTaskStart() { /* do nothing */ }
@Override
public void close() { /* do nothing */ }
}
@@ -32,7 +32,7 @@ public class ModMenuIntegration implements ModMenuApi
@Override @Override
public ConfigScreenFactory<?> getModConfigScreenFactory() public ConfigScreenFactory<?> getModConfigScreenFactory()
{ {
return GetConfigScreen::getScreen; return parent -> GetConfigScreen.getScreen(parent);
} }
} }
@@ -37,7 +37,7 @@ public class ModChecker implements IModChecker
@Override @Override
public File modLocation(String modid) public File modLocation(String modid)
{ {
return new File(FabricLoader.getInstance().getModContainer(modid).get().getOrigin().getPaths().getFirst().toUri()); return new File(FabricLoader.getInstance().getModContainer(modid).get().getOrigin().getPaths().get(0).toUri());
} }
} }
@@ -19,20 +19,18 @@
package com.seibel.distanthorizons.fabric.wrappers.modAccessor; package com.seibel.distanthorizons.fabric.wrappers.modAccessor;
import java.util.HashSet; import java.lang.invoke.MethodHandle;
import java.util.stream.Collectors; import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; #if MC_VER < MC_1_20_1
import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer; import me.jellysquid.mods.sodium.client.render.SodiumWorldRenderer;
import net.minecraft.client.Minecraft; #endif
#if MC_VER < MC_1_17_1 #if MC_VER < MC_1_17_1
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.Packet;
@@ -40,101 +38,100 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
#else #else
import net.minecraft.world.level.LevelHeightAccessor;
#endif #endif
public class SodiumAccessor implements ISodiumAccessor public class SodiumAccessor implements ISodiumAccessor
{ {
private final IWrapperFactory factory = SingletonInjector.INSTANCE.get(IWrapperFactory.class); #if MC_VER >= MC_1_20_1
private final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); private static MethodHandle setFogOcclusionMethod;
private static Object sodiumPerformanceOptions;
public IClientLevelWrapper levelWrapper;
public Mat4f mcModelViewMatrix;
public Mat4f mcProjectionMatrix;
public float partialTicks;
@Override
public String getModName()
{
return "Sodium-Fabric";
}
#if MC_VER >= MC_1_17_1
@Override
public HashSet<DhChunkPos> getNormalRenderedChunks()
{
SodiumWorldRenderer renderer = SodiumWorldRenderer.instance();
LevelHeightAccessor height = Minecraft.getInstance().level;
#if MC_VER >= MC_1_20_1
// TODO: This is just a tmp solution, use a proper solution later
return MC_RENDER.getMaximumRenderedChunks().stream().filter((DhChunkPos chunk) -> (renderer.isBoxVisible(
chunk.getMinBlockX() + 1, height.getMinBuildHeight() + 1, chunk.getMinBlockZ() + 1,
chunk.getMinBlockX() + 15, height.getMaxBuildHeight() - 1, chunk.getMinBlockZ() + 15))).collect(Collectors.toCollection(HashSet::new));
#elif MC_VER >= MC_1_18_2
// 0b11 = Lighted chunk & loaded chunk
return renderer.getChunkTracker().getChunks(0b00).filter(
(long l) -> {
return true;
}).mapToObj(DhChunkPos::new).collect(Collectors.toCollection(HashSet::new));
#else
// TODO: Maybe use a mixin to make this more efficient, and maybe ignore changes behind the camera
return MC_RENDER.getMaximumRenderedChunks().stream().filter((DhChunkPos chunk) -> {
return (renderer.isBoxVisible(
chunk.getMinBlockX() + 1, height.getMinBuildHeight() + 1, chunk.getMinBlockZ() + 1,
chunk.getMinBlockX() + 15, height.getMaxBuildHeight() - 1, chunk.getMinBlockZ() + 15));
}).collect(Collectors.toCollection(HashSet::new));
#endif
}
#else
@Override
public HashSet<DhChunkPos> getNormalRenderedChunks() {
SodiumWorldRenderer renderer = SodiumWorldRenderer.getInstance();
LevelAccessor height = Minecraft.getInstance().level;
// TODO: Maybe use a mixin to make this more efficient
return MC_RENDER.getMaximumRenderedChunks().stream().filter((DhChunkPos chunk) -> {
FakeChunkEntity AABB = new FakeChunkEntity(chunk.x, chunk.z, height.getMaxBuildHeight());
return (renderer.isEntityVisible(AABB));
}).collect(Collectors.toCollection(HashSet::new));
}
private static class FakeChunkEntity extends Entity {
public int cx;
public int cz;
public int my;
public FakeChunkEntity(int chunkX, int chunkZ, int maxHeight) {
super(EntityType.AREA_EFFECT_CLOUD, null);
cx = chunkX;
cz = chunkZ;
my = maxHeight;
}
@Override
public AABB getBoundingBoxForCulling() {
return new AABB(cx*16+1, 1, cz*16+1,
cx*16+15, my-1, cz*16+15);
}
@Override
protected void defineSynchedData() {}
@Override
protected void readAdditionalSaveData(CompoundTag paramCompoundTag) {}
@Override
protected void addAdditionalSaveData(CompoundTag paramCompoundTag) {}
@Override
public Packet<?> getAddEntityPacket() {
throw new UnsupportedOperationException("This is a FAKE CHUNK ENTITY... For tricking the Sodium to check a AABB.");
}
}
#endif #endif
/** A temporary overwrite for a config in sodium 0.5 to fix their terrain from showing, will be removed once a proper fix is added */
// FIXME
//======================//
// mod accessor methods //
//======================//
@Override @Override
public void setFogOcclusion(boolean b) public String getModName() { return "Sodium-Fabric"; }
//================//
// sodium methods //
//================//
/** An overwrite for a config in sodium 0.5 to fix their terrain from showing */
@Override
public void setFogOcclusion(boolean occlusionEnabled)
{ {
#if MC_VER >= MC_1_20_1 #if MC_VER >= MC_1_20_1
me.jellysquid.mods.sodium.client.SodiumClientMod.options().performance.useFogOcclusion = b; try
{
if (sodiumPerformanceOptions == null)
{
boolean sodiumV6 = classPresent("net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer");
if (!sodiumV6)
{
// sodium 0.5
Class<?> optionsClass = Class.forName("me.jellysquid.mods.sodium.client.gui.SodiumGameOptions");
Object basicOptions = MethodHandles.lookup().findStatic(
Class.forName("me.jellysquid.mods.sodium.client.SodiumClientMod"),
"options", MethodType.methodType(optionsClass)).invoke();
sodiumPerformanceOptions = optionsClass.getDeclaredField("performance").get(basicOptions);
setFogOcclusionMethod = MethodHandles.lookup()
.findSetter(Class.forName(
"me.jellysquid.mods.sodium.client.gui.SodiumGameOptions$PerformanceSettings"),
"useFogOcclusion", boolean.class);
// alternate option if referencing Sodium 0.5 directly
//me.jellysquid.mods.sodium.client.SodiumClientMod.options().performance.useFogOcclusion = b;
}
else
{
// sodium 0.6
Class<?> optionsClass = Class.forName("net.caffeinemc.mods.sodium.client.gui.SodiumGameOptions");
Object basicOptions = MethodHandles.lookup().findStatic(
Class.forName("net.caffeinemc.mods.sodium.client.SodiumClientMod"),
"options", MethodType.methodType(optionsClass)).invoke();
sodiumPerformanceOptions = optionsClass.getDeclaredField("performance").get(basicOptions);
setFogOcclusionMethod = MethodHandles.lookup()
.findSetter(Class.forName(
"net.caffeinemc.mods.sodium.client.gui.SodiumGameOptions$PerformanceSettings"),
"useFogOcclusion", boolean.class);
}
}
setFogOcclusionMethod.invoke(sodiumPerformanceOptions, occlusionEnabled);
}
catch (Throwable e)
{
throw new RuntimeException(e);
}
#endif #endif
} }
//================//
// helper methods //
//================//
private static boolean classPresent(String className)
{
try
{
Class.forName(className);
return true;
}
catch (ClassNotFoundException e)
{
return false;
}
}
} }
@@ -10,11 +10,7 @@
"client": [ "client": [
"client.MixinClientLevel", "client.MixinClientLevel",
"client.MixinClientPacketListener", "client.MixinClientPacketListener",
"client.MixinDebugScreenOverlay",
"client.MixinFogRenderer",
"client.MixinGameRenderer",
"client.MixinLevelRenderer", "client.MixinLevelRenderer",
"client.MixinLightTexture",
"client.MixinOptionsScreen", "client.MixinOptionsScreen",
"client.MixinMinecraft", "client.MixinMinecraft",
"client.MixinTextureUtil" "client.MixinTextureUtil"
@@ -35,6 +35,7 @@
}, },
"mixins": [ "mixins": [
"DistantHorizons.common.mixins.json",
"DistantHorizons.fabric.mixins.json" "DistantHorizons.fabric.mixins.json"
], ],
+3 -2
View File
@@ -28,6 +28,7 @@ loom {
extraAccessWideners.add loom.accessWidenerPath.get().asFile.name extraAccessWideners.add loom.accessWidenerPath.get().asFile.name
mixinConfigs = [ mixinConfigs = [
"DistantHorizons.common.mixins.json",
"DistantHorizons.forge.mixins.json" "DistantHorizons.forge.mixins.json"
] ]
} }
@@ -51,8 +52,8 @@ loom {
} }
remapJar { remapJar {
inputFile = shadeDowngradedApi.archiveFile inputFile = shadowJar.archiveFile
dependsOn shadeDowngradedApi dependsOn shadowJar
} }
def addMod(path, enabled) { def addMod(path, enabled) {
@@ -1,23 +0,0 @@
package com.seibel.distanthorizons.forge.mixins.client;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import net.minecraft.client.gui.components.DebugScreenOverlay;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.List;
@Mixin(DebugScreenOverlay.class)
public class MixinDebugScreenOverlay
{
@Inject(method = "getSystemInformation", at = @At("RETURN"))
private void addCustomF3(CallbackInfoReturnable<List<String>> cir)
{
List<String> messages = cir.getReturnValue();
F3Screen.addStringToDisplay(messages);
}
}
@@ -1,84 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.forge.mixins.client;
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;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
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
{
// Using this instead of Float.MAX_VALUE because Sodium don't like it.
private static final float A_REALLY_REALLY_BIG_VALUE = 420694206942069.F;
private static final float A_EVEN_LARGER_VALUE = 42069420694206942069.F;
@Inject(at = @At("RETURN"),
method = "setupFog(Lnet/minecraft/client/Camera;Lnet/minecraft/client/renderer/FogRenderer$FogMode;FZF)V",
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();
#else
FogType fogTypes = camera.getFluidInCamera();
boolean cameraNotInFluid = fogTypes == FogType.NONE;
#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.disableVanillaFog.get())
{
#if MC_VER < MC_1_17_1
RenderSystem.fogStart(A_REALLY_REALLY_BIG_VALUE);
RenderSystem.fogEnd(A_EVEN_LARGER_VALUE);
#else
RenderSystem.setShaderFogStart(A_REALLY_REALLY_BIG_VALUE);
RenderSystem.setShaderFogEnd(A_EVEN_LARGER_VALUE);
#endif
}
}
}
@@ -1,58 +0,0 @@
package com.seibel.distanthorizons.forge.mixins.client;
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import net.minecraft.client.renderer.GameRenderer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
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;
// TODO: Check if this port from fabric works
@Mixin(GameRenderer.class)
public class MixinGameRenderer
{
private static final Logger LOGGER = LogManager.getLogger(MixinGameRenderer.class.getSimpleName());
#if MC_VER >= MC_1_17_1
// FIXME: This I think will dup multiple renderStartupEvent calls...
@Inject(method = {"reloadShaders", "preloadUiShader"}, at = @At("TAIL"))
public void onStartupShaders(CallbackInfo ci)
{
LOGGER.info("Starting up renderer (forge)");
if (!DependencySetupDoneCheck.isDone)
{
LOGGER.warn("Dependency setup is not done yet, skipping renderer this startup event!");
return;
}
ClientApi.INSTANCE.rendererStartupEvent();
}
@Inject(method = "shutdownShaders", at = @At("HEAD"))
public void onShutdownShaders(CallbackInfo ci)
{
LOGGER.info("Shutting down renderer (forge)");
if (!DependencySetupDoneCheck.isDone)
{
LOGGER.warn("Dependency setup is not done yet, skipping renderer this shutdown event!");
return;
}
ClientApi.INSTANCE.rendererShutdownEvent();
}
#else
@Inject(method = {"loadEffect"}, at = @At("TAIL"))
public void onStartupShaders(CallbackInfo ci) {
ClientApi.INSTANCE.rendererStartupEvent();
}
@Inject(method = "shutdownEffect", at = @At("HEAD"))
public void onShutdownShaders(CallbackInfo ci) {
ClientApi.INSTANCE.rendererShutdownEvent();
}
#endif
}
@@ -1,59 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.forge.mixins.client;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
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;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LightTexture.class)
public class MixinLightTexture
{
@Shadow //# if MC_VER >= MC_1_20_4 (remap = false) # endif
@Final
private NativeImage lightPixels;
@Inject(method = "updateLightTexture(F)V", at = @At("RETURN"))
public void updateLightTexture(float partialTicks, CallbackInfo ci)
{
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
if (mc == null || mc.getWrappedClientLevel() == null)
{
return;
}
IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
MinecraftRenderWrapper.INSTANCE.updateLightmap(this.lightPixels, clientLevel);
}
}
@@ -9,11 +9,7 @@
], ],
"client": [ "client": [
"client.MixinClientPacketListener", "client.MixinClientPacketListener",
"client.MixinDebugScreenOverlay",
"client.MixinFogRenderer",
"client.MixinGameRenderer",
"client.MixinLevelRenderer", "client.MixinLevelRenderer",
"client.MixinLightTexture",
"client.MixinOptionsScreen", "client.MixinOptionsScreen",
"client.MixinTextureUtil" "client.MixinTextureUtil"
], ],
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.7-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
+15 -11
View File
@@ -23,7 +23,7 @@ loom {
mixin { mixin {
// Mixins are defined in the `mods.toml` // Mixins are defined in the `mods.toml`
} }
// "runs" isn't required, but when we do need it then it can be useful // "runs" isn't required, but when we do need it then it can be useful
runs { runs {
client { client {
@@ -31,7 +31,7 @@ loom {
setConfigName("NeoForge Client") setConfigName("NeoForge Client")
ideConfigGenerated(true) ideConfigGenerated(true)
runDir("../run") runDir("../run")
// vmArgs("-XX:-OmitStackTraceInFastThrow", minecraftMemoryJavaArg) //vmArgs("-XX:-OmitStackTraceInFastThrow", minecraftMemoryJavaArg)
} }
server { server {
server() server()
@@ -42,13 +42,7 @@ loom {
} }
} }
remapJar {
inputFile = apiDowngrade.archiveFile
dependsOn apiDowngrade
// classifier null
atAccessWideners.add("distanthorizons.accesswidener")
}
def addMod(path, enabled) { def addMod(path, enabled) {
if (enabled == "2") if (enabled == "2")
@@ -56,21 +50,24 @@ def addMod(path, enabled) {
else if (enabled == "1") else if (enabled == "1")
dependencies { modCompileOnly(path) } dependencies { modCompileOnly(path) }
} }
dependencies { dependencies {
minecraft "com.mojang:minecraft:${rootProject.minecraft_version}" minecraft "com.mojang:minecraft:${rootProject.minecraft_version}"
mappings loom.layered() { mappings loom.layered()
{
// Mojmap mappings // Mojmap mappings
officialMojangMappings() officialMojangMappings()
// Parchment mappings (it adds parameter mappings & javadoc) // Parchment mappings (it adds parameter mappings & javadoc)
parchment("org.parchmentmc.data:parchment-${rootProject.parchment_version}@zip") parchment("org.parchmentmc.data:parchment-${rootProject.parchment_version}@zip")
} }
// Neoforge // Neoforge
neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}" neoForge "net.neoforged:neoforge:${rootProject.neoforge_version}"
addMod("curse.maven:TerraFirmaCraft-302973:4616004", rootProject.enable_terrafirmacraft) addMod("curse.maven:TerraFirmaCraft-302973:4616004", rootProject.enable_terrafirmacraft)
} }
task deleteResources(type: Delete) { task deleteResources(type: Delete) {
delete file("build/resources/main") delete file("build/resources/main")
} }
@@ -89,6 +86,13 @@ tasks.named('runClient') {
finalizedBy(deleteResources) finalizedBy(deleteResources)
} }
remapJar {
inputFile = shadowJar.archiveFile
dependsOn shadowJar
atAccessWideners.add("distanthorizons.accesswidener")
}
sourcesJar { sourcesJar {
def commonSources = project(":common").sourcesJar def commonSources = project(":common").sourcesJar
dependsOn commonSources dependsOn commonSources
@@ -129,11 +129,12 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
LOGGER.info("level load"); LOGGER.info("level load");
LevelAccessor level = event.getLevel(); LevelAccessor level = event.getLevel();
if (!(level instanceof ClientLevel clientLevel)) if (!(level instanceof ClientLevel))
{ {
return; return;
} }
ClientLevel clientLevel = (ClientLevel) level;
IClientLevelWrapper clientLevelWrapper = ClientLevelWrapper.getWrapper(clientLevel); IClientLevelWrapper clientLevelWrapper = ClientLevelWrapper.getWrapper(clientLevel);
// TODO this causes a crash due to level being set to null somewhere // TODO this causes a crash due to level being set to null somewhere
ClientApi.INSTANCE.clientLevelLoadEvent(clientLevelWrapper); ClientApi.INSTANCE.clientLevelLoadEvent(clientLevelWrapper);
@@ -144,11 +145,12 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
LOGGER.info("level unload"); LOGGER.info("level unload");
LevelAccessor level = event.getLevel(); LevelAccessor level = event.getLevel();
if (!(level instanceof ClientLevel clientLevel)) if (!(level instanceof ClientLevel))
{ {
return; return;
} }
ClientLevel clientLevel = (ClientLevel) level;
IClientLevelWrapper clientLevelWrapper = ClientLevelWrapper.getWrapper(clientLevel); IClientLevelWrapper clientLevelWrapper = ClientLevelWrapper.getWrapper(clientLevel);
ClientApi.INSTANCE.clientLevelUnloadEvent(clientLevelWrapper); ClientApi.INSTANCE.clientLevelUnloadEvent(clientLevelWrapper);
} }
@@ -88,7 +88,7 @@ public class NeoforgeMain extends AbstractModInitializer
@Override @Override
protected void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler) protected void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler)
{ {
NeoForge.EVENT_BUS.addListener((RegisterCommandsEvent e) -> eventHandler.accept(e.getDispatcher())); NeoForge.EVENT_BUS.addListener((RegisterCommandsEvent e) -> { eventHandler.accept(e.getDispatcher()); });
} }
@Override @Override
@@ -100,7 +100,7 @@ public class NeoforgeMain extends AbstractModInitializer
@Override @Override
protected void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler) protected void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler)
{ {
NeoForge.EVENT_BUS.addListener((ServerStartingEvent e) -> eventHandler.accept(e.getServer())); NeoForge.EVENT_BUS.addListener((ServerStartingEvent e) -> { eventHandler.accept(e.getServer()); });
} }
@Override @Override
@@ -1,23 +0,0 @@
package com.seibel.distanthorizons.neoforge.mixins.client;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import net.minecraft.client.gui.components.DebugScreenOverlay;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.List;
@Mixin(DebugScreenOverlay.class)
public class MixinDebugScreenOverlay
{
@Inject(method = "getSystemInformation", at = @At("RETURN"))
private void addCustomF3(CallbackInfoReturnable<List<String>> cir)
{
List<String> messages = cir.getReturnValue();
F3Screen.addStringToDisplay(messages);
}
}
@@ -1,84 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforge.mixins.client;
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;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
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
{
// Using this instead of Float.MAX_VALUE because Sodium don't like it.
private static final float A_REALLY_REALLY_BIG_VALUE = 420694206942069.F;
private static final float A_EVEN_LARGER_VALUE = 42069420694206942069.F;
@Inject(at = @At("RETURN"),
method = "setupFog(Lnet/minecraft/client/Camera;Lnet/minecraft/client/renderer/FogRenderer$FogMode;FZF)V",
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();
#else
FogType fogTypes = camera.getFluidInCamera();
boolean cameraNotInFluid = fogTypes == FogType.NONE;
#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.disableVanillaFog.get())
{
#if MC_VER < MC_1_17_1
RenderSystem.fogStart(A_REALLY_REALLY_BIG_VALUE);
RenderSystem.fogEnd(A_EVEN_LARGER_VALUE);
#else
RenderSystem.setShaderFogStart(A_REALLY_REALLY_BIG_VALUE);
RenderSystem.setShaderFogEnd(A_EVEN_LARGER_VALUE);
#endif
}
}
}
@@ -1,58 +0,0 @@
package com.seibel.distanthorizons.neoforge.mixins.client;
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import net.minecraft.client.renderer.GameRenderer;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
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;
// TODO: Check if this port from fabric works
@Mixin(GameRenderer.class)
public class MixinGameRenderer
{
private static final Logger LOGGER = LogManager.getLogger(MixinGameRenderer.class.getSimpleName());
#if MC_VER >= MC_1_17_1
// FIXME: This I think will dup multiple renderStartupEvent calls...
@Inject(method = {"reloadShaders", "preloadUiShader"}, at = @At("TAIL"))
public void onStartupShaders(CallbackInfo ci)
{
LOGGER.info("Starting up renderer (forge)");
if (!DependencySetupDoneCheck.isDone)
{
LOGGER.warn("Dependency setup is not done yet, skipping renderer this startup event!");
return;
}
ClientApi.INSTANCE.rendererStartupEvent();
}
@Inject(method = "shutdownShaders", at = @At("HEAD"))
public void onShutdownShaders(CallbackInfo ci)
{
LOGGER.info("Shutting down renderer (forge)");
if (!DependencySetupDoneCheck.isDone)
{
LOGGER.warn("Dependency setup is not done yet, skipping renderer this shutdown event!");
return;
}
ClientApi.INSTANCE.rendererShutdownEvent();
}
#else
@Inject(method = {"loadEffect"}, at = @At("TAIL"))
public void onStartupShaders(CallbackInfo ci) {
ClientApi.INSTANCE.rendererStartupEvent();
}
@Inject(method = "shutdownEffect", at = @At("HEAD"))
public void onShutdownShaders(CallbackInfo ci) {
ClientApi.INSTANCE.rendererShutdownEvent();
}
#endif
}
@@ -1,59 +0,0 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020-2023 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 <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.neoforge.mixins.client;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
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;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LightTexture.class)
public class MixinLightTexture
{
@Shadow
@Final
private NativeImage lightPixels;
@Inject(method = "updateLightTexture(F)V", at = @At("RETURN"))
public void updateLightTexture(float partialTicks, CallbackInfo ci)
{
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
if (mc == null || mc.getWrappedClientLevel() == null)
{
return;
}
IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
MinecraftRenderWrapper.INSTANCE.updateLightmap(this.lightPixels, clientLevel);
}
}
@@ -86,7 +86,7 @@ public class MixinMinecraft
} }
else else
{ {
versionId = GitlabGetter.INSTANCE.projectPipelines.getFirst().get("sha"); versionId = GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha");
} }
Minecraft.getInstance().setScreen(new UpdateModScreen( Minecraft.getInstance().setScreen(new UpdateModScreen(
@@ -104,14 +104,14 @@ public class MixinOptionsScreen extends Screen
// add the button to the correct location in the UI // add the button to the correct location in the UI
// TODO is there a better way to do this instead of using access transformers to inject into the exact UI elements? // TODO is there a better way to do this instead of using access transformers to inject into the exact UI elements?
LinearLayout layout = (LinearLayout) this.layout.headerFrame.children.getFirst().child; LinearLayout layout = (LinearLayout) this.layout.headerFrame.children.get(0).child;
// determine how wide the other option buttons are so we can put our botton to the left of them all // determine how wide the other option buttons are so we can put our botton to the left of them all
AtomicInteger width = new AtomicInteger(0); AtomicInteger width = new AtomicInteger(0);
layout.visitChildren(x -> width.addAndGet(x.getWidth())); layout.visitChildren(x -> { width.addAndGet(x.getWidth()); });
width.addAndGet(-10); // padding between the DH button and the FOV slider width.addAndGet(-10); // padding between the DH button and the FOV slider
layout.wrapped.addChild(this.getOptionsButton(), 1, 2, (settings) -> settings.paddingLeft(width.get() * -1)); layout.wrapped.addChild(this.getOptionsButton(), 1, 2, (settings) -> { settings.paddingLeft(width.get() * -1); });
layout.arrangeElements(); layout.arrangeElements();
#endif #endif
@@ -9,11 +9,7 @@
], ],
"client": [ "client": [
"client.MixinClientPacketListener", "client.MixinClientPacketListener",
"client.MixinDebugScreenOverlay",
"client.MixinFogRenderer",
"client.MixinGameRenderer",
"client.MixinLevelRenderer", "client.MixinLevelRenderer",
"client.MixinLightTexture",
"client.MixinOptionsScreen", "client.MixinOptionsScreen",
"client.MixinTextureUtil" "client.MixinTextureUtil"
], ],
@@ -24,6 +24,8 @@ issueTrackerURL = "${issues}"
acceptableRemoteVersions = "*" acceptableRemoteVersions = "*"
# We may need this to make forge (lexforge) & neoforge work together # We may need this to make forge (lexforge) & neoforge work together
[[mixins]]
config = "DistantHorizons.common.mixins.json"
[[mixins]] [[mixins]]
config = "DistantHorizons.neoforge.mixins.json" config = "DistantHorizons.neoforge.mixins.json"
-6
View File
@@ -33,12 +33,6 @@ pluginManagement {
name "ParchmentMC" name "ParchmentMC"
url "https://maven.parchmentmc.org" url "https://maven.parchmentmc.org"
} }
maven { // Used for downgrading Java versions
url "https://maven.wagyourtail.xyz/releases"
}
maven {
url "https://maven.wagyourtail.xyz/snapshots"
}
mavenCentral() mavenCentral()
gradlePluginPortal() gradlePluginPortal()
+1 -1
View File
@@ -20,7 +20,7 @@ fabric_api_version=0.90.4+1.20.1
immersive_portals_version= immersive_portals_version=
canvas_version= canvas_version=
fabric_incompatibility_list={ "iris": "<=1.6.20" } fabric_incompatibility_list={ "iris": "<=1.7.4" }
fabric_recommend_list={} fabric_recommend_list={}
# Fabric mod run # Fabric mod run
+1 -1
View File
@@ -20,7 +20,7 @@ fabric_api_version=0.90.4+1.20.2
immersive_portals_version= immersive_portals_version=
canvas_version= canvas_version=
fabric_incompatibility_list={ "iris": "<=1.6.20" } fabric_incompatibility_list={ "iris": "<=1.7.4" }
fabric_recommend_list={} fabric_recommend_list={}
# Fabric mod run # Fabric mod run
+1 -1
View File
@@ -21,7 +21,7 @@ fabric_api_version=0.91.2+1.20.4
immersive_portals_version= immersive_portals_version=
canvas_version= canvas_version=
fabric_incompatibility_list={ "iris": "<=1.6.20" } fabric_incompatibility_list={ "iris": "<=1.7.4" }
fabric_recommend_list={} fabric_recommend_list={}
# Fabric mod run # Fabric mod run
+1 -1
View File
@@ -21,7 +21,7 @@ fabric_api_version=0.97.8+1.20.6
immersive_portals_version= immersive_portals_version=
canvas_version= canvas_version=
fabric_incompatibility_list={ "iris": "<=1.6.20" } fabric_incompatibility_list={ "iris": "<=1.7.4" }
fabric_recommend_list={} fabric_recommend_list={}
# Fabric mod run # Fabric mod run
+1 -1
View File
@@ -21,7 +21,7 @@ fabric_api_version=0.100.1+1.21
immersive_portals_version= immersive_portals_version=
canvas_version= canvas_version=
fabric_incompatibility_list={ "iris": "<=1.6.20" } fabric_incompatibility_list={ "iris": "<=1.7.4" }
fabric_recommend_list={} fabric_recommend_list={}
# Fabric mod run # Fabric mod run