Compare commits

..

8 Commits

Author SHA1 Message Date
Ran 0c8b3f3cda Merge remote-tracking branch 'origin/java_omega' into java_omega
# Conflicts:
#	build.gradle
#	coreSubProjects
#	fabric/build.gradle
#	forge/build.gradle
2024-07-26 00:21:34 +10:00
Ran a9a6a19307 Attempt to migrate most of the codebase to newer versions of Java 2024-07-26 00:17:15 +10:00
Cutiepie 208fc69eb7 Rebase 2024-07-25 23:00:43 +10:00
Cutiepie eb1a062934 Rebase 2024-07-25 22:59:16 +10:00
Cutiepie 55d5bca76d Downgrade to the minimum Java version required by the selected Minecraft version 2024-05-22 22:17:59 +10:00
Cutiepie f81026c707 Bump JVM Downgrader 2024-05-22 21:51:46 +10:00
Cutiepie e86af817e0 Java Ω & cleanup core's code 2024-05-22 21:06:36 +10:00
Cutiepie e0fd2b2f7a Java Ω & cleanup core's code 2024-05-22 21:03:38 +10:00
182 changed files with 3498 additions and 7253 deletions
+1 -1
View File
@@ -688,7 +688,7 @@ ij_markdown_wrap_text_inside_blockquotes = true
ij_toml_keep_indents_on_empty_lines = false
[{*.yaml,*.yml}]
indent_size = 2
indent_size = 4
ij_yaml_align_values_properties = do_not_align
ij_yaml_autoinsert_sequence_marker = true
ij_yaml_block_mapping_on_new_line = false
+2 -7
View File
@@ -18,24 +18,19 @@ variables:
.build_java:
#image: eclipse-temurin:17
cache:
key: "gradleCache_$CI_JOB_NAME_SLUG"
key: "gradleCache"
policy: pull-push
paths:
- .gradle
- cache/
allow_failure: true
retry:
max: 2
when:
- runner_system_failure
- stuck_or_timeout_failure
build:
stage: build
parallel:
matrix:
- MC_VER: ["1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2", "1.20.4", "1.20.6", "1.21.1", "1.21.3", "1.21.4"]
- MC_VER: ["1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2", "1.20.4", "1.20.6", "1.21"]
script:
# this both runs the unit tests and assembles the code
- ./gradlew clean -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
-7
View File
@@ -1,7 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Fabric Client &amp; Server" type="CompoundRunConfigurationType">
<toRun name="Fabric Client (:fabric)" type="Application" />
<toRun name="Fabric Server (:fabric)" type="Application" />
<method v="2" />
</configuration>
</component>
-7
View File
@@ -1,7 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Forge Client &amp; Server" type="CompoundRunConfigurationType">
<toRun name="Forge Client (gradle)" type="GradleRunConfiguration" />
<toRun name="Forge Server (gradle)" type="GradleRunConfiguration" />
<method v="2" />
</configuration>
</component>
-24
View File
@@ -1,24 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Forge Client (gradle)" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="forge:runClient" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
-24
View File
@@ -1,24 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Forge Server (gradle)" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="forge:runServer" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
-7
View File
@@ -1,7 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Neoforge Client &amp; Server" type="CompoundRunConfigurationType">
<toRun name="Neoforge Client (gradle)" type="GradleRunConfiguration" />
<toRun name="Neoforge Server (gradle)" type="GradleRunConfiguration" />
<method v="2" />
</configuration>
</component>
-24
View File
@@ -1,24 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Neoforge Client (gradle)" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="neoforge:runClient" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
-24
View File
@@ -1,24 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Neoforge Server (gradle)" type="GradleRunConfiguration" factoryName="Gradle">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="neoforge:runServer" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
-24
View File
@@ -1,24 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="distant-horizons [build]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="build" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
-24
View File
@@ -1,24 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="distant-horizons [clean]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="clean" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
@@ -1,24 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="distant-horizons [core:build]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="core:build" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
@@ -1,24 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="distant-horizons [fabric:runClient]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="fabric:runClient" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
@@ -1,24 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="distant-horizons [forge:runClient]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="forge:runClient" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
-24
View File
@@ -1,24 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="distant-horizons [mergeJars]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="mergeJars" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
@@ -1,24 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="distant-horizons [neoforge:runClient]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="neoforge:runClient" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
-24
View File
@@ -1,24 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="distant-horizons [test]" type="GradleRunConfiguration" factoryName="Gradle" nameIsGenerated="true">
<ExternalSystemSettings>
<option name="executionName" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="externalSystemIdString" value="GRADLE" />
<option name="scriptParameters" value="" />
<option name="taskDescriptions">
<list />
</option>
<option name="taskNames">
<list>
<option value="test" />
</list>
</option>
<option name="vmOptions" />
</ExternalSystemSettings>
<ExternalSystemDebugServerProcess>true</ExternalSystemDebugServerProcess>
<ExternalSystemReattachDebugProcess>true</ExternalSystemReattachDebugProcess>
<DebugAllEnabled>false</DebugAllEnabled>
<RunAsTest>false</RunAsTest>
<method v="2" />
</configuration>
</component>
-1
View File
@@ -1 +0,0 @@
Distant Horizons logos © 2024 by Pankakes are licensed under CC BY-SA 4.0
+3 -3
View File
@@ -1,4 +1,4 @@
# <img src="https://gitlab.com/distant-horizons-team/distant-horizons-core/-/raw/main/_Misc%20Files/logo%20files/new/SVG/Distant-Horizons.svg" height="128px">
# <img src="https://gitlab.com/jeseibel/distant-horizons-core/-/raw/main/_Misc%20Files/logo%20files/LOD%20logo%20flat%20-%20with%20boarder.png" width="32"> Distant Horizons
_See farther without turning your game into a slide show._
<br>
@@ -131,14 +131,14 @@ Prerequisites:
From the File Explorer:
1. Download and extract the project zip
2. Download the core from https://gitlab.com/distant-horizons-team/distant-horizons-core and extract into a folder called `coreSubProjects`
2. Download the core from https://gitlab.com/jeseibel/distant-horizons-core and extract into a folder called `coreSubProjects`
3. Open a terminal emulator in the project folder (On Windows you can type `cmd` in the title bar)
4. Run the commands: `./gradlew assemble` (You may need to use a `.\` on Windows)
5. Merge the jars with `./gradlew mergeJars`
6. The compiled jar file will be in the folder `Merged`
From the command line:
1. `git clone --recurse-submodules https://gitlab.com/distant-horizons-team/distant-horizons.git`
1. `git clone --recurse-submodules https://gitlab.com/jeseibel/distant-horizons.git`
2. `cd distant-horizons`
3. `./gradlew assemble`
4. `./gradlew mergeJars`
+37 -101
View File
@@ -11,7 +11,9 @@ plugins {
id "systems.manifold.manifold-gradle-plugin" version "0.0.2-alpha"
// Architectury is used here only as a replacement for forge's own loom
id "dev.architectury.loom" version "1.7-SNAPSHOT" apply false
id "dev.architectury.loom" version "1.6-SNAPSHOT" apply false
id 'xyz.wagyourtail.jvmdowngrader' version '0.9.1' apply true
}
@@ -66,11 +68,8 @@ writeBuildGradlePredefine(rootProject.mcVers, rootProject.mcIndex)
rootProject.versionStr = rootProject.mod_version + "-" + rootProject.minecraft_version // + "-" + new Date().format("yyyy_MM_dd_HH_mm")
// Forgix settings (used for merging jars)
forgix {
String loaderHyphenSeparatedList = ((String)gradle.builds_for).replaceAll(",", "-");
group = "com.seibel.distanthorizons"
mergedJarName = "DistantHorizons-${rootProject.versionStr}-${loaderHyphenSeparatedList}.jar"
mergedJarName = "DistantHorizons-${rootProject.versionStr}.jar"
if (findProject(":forge"))
forge {
@@ -105,16 +104,13 @@ subprojects { p ->
// Apply plugins
apply plugin: "java"
apply plugin: "com.github.johnrengelman.shadow"
apply plugin: "xyz.wagyourtail.jvmdowngrader"
if (isMinecraftSubProject)
apply plugin: "systems.manifold.manifold-gradle-plugin"
// 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"
}
// Set the manifold version (may not be required tough)
@@ -162,7 +158,6 @@ subprojects { p ->
if (findProject(":neoforge"))
developmentNeoForge.extendsFrom coreProjects
// TODO remove unused fabricLike
if (findProject(":fabricLike") && p != project(":fabricLike")) {
// Shadow fabricLike
fabricLike
@@ -185,19 +180,10 @@ subprojects { p ->
}
// Log4j
if (p == project(":core"))
{
// the standalone core jar needs logging shaded otherwise it won't run
forgeShadowMe("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}")
forgeShadowMe("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}")
}
else
{
// When running in MC, MC already includes logging
// TODO: Change to shadowMe later to work in the standalone jar
// We cannot do this now as it would break Quilt
implementation("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}")
implementation("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}")
}
// JOML
if (project.hasProperty("embed_joml") && embed_joml == "true")
@@ -231,7 +217,8 @@ subprojects { p ->
// forgeShadowMe("com.formdev:svgSalamander:${rootProject.svgSalamander_version}")
// Netty
implementation("io.netty:netty-buffer:${rootProject.netty_version}")
// Breaks 1.16.5
//forgeShadowMe("io.netty:netty-all:${rootProject.netty_version}")
// Remember, for lwjgl dependencies that arent included in Minecraft, you need to also need to add it to the ShadowJar thing
forgeShadowMe("org.lwjgl:lwjgl-jawt:${rootProject.lwjgl_version}") {
@@ -304,29 +291,9 @@ subprojects { p ->
// Compression (LZ4)
relocate "net.jpountz", "${librariesLocation}.jpountz"
// Logging
relocate "org.slf4j", "${librariesLocation}.slf4j"
// // Sqlite Database
// // James can't determine how to relocate the library correctly so this is commented out
// relocate ("org.sqlite", "${librariesLocation}.sqlite") {
// exclude("org/sqlite/core/NativeDB/**")
//
// exclude("org/sqlite/native/FreeBSD/**")
// exclude("org/sqlite/native/Linux-Android/**")
// exclude("org/sqlite/native/Linux-Musl/**")
// exclude("org/sqlite/native/Linux/arm/**")
// exclude("org/sqlite/native/Linux/aarch64/**")
// exclude("org/sqlite/native/Linux/armv6/**")
// exclude("org/sqlite/native/Linux/x86/**")
// exclude("org/sqlite/native/Linux/armv7/**")
// exclude("org/sqlite/native/Linux/ppc64/**")
// exclude("org/sqlite/native/Linux/riscv64/**")
// exclude("org/sqlite/native/Windows/armv7/**")
// exclude("org/sqlite/native/Windows/aarch64/**")
// exclude("org/sqlite/native/Windows/armv7/**")
// }
// Sqlite Database
//At the moment, there is a bug in this library which doesnt allow it to be relocated
// relocate "org.sqlite", "${librariesLocation}.sqlite"
// JOML
if (project.hasProperty("embed_joml") && embed_joml == "true")
@@ -339,13 +306,25 @@ subprojects { p ->
// relocate "com.kitfox.svg", "${librariesLocation}.kitfox.svg"
// Netty
// Don't relocate, it causes problems with using MC's FriendlyByteBufs
// relocate "io.netty", "${librariesLocation}.netty"
relocate "io.netty", "${librariesLocation}.netty"
mergeServiceFiles()
}
// Using jar.finalizedBy(shadowJar) causes issues so we do this scuffed bypass
jar.dependsOn(shadowJar)
// For downgrading the Java version of our project to match the minimum Java version required by the selected Minecraft version.
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
@@ -464,12 +443,9 @@ subprojects { p ->
jar {
from "LICENSE.txt"
manifest {
attributes(
'Implementation-Title': rootProject.mod_name,
attributes 'Implementation-Title': rootProject.mod_name,
'Implementation-Version': rootProject.mod_version,
'Multi-Release': true, // needed for logging in the standalone core jar
'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain', // When changing the main of the jar change this line
)
'Main-Class': 'com.seibel.distanthorizons.core.jar.JarMain' // When changing the main of the jar change this line
}
}
@@ -523,9 +499,6 @@ allprojects { p ->
repositories {
// Mojang overrides (added to fix downloading the wrong LWJGL libs on M1 Mac's and potentially other arm64 based machines)
maven { url "https://libraries.minecraft.net/" }
// The central repo
mavenCentral()
@@ -533,7 +506,6 @@ allprojects { p ->
maven { url "https://repo.enonic.com/public/" }
// For parchment mappings
// versions can be found here: https://ldtteam.jfrog.io/ui/native/parchmentmc-public/org/parchmentmc/data/
maven { url "https://maven.parchmentmc.org" }
// For Architectury API
@@ -595,44 +567,6 @@ allprojects { p ->
// 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) {
from project(":common").file("src/main/resources/${accessWidenerVersion}.distanthorizons.accesswidener")
into(file(p.file("build/resources/main")))
@@ -654,15 +588,17 @@ allprojects { p ->
tasks.withType(JavaCompile) {
if (isMinecraftSubProject) {
options.release = rootProject.java_version as Integer
} else {
options.release = 8; // Core & Api should use Java 8 no matter what
//options.release = rootProject.java_version as Integer // But if you want to test some stuff, then this can be enabled
options.release = rootProject.java_version as Integer // Neoforge complains without this
options.compilerArgs += ["-Xplugin:Manifold"]
}
options.encoding = "UTF-8"
}
// Sets the project's actual Java version (it's recommended to use this over the `options.release` method above)
java {
sourceCompatibility = rootProject.java_version
targetCompatibility = rootProject.java_version
withSourcesJar()
}
}
+6 -12
View File
@@ -1,8 +1,7 @@
#!/bin/sh
echo "==================== Note: All build jars will be in the folder called 'buildAllJars' ===================="
mkdir -p buildAllJars
rm -rf buildAllJars/*
mkdir -p buildAllJars | true
# Loop trough everything in the version properties folder
for d in versionProperties/*; do
@@ -12,17 +11,12 @@ for d in versionProperties/*; do
# Clean out the folders, build it, and merge it
# (We could use "./" to run gradlew, but as it is a shell script im going to be running it with the "sh" command)
echo "==================== Cleaning workspace to build $version ===================="
sh gradlew clean -PmcVer=$version
if [ $? != 0 ]; then continue; fi
sh gradlew clean -PmcVer=$version --no-daemon || true
echo "====================Building $version ===================="
sh gradlew build -PmcVer=$version
if [ $? != 0 ]; then continue; fi
sh gradlew build -PmcVer=$version --no-daemon || true
echo "==================== Merging $version ===================="
sh gradlew mergeJars -PmcVer=$version
if [ $? != 0 ]; then continue; fi
sh gradlew mergeJars -PmcVer=$version --no-daemon || true
echo "==================== Moving jar ===================="
mv Merged/*.jar buildAllJars/
mv Merged/*.jar buildAllJars/ || true
# The "| true" at the end of those are just to make sure the script continues even if a build fails
done
+3 -4
View File
@@ -5,7 +5,6 @@
echo ==================== Note: All build jars will be in the folder called 'buildAllJars' ====================
mkdir buildAllJars
del buildAllJars/*
@rem Loop trough everything in the version properties folder
for %%f in (versionProperties\*) do (
@@ -14,11 +13,11 @@ for %%f in (versionProperties\*) do (
@rem Clean out the folders, build it, and merge it
echo ==================== Cleaning workspace to build !version! ====================
call .\gradlew.bat clean -PmcVer="!version!"
call .\gradlew.bat clean -PmcVer="!version!" --no-daemon
echo ==================== Building !version! ====================
call .\gradlew.bat build -PmcVer="!version!"
call .\gradlew.bat build -PmcVer="!version!" --no-daemon
echo ==================== Merging !version! ====================
call .\gradlew.bat mergeJars -PmcVer="!version!"
call .\gradlew.bat mergeJars -PmcVer="!version!" --no-daemon
echo ==================== Moving jar ====================
move Merged\*.jar buildAllJars\
)
-2
View File
@@ -4,8 +4,6 @@ buildscript {
configurations.configureEach {
resolutionStrategy {
force 'org.spongepowered:vanillagradle:0.2.1-20240507.024226-82'
// newer versions can be found by going to the link:
// https://repo.spongepowered.org/#browse/browse:maven-public:org%2Fspongepowered%2Fvanillagradle%2F0.2.1-SNAPSHOT
}
}
}
@@ -3,10 +3,8 @@ package com.seibel.distanthorizons.common;
import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDhInitEvent;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiBeforeDhInitEvent;
import com.seibel.distanthorizons.common.commands.CommandInitializer;
import com.seibel.distanthorizons.common.wrappers.DependencySetup;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftDedicatedServerWrapper;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigBase;
@@ -14,7 +12,6 @@ import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPreset
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.jar.ModJarInfo;
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
@@ -37,7 +34,7 @@ public abstract class AbstractModInitializer
{
protected static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
private CommandInitializer commandInitializer;
private CommandDispatcher<CommandSourceStack> commandDispatcher;
@@ -66,23 +63,22 @@ public abstract class AbstractModInitializer
{
DependencySetup.createClientBindings();
LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " client.");
LOGGER.info("Initializing " + ModInfo.READABLE_NAME);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
this.startup();
this.logBuildInfo();
this.printModInfo(true);
this.createClientProxy().registerEvents();
this.createServerProxy(false).registerEvents();
this.initializeModCompat();
LOGGER.info(ModInfo.READABLE_NAME + " client Initialized.");
LOGGER.info(ModInfo.READABLE_NAME + " Initialized");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
// Client uses config for auto-updater, so it's initialized here instead of post-init stage
this.initConfig();
logModIncompatibilityWarnings(); // needs to be called after config loading
this.subscribeClientStartedEvent(this::postInit);
}
@@ -91,11 +87,11 @@ public abstract class AbstractModInitializer
{
DependencySetup.createServerBindings();
LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " server.");
LOGGER.info("Initializing " + ModInfo.READABLE_NAME);
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
this.startup();
this.logBuildInfo();
this.printModInfo(false);
// This prevents returning uninitialized Config values,
// resulting from a circular reference mid-initialization in a static class
@@ -104,22 +100,18 @@ public abstract class AbstractModInitializer
this.createServerProxy(true).registerEvents();
this.initializeModCompat();
LOGGER.info(ModInfo.READABLE_NAME + " server Initialized.");
LOGGER.info(ModInfo.READABLE_NAME + " Initialized");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandInitializer = new CommandInitializer(dispatcher); });
this.subscribeRegisterCommandsEvent(dispatcher -> this.commandDispatcher = dispatcher);
this.subscribeServerStartingEvent(server ->
{
MinecraftServerWrapper.INSTANCE.dedicatedServer = (DedicatedServer)server;
MinecraftDedicatedServerWrapper.INSTANCE.dedicatedServer = (DedicatedServer)server;
this.initConfig();
this.postInit();
this.commandInitializer.initCommands();
this.checkForUpdates();
this.initCommands();
LOGGER.info("Dedicated server initialized at " + server.getServerDirectory());
});
@@ -138,13 +130,13 @@ public abstract class AbstractModInitializer
this.createInitialBindings();
}
private void logBuildInfo()
private void printModInfo(boolean printGitInfo)
{
LOGGER.info(ModInfo.READABLE_NAME + ", Version: " + ModInfo.VERSION);
// if the build is stable the branch/commit/etc shouldn't be needed
if (ModInfo.IS_DEV_BUILD)
if (printGitInfo)
{
// Useful for dev builds
LOGGER.info("DH Branch: " + ModJarInfo.Git_Branch);
LOGGER.info("DH Commit: " + ModJarInfo.Git_Commit);
LOGGER.info("DH Jar Build Source: " + ModJarInfo.Build_Source);
@@ -163,24 +155,10 @@ public abstract class AbstractModInitializer
private void initConfig()
{
ConfigBase.INSTANCE = new ConfigBase(ModInfo.ID, ModInfo.NAME, Config.class, ModInfo.CONFIG_FILE_VERSION);
ConfigBase.INSTANCE = new ConfigBase(ModInfo.ID, ModInfo.NAME, Config.class, 2);
Config.completeDelayedSetup();
}
private void checkForUpdates()
{
if (Config.Client.Advanced.AutoUpdater.enableAutoUpdater.get())
{
if (Config.Client.Advanced.AutoUpdater.enableSilentUpdates.get())
{
LOGGER.info("Silent updates are not allowed for dedicated servers; force disabling.");
Config.Client.Advanced.AutoUpdater.enableSilentUpdates.set(false);
}
SelfUpdater.onStart();
}
}
private void postInit()
{
LOGGER.info("Post-Initializing Mod");
@@ -188,101 +166,7 @@ public abstract class AbstractModInitializer
LOGGER.info("Mod Post-Initialized");
}
//==================================//
// mod partial compatibility checks //
//==================================//
/**
* Some mods will work with a few tweaks
* or will partially work but have some known issues we can't solve.
* This method will log (and display to chat if enabled)
* these warnings and potential fixes.
*/
private static void logModIncompatibilityWarnings()
{
boolean showChatWarnings = Config.Common.Logging.Warning.showModCompatibilityWarningsOnStartup.get();
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
String startingString = "Partially Incompatible Distant Horizons mod detected: ";
// Alex's caves
if (modChecker.isModLoaded("alexscaves"))
{
// There've been a few reports about this mod breaking DH at a few different points in time
// the fixes for said breakage changes depending on the version so unfortunately
// all we can do is log a warning so the user can handle it.
if (showChatWarnings)
{
String message =
// orange text
"\u00A76" + "Distant Horizons: Alex's Cave detected." + "\u00A7r\n" +
"You may have to change Alex's config for DH to render. ";
ClientApi.INSTANCE.showChatMessageNextFrame(message);
}
LOGGER.warn(startingString + "[Alex's Caves] may require some config changes in order to render Distant Horizons correctly.");
}
// William Wythers' Overhauled Overworld (WWOO)
if (modChecker.isModLoaded("wwoo"))
{
// WWOO has a bug with it's world gen that can't be fixed by DH or WWOO
// (at least that is what James learned after talking with WWOO)
// WWOO will cause grid lines to appear in the world when DH generates the chunks
// this might be due to how WWOO uses features for everything when generating
// and said features don't always get to the edge of said chunks.
String wwooWarning = "LODs generated by DH may have grid lines between sections. Disabling either WWOO or DH's distant generator will fix the problem.";
if (showChatWarnings)
{
String message =
// orange text
"\u00A76" + "Distant Horizons: WWOO detected." + "\u00A7r\n" +
wwooWarning;
ClientApi.INSTANCE.showChatMessageNextFrame(message);
}
LOGGER.warn(startingString + "[WWOO] "+ wwooWarning);
}
// Chunky
boolean chunkyPresent = false;
try
{
Class.forName("org.popcraft.chunky.api.ChunkyAPI");
chunkyPresent = true;
}
catch (ClassNotFoundException ignore) { }
if (chunkyPresent)
{
// Chunky can generate chunks faster than DH can process them,
// causing holes in the LODs.
// Generally it's better and faster to use DH's world generator.
String chunkyWarning = "Chunky can cause DH LODs to have holes " +
"since Chunky can generate chunks faster than DH can process them. \n" +
"Using DH's distant generator instead of chunky or increasing DH's CPU thread count can resolve the issue.";
if (showChatWarnings)
{
String message =
// orange text
"\u00A76" + "Distant Horizons: Chunky detected." + "\u00A7r\n" +
chunkyWarning;
ClientApi.INSTANCE.showChatMessageNextFrame(message);
}
LOGGER.warn(startingString + "[Chunky] "+ chunkyWarning);
}
}
private void initCommands() { /* currently unimplemented */ }
@@ -1,112 +0,0 @@
package com.seibel.distanthorizons.common;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.network.event.internal.IncompatibleMessageInternalEvent;
import com.seibel.distanthorizons.core.network.event.internal.ProtocolErrorInternalEvent;
import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import com.seibel.distanthorizons.core.network.messages.base.CloseReasonMessage;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import io.netty.buffer.ByteBufUtil;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.level.ServerPlayer;
import org.apache.logging.log4j.LogManager;
import java.io.IOException;
import java.util.Objects;
public abstract class AbstractPluginPacketSender implements IPluginPacketSender
{
private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(),
() -> Config.Common.Logging.logNetworkEvent.get());
#if MC_VER >= MC_1_21_1
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
#else
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
#endif
@Override
public final void sendToClient(IServerPlayerWrapper serverPlayer, AbstractNetworkMessage message)
{
this.sendToClient((ServerPlayer) serverPlayer.getWrappedMcObject(), message);
}
public abstract void sendToClient(ServerPlayer serverPlayer, AbstractNetworkMessage message);
@Override
public abstract void sendToServer(AbstractNetworkMessage message);
public static AbstractNetworkMessage decodeMessage(FriendlyByteBuf in)
{
AbstractNetworkMessage message = null;
try
{
in.markReaderIndex();
int protocolVersion = in.readShort();
if (protocolVersion != ModInfo.PROTOCOL_VERSION)
{
return new IncompatibleMessageInternalEvent(protocolVersion);
}
message = MessageRegistry.INSTANCE.createMessage(in.readUnsignedShort());
message.decode(in);
if (in.isReadable())
{
throw new IOException("Buffer has not been fully read");
}
return message;
}
catch (Exception e)
{
in.resetReaderIndex();
LOGGER.error("Failed to decode message", e);
LOGGER.error("Buffer: ["+in+"]");
LOGGER.error("Buffer contents: ["+ByteBufUtil.hexDump(in)+"]");
return new ProtocolErrorInternalEvent(e, message, true);
}
finally
{
// Prevent connection crashing if not entire buffer has been read
in.readerIndex(in.writerIndex());
}
}
public static void encodeMessage(FriendlyByteBuf out, AbstractNetworkMessage message)
{
// This is intentionally unhandled, because errors related to this are unlikely to appear in wild
Objects.requireNonNull(message);
out.writeShort(ModInfo.PROTOCOL_VERSION);
try
{
out.markWriterIndex();
out.writeShort(MessageRegistry.INSTANCE.getMessageId(message));
message.encode(out);
}
catch (Exception e)
{
LOGGER.error("Failed to encode message", e);
LOGGER.error("Message: ["+message+"]");
message.getSession().tryHandleMessage(new ProtocolErrorInternalEvent(e, message, false));
// Encode close reason message instead
out.resetWriterIndex();
message = new CloseReasonMessage("Internal error on other side");
out.writeShort(MessageRegistry.INSTANCE.getMessageId(message));
message.encode(out);
}
}
}
@@ -1,36 +0,0 @@
package com.seibel.distanthorizons.common;
#if MC_VER >= MC_1_20_6
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.codec.StreamCodec;
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public record CommonPacketPayload(@Nullable AbstractNetworkMessage message) implements CustomPacketPayload
{
public static final Type<CommonPacketPayload> TYPE = new Type<>(AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE);
@NotNull
@Override
public Type<? extends CustomPacketPayload> type() { return TYPE; }
public static class Codec implements StreamCodec<FriendlyByteBuf, CommonPacketPayload>
{
@NotNull
@Override
public CommonPacketPayload decode(@NotNull FriendlyByteBuf in)
{ return new CommonPacketPayload(AbstractPluginPacketSender.decodeMessage(in)); }
@Override
public void encode(@NotNull FriendlyByteBuf out, CommonPacketPayload payload)
{ AbstractPluginPacketSender.encodeMessage(out, payload.message()); }
}
}
#endif
@@ -1,83 +0,0 @@
package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import net.minecraft.commands.CommandSourceStack;
#if MC_VER >= MC_1_19_2
import net.minecraft.network.chat.Component;
import java.util.Objects;
#else // < 1.19.2
import net.minecraft.network.chat.TranslatableComponent;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
#endif
/**
* Abstract class providing common functionality for DH's commands.
*/
public abstract class AbstractCommand
{
public abstract LiteralArgumentBuilder<CommandSourceStack> buildCommand();
/**
* Sends a success response to the player with the given text.
*
* @param commandContext The command context to send the response to.
* @param text The text to display in the success message.
* @return 1, indicating that the command was successful.
*/
protected int sendSuccessResponse(CommandContext<CommandSourceStack> commandContext, String text)
{
#if MC_VER >= MC_1_20_1
commandContext.getSource().sendSuccess(() -> Component.literal(text), true);
#elif MC_VER >= MC_1_19_2
commandContext.getSource().sendSuccess(Component.literal(text), true);
#else
commandContext.getSource().sendSuccess(new TranslatableComponent(text), true);
#endif
return 1;
}
/**
* Gets the server player from a command context.
*
* @param commandContext The command context to get the server player from.
* @return The server player wrapper for the player who sent the command.
*/
protected IServerPlayerWrapper getSourcePlayer(CommandContext<CommandSourceStack> commandContext) #if MC_VER < MC_1_19_2 throws CommandSyntaxException #endif
{
#if MC_VER >= MC_1_19_2
return ServerPlayerWrapper.getWrapper(Objects.requireNonNull(commandContext.getSource().getPlayer()));
#else
return ServerPlayerWrapper.getWrapper(commandContext.getSource().getPlayerOrException());
#endif
}
/**
* Checks if the source of a command is a player.
*
* @param source The source of the command to check.
* @return True if the source is a player, false otherwise.
*/
protected boolean isPlayerSource(CommandSourceStack source)
{
#if MC_VER >= MC_1_19_2
return source.isPlayer();
#else
try
{
source.getPlayerOrException();
return true;
}
catch (CommandSyntaxException e)
{
return false;
}
#endif
}
}
@@ -1,48 +0,0 @@
package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import net.minecraft.commands.CommandSourceStack;
import static com.seibel.distanthorizons.core.network.messages.MessageRegistry.DEBUG_CODEC_CRASH_MESSAGE;
import static net.minecraft.commands.Commands.literal;
/**
* Initializes commands of the mod.
*/
public class CommandInitializer
{
private final CommandDispatcher<CommandSourceStack> commandDispatcher;
/**
* Constructs a new instance of this class.
*
* @param commandDispatcher The dispatcher to use for registering commands.
*/
public CommandInitializer(CommandDispatcher<CommandSourceStack> commandDispatcher)
{
this.commandDispatcher = commandDispatcher;
}
/**
* Initializes all available commands.
*/
public void initCommands()
{
LiteralArgumentBuilder<CommandSourceStack> builder = literal("dh")
.requires(source -> source.hasPermission(4));
builder.then(new ConfigCommand().buildCommand());
builder.then(new DebugCommand().buildCommand());
if (DEBUG_CODEC_CRASH_MESSAGE)
{
builder.then(new CrashCommand().buildCommand());
}
this.commandDispatcher.register(builder);
}
}
@@ -1,146 +0,0 @@
package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.arguments.*;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.config.types.AbstractConfigType;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import net.minecraft.commands.CommandSourceStack;
import java.util.Arrays;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.function.ToIntBiFunction;
import static com.mojang.brigadier.arguments.DoubleArgumentType.doubleArg;
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
import static net.minecraft.commands.Commands.argument;
import static net.minecraft.commands.Commands.literal;
/**
* Command for managing config.
*/
public class ConfigCommand extends AbstractCommand
{
private static final List<CommandArgumentData<?>> commandArguments = Arrays.asList(
new CommandArgumentData<>(Integer.class, configEntry -> integer(configEntry.getMin(), configEntry.getMax()), IntegerArgumentType::getInteger),
new CommandArgumentData<>(Double.class, configEntry -> doubleArg(configEntry.getMin(), configEntry.getMax()), DoubleArgumentType::getDouble),
new CommandArgumentData<>(Boolean.class, BoolArgumentType::bool, BoolArgumentType::getBool),
new CommandArgumentData<>(String.class, StringArgumentType::string, StringArgumentType::getString)
);
/**
* Builds a command tree.
*/
@Override
@SuppressWarnings({"rawtypes", "unchecked"})
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
{
LiteralArgumentBuilder<CommandSourceStack> builder = literal("config");
for (AbstractConfigType<?, ?> type : ConfigBase.INSTANCE.entries)
{
// Skip non-config entries
if (!(type instanceof ConfigEntry))
{
continue;
}
//noinspection PatternVariableCanBeUsed
ConfigEntry configEntry = (ConfigEntry) type;
if (configEntry.getChatCommandName() == null)
{
continue;
}
LiteralArgumentBuilder<CommandSourceStack> subcommand = literal(configEntry.getChatCommandName())
.executes(commandContext -> this.sendSuccessResponse(commandContext,
"\n" +
"Description of §l" + configEntry.getChatCommandName() + "§r:\n" +
"§o" + configEntry.getComment().trim() + "§r\n" +
"§7Config file name: §f" + configEntry.name + "§7, category: §f" + configEntry.category + "\n" +
"\n" +
"Current value of " + configEntry.getChatCommandName() + " is §n" + configEntry.get() + "§r"
));
ToIntBiFunction<CommandContext<CommandSourceStack>, Object> updateConfigValue = (commandContext, value) -> {
configEntry.set(value);
return this.sendSuccessResponse(commandContext, "Changed the value of ["+configEntry.getChatCommandName()+"] to ["+value+"]");
};
// Enum type needs a special case since enums aren't represented by existing argument type
// and need literals for each individual value
if (Enum.class.isAssignableFrom(configEntry.getType()))
{
for (Object choice : configEntry.getType().getEnumConstants())
{
subcommand.then(
literal(choice.toString())
.executes(c -> updateConfigValue.applyAsInt(c, choice))
);
}
}
else
{
boolean setterAdded = false;
for (CommandArgumentData<?> commandArgumentData : commandArguments)
{
if (!commandArgumentData.argumentClass.isAssignableFrom(configEntry.getType()))
{
continue;
}
subcommand.then(argument("value", commandArgumentData.getArgumentType(configEntry))
.executes(c -> updateConfigValue.applyAsInt(c, commandArgumentData.getValue(c, "value"))));
setterAdded = true;
break;
}
if (!setterAdded)
{
throw new RuntimeException("Config type of " + type.getName() + " is not supported: " + configEntry.getType().getSimpleName());
}
}
builder.then(subcommand);
}
return builder;
}
private static class CommandArgumentData<T>
{
public final Class<T> argumentClass;
public final Function<ConfigEntry<T>, ArgumentType<T>> argumentTypeFunction;
private final BiFunction<CommandContext<CommandSourceStack>, String, T> valueGetter;
public CommandArgumentData(Class<T> argumentClass, Supplier<ArgumentType<T>> argumentTypeSupplier, BiFunction<CommandContext<CommandSourceStack>, String, T> valueGetter)
{
this(argumentClass, configEntry -> argumentTypeSupplier.get(), valueGetter);
}
public CommandArgumentData(Class<T> argumentClass, Function<ConfigEntry<T>, ArgumentType<T>> argumentTypeFunction, BiFunction<CommandContext<CommandSourceStack>, String, T> valueGetter)
{
this.argumentClass = argumentClass;
this.argumentTypeFunction = argumentTypeFunction;
this.valueGetter = valueGetter;
}
public ArgumentType<T> getArgumentType(ConfigEntry<T> configEntry)
{
return this.argumentTypeFunction.apply(configEntry);
}
public T getValue(CommandContext<CommandSourceStack> commandContext, String argumentName)
{
return this.valueGetter.apply(commandContext, argumentName);
}
}
}
@@ -1,44 +0,0 @@
package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState;
import com.seibel.distanthorizons.core.network.messages.base.CodecCrashMessage;
import net.minecraft.commands.CommandSourceStack;
import static net.minecraft.commands.Commands.literal;
public class CrashCommand extends AbstractCommand
{
@Override
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
{
return literal("crash")
.requires(this::isPlayerSource)
.then(literal("encode")
.executes(c -> {
assert SharedApi.getIDhServerWorld() != null;
ServerPlayerState serverPlayerState = SharedApi.getIDhServerWorld().getServerPlayerStateManager()
.getConnectedPlayer(this.getSourcePlayer(c));
if (serverPlayerState != null)
{
serverPlayerState.networkSession.sendMessage(new CodecCrashMessage(CodecCrashMessage.ECrashPhase.ENCODE));
}
return 1;
}))
.then(literal("decode")
.executes(c -> {
assert SharedApi.getIDhServerWorld() != null;
ServerPlayerState serverPlayerState = SharedApi.getIDhServerWorld().getServerPlayerStateManager()
.getConnectedPlayer(this.getSourcePlayer(c));
if (serverPlayerState != null)
{
serverPlayerState.networkSession.sendMessage(new CodecCrashMessage(CodecCrashMessage.ECrashPhase.DECODE));
}
return 1;
}));
}
}
@@ -1,25 +0,0 @@
package com.seibel.distanthorizons.common.commands;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import net.minecraft.commands.CommandSourceStack;
import java.util.ArrayList;
import java.util.List;
import static net.minecraft.commands.Commands.literal;
public class DebugCommand extends AbstractCommand
{
@Override
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
{
return literal("debug")
.executes(c -> {
List<String> lines = new ArrayList<>();
F3Screen.addStringToDisplay(lines);
return this.sendSuccessResponse(c, String.join("\n", lines));
});
}
}
@@ -1,85 +0,0 @@
package com.seibel.distanthorizons.common.commonMixins;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
public class MixinChunkMapCommon
{
public static void onChunkSave(ServerLevel level, ChunkAccess chunk, CallbackInfoReturnable<Boolean> ci)
{
// is this position already being updated?
if (SharedApi.isChunkAtChunkPosAlreadyUpdating(chunk.getPos().x, chunk.getPos().z))
{
return;
}
// is this chunk being saved to disk?
boolean savingChunkToDisk = ci.getReturnValue();
// true means a chunk was saved to disk
if (!savingChunkToDisk)
{
return;
}
// TODO are the following validations necessary since we are checking above if
// the callback return value should state if the chunk was actually saved or not?
// Do we trust it to always be correct?
// corrupt/incomplete chunk validation //
// MC has a tendency to try saving incomplete or corrupted chunks (which show up as empty or black chunks)
// this logic should prevent that from happening
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
if (chunk.isUnsaved() || chunk.getUpgradeData() != null || !chunk.isLightCorrect())
{
return;
}
#else
if (chunk.isUnsaved() || chunk.isUpgrading() || !chunk.isLightCorrect())
{
return;
}
#endif
// biome validation //
// some chunks may be missing their biomes, which cause issues when attempting to save them
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
if (chunk.getBiomes() == null)
{
return;
}
#else
try
{
// this will throw an exception if the biomes aren't set up
chunk.getNoiseBiome(0,0,0);
}
catch (Exception e)
{
return;
}
#endif
// submit the update event
ServerApi.INSTANCE.serverChunkSaveEvent(
new ChunkWrapper(chunk, ServerLevelWrapper.getWrapper(level)),
ServerLevelWrapper.getWrapper(level)
);
}
}
@@ -22,8 +22,7 @@ package com.seibel.distanthorizons.common.wrappers;
import com.seibel.distanthorizons.common.wrappers.gui.ClassicConfigGUI;
import com.seibel.distanthorizons.common.wrappers.gui.LangWrapper;
import com.seibel.distanthorizons.common.wrappers.level.KeyedClientLevelManager;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftGLWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftDedicatedServerWrapper;
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui;
import com.seibel.distanthorizons.core.wrapperInterfaces.config.ILangWrapper;
@@ -33,7 +32,6 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
@@ -62,7 +60,7 @@ public class DependencySetup
//@Environment(EnvType.SERVER)
public static void createServerBindings()
{
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftServerWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftDedicatedServerWrapper.INSTANCE);
}
//@Environment(EnvType.CLIENT)
@@ -71,7 +69,6 @@ public class DependencySetup
SingletonInjector.INSTANCE.bind(IMinecraftClientWrapper.class, MinecraftClientWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftClientWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IMinecraftGLWrapper.class, MinecraftGLWrapper.INSTANCE);
SingletonInjector.INSTANCE.bind(IConfigGui.class, ClassicConfigGUI.CONFIG_CORE_INTERFACE);
}
@@ -25,12 +25,7 @@ public class DependencySetupDoneCheck
{
// TODO move to DependencySetup
public static boolean isDone = false;
/**
* This is used so we can override some MC logic when running
* in DH's world generator.
* Specifically so we can redirect threads to run on DH threads instead
* of MC threads.
*/
public static Supplier<Boolean> getIsCurrentThreadDistantGeneratorThread = (() -> { return false; });
// TODO why is this here and what is its purpose?
public static Supplier<Boolean> getIsCurrentThreadDistantGeneratorThread = (() -> false);
}
@@ -20,9 +20,11 @@
package com.seibel.distanthorizons.common.wrappers;
import java.nio.FloatBuffer;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.math.Mat4f;
@@ -100,31 +102,15 @@ public class McObjectConverter
lodDirections = new EDhDirection[lodDirs.length];
for (EDhDirection lodDir : lodDirs)
{
Direction dir;
switch (lodDir.name().toUpperCase())
{
case "DOWN":
dir = Direction.DOWN;
break;
case "UP":
dir = Direction.UP;
break;
case "NORTH":
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;
}
Direction dir = switch (lodDir.name().toUpperCase()) {
case "DOWN" -> Direction.DOWN;
case "UP" -> Direction.UP;
case "NORTH" -> Direction.NORTH;
case "SOUTH" -> Direction.SOUTH;
case "WEST" -> Direction.WEST;
case "EAST" -> Direction.EAST;
default -> null;
};
if (dir == null)
{
@@ -135,10 +121,41 @@ public class McObjectConverter
}
}
public static BlockPos Convert(DhBlockPos wrappedPos) { return new BlockPos(wrappedPos.getX(), wrappedPos.getY(), wrappedPos.getZ()); }
public static ChunkPos Convert(DhChunkPos wrappedPos) { return new ChunkPos(wrappedPos.getX(), wrappedPos.getZ()); }
public static BlockPos Convert(DhBlockPos wrappedPos)
{
return new BlockPos(wrappedPos.x, wrappedPos.y, wrappedPos.z);
}
public static ChunkPos Convert(DhChunkPos wrappedPos)
{
return new ChunkPos(wrappedPos.x, wrappedPos.z);
}
public static Direction Convert(EDhDirection lodDirection) { return directions[lodDirection.ordinal()]; }
public static EDhDirection Convert(Direction direction) { return lodDirections[direction.ordinal()]; }
public static Direction Convert(EDhDirection lodDirection)
{
return directions[lodDirection.ordinal()];
}
public static EDhDirection Convert(Direction direction)
{
return lodDirections[direction.ordinal()];
}
public static void DebugCheckAllPackers()
{
BiConsumer<Integer, Integer> func = (x, z) -> DhChunkPos._DebugCheckPacker(x, z, ChunkPos.asLong(x, z));
func.accept(0, 0);
func.accept(12345, 134);
func.accept(-12345, -134);
func.accept(-30000000 / 16, 30000000 / 16);
func.accept(30000000 / 16, -30000000 / 16);
func.accept(30000000 / 16, 30000000 / 16);
func.accept(-30000000 / 16, -30000000 / 16);
Consumer<BlockPos> func2 = (p) -> DhBlockPos._DebugCheckPacker(p.getX(), p.getY(), p.getZ(), p.asLong());
func2.accept(new BlockPos(0, 0, 0));
func2.accept(new BlockPos(12345, 134, 123));
func2.accept(new BlockPos(-12345, -134, -80));
func2.accept(new BlockPos(-30000000, 2047, 30000000));
func2.accept(new BlockPos(30000000, -2048, -30000000));
func2.accept(new BlockPos(30000000, 2047, 30000000));
func2.accept(new BlockPos(-30000000, -2048, -30000000));
}
}
@@ -155,41 +155,60 @@ public class WrapperFactory implements IWrapperFactory
}
}
//#if MC_VER <= MC_1_XX_X
#if MC_VER <= MC_1_21
else if (objectArray.length == 2)
{
// correct number of parameters from the API
// chunk
if (!(objectArray[0] instanceof ChunkAccess))
if (!(objectArray[0] instanceof ChunkAccess chunk))
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
ChunkAccess chunk = (ChunkAccess) objectArray[0];
// level / light source
if (!(objectArray[1] instanceof Level))
if (!(objectArray[1] instanceof Level level))
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
// the level is needed for the DH level wrapper...
Level level = (Level) objectArray[1];
// ...the LevelReader is needed for chunk lighting
LevelReader lightSource = level;
// level wrapper
ILevelWrapper levelWrapper = level.isClientSide()
? ClientLevelWrapper.getWrapper((ClientLevel)level)
: ServerLevelWrapper.getWrapper((ServerLevel)level);
ILevelWrapper levelWrapper;
if (level instanceof ServerLevel)
{
levelWrapper = ServerLevelWrapper.getWrapper((ServerLevel)level);
}
else if (level instanceof ClientLevel)
{
levelWrapper = ClientLevelWrapper.getWrapper((ClientLevel)level);
}
else
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
return new ChunkWrapper(chunk, levelWrapper);
return new ChunkWrapper(chunk, lightSource, levelWrapper);
}
// incorrect number of parameters from the API
else
{
throw new ClassCastException(createChunkWrapperErrorMessage(objectArray));
}
//#endif
#else
// Intentional compiler error to bring attention to the missing wrapper function.
// If you need to work on an unimplemented version but don't have the ability to implement this yet
// you can comment it out, but please don't commit it. Someone will have to implement it.
// After implementing the new version please read this method's javadocs for instructions
// on what other locations also need to be updated, the DhAPI specifically needs to
// be updated to state which objects this method accepts.
not implemented for this version of Minecraft!
#endif
}
/**
* Note: when this is updated for different MC versions,
@@ -199,13 +218,16 @@ public class WrapperFactory implements IWrapperFactory
{
String[] expectedClassNames;
//#if MC_VER <= MC_1_XX_X
#if MC_VER <= MC_1_21
expectedClassNames = new String[]
{
ChunkAccess.class.getName(),
"[ServerLevel] or [ClientLevel]" // Classes are not referenced by names to avoid exception when one of them is missing
ServerLevel.class.getName() + "] or [" + ClientLevel.class.getName()
};
//#endif
#else
// See preprocessor comment in createChunkWrapper() for full documentation
not implemented for this version of Minecraft!
#endif
return createWrapperErrorMessage("Chunk wrapper", expectedClassNames, objectArray);
}
@@ -221,11 +243,10 @@ public class WrapperFactory implements IWrapperFactory
public IDhApiBiomeWrapper getBiomeWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper)
{
// confirm the API level wrapper is also a Core wrapper
if (!(levelWrapper instanceof ILevelWrapper))
if (!(levelWrapper instanceof ILevelWrapper coreLevelWrapper))
{
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;
@@ -244,7 +265,7 @@ public class WrapperFactory implements IWrapperFactory
Biome biome = (Biome) objectArray[0];
return BiomeWrapper.getBiomeWrapper(biome, coreLevelWrapper);
#else
#elif MC_VER <= MC_1_21
if (!(objectArray[0] instanceof Holder) || !(((Holder<?>) objectArray[0]).value() instanceof Biome))
{
throw new ClassCastException(createBiomeWrapperErrorMessage(objectArray));
@@ -252,6 +273,9 @@ public class WrapperFactory implements IWrapperFactory
Holder<Biome> biomeHolder = (Holder<Biome>) objectArray[0];
return BiomeWrapper.getBiomeWrapper(biomeHolder, coreLevelWrapper);
#else
// See preprocessor comment in createChunkWrapper() for full documentation (not a typo, check createChunkWrapper()'s else statement for full documentation)
not implemented for this version of Minecraft!
#endif
}
/**
@@ -264,8 +288,11 @@ public class WrapperFactory implements IWrapperFactory
#if MC_VER < MC_1_18_2
expectedClassNames = new String[] { Biome.class.getName() };
#else
#elif MC_VER <= MC_1_21
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
#else
// See preprocessor comment in createChunkWrapper() for full documentation
not implemented for this version of Minecraft!
#endif
return createWrapperErrorMessage("Biome wrapper", expectedClassNames, objectArray);
@@ -274,27 +301,28 @@ public class WrapperFactory implements IWrapperFactory
public IDhApiBlockStateWrapper getBlockStateWrapper(Object[] objectArray, IDhApiLevelWrapper levelWrapper)
{
// confirm the API level wrapper is also a Core wrapper
if (!(levelWrapper instanceof ILevelWrapper))
if (!(levelWrapper instanceof ILevelWrapper coreLevelWrapper))
{
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;
//#if MC_VER <= MC_1_XX_X
#if MC_VER <= MC_1_21
if (objectArray.length != 1)
{
throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray));
}
if (!(objectArray[0] instanceof BlockState))
if (!(objectArray[0] instanceof BlockState blockState))
{
throw new ClassCastException(createBlockStateWrapperErrorMessage(objectArray));
}
BlockState blockState = (BlockState) objectArray[0];
return BlockStateWrapper.fromBlockState(blockState, coreLevelWrapper);
//#endif
#else
// See preprocessor comment in createChunkWrapper() for full documentation (not a typo, check createChunkWrapper()'s else statement for full documentation)
not implemented for this version of Minecraft!
#endif
}
/**
* Note: when this is updated for different MC versions,
@@ -306,8 +334,11 @@ public class WrapperFactory implements IWrapperFactory
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
expectedClassNames = new String[] { Biome.class.getName() };
#else
#elif MC_VER <= MC_1_21
expectedClassNames = new String[] { Holder.class.getName()+"<"+Biome.class.getName()+">" };
#else
// See preprocessor comment in createChunkWrapper() for full documentation
not implemented for this version of Minecraft!
#endif
return createWrapperErrorMessage("BlockState wrapper", expectedClassNames, objectArray);
@@ -22,7 +22,6 @@ package com.seibel.distanthorizons.common.wrappers.block;
import java.io.IOException;
import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
@@ -227,10 +226,8 @@ public class BiomeWrapper implements IBiomeWrapper
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome);
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome.value());
#elif MC_VER < MC_1_21_3
resourceLocation = registryAccess.registryOrThrow(Registries.BIOME).getKey(this.biome.value());
#else
resourceLocation = registryAccess.lookupOrThrow(Registries.BIOME).getKey(this.biome.value());
resourceLocation = registryAccess.registryOrThrow(Registries.BIOME).getKey(this.biome.value());
#endif
if (resourceLocation == null)
@@ -269,7 +266,7 @@ public class BiomeWrapper implements IBiomeWrapper
}
return EMPTY_WRAPPER;
}
else if (resourceLocationString.trim().isEmpty() || resourceLocationString.equals(""))
else if (resourceLocationString.trim().isEmpty() || resourceLocationString.isEmpty())
{
LOGGER.warn("Null biome string deserialized.");
return EMPTY_WRAPPER;
@@ -296,7 +293,7 @@ public class BiomeWrapper implements IBiomeWrapper
ResourceLocation resourceLocation;
try
{
#if MC_VER < MC_1_21_1
#if MC_VER < MC_1_21
resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
#else
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
@@ -321,24 +318,10 @@ public class BiomeWrapper implements IBiomeWrapper
Biome unwrappedBiome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
success = (unwrappedBiome != null);
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#elif MC_VER < MC_1_21_3
#else
Biome unwrappedBiome = registryAccess.registryOrThrow(Registries.BIOME).get(resourceLocation);
success = (unwrappedBiome != null);
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
#else
Holder<Biome> biome;
Optional<Holder.Reference<Biome>> optionalBiomeHolder = registryAccess.lookupOrThrow(Registries.BIOME).get(resourceLocation);
if (optionalBiomeHolder.isPresent())
{
Biome unwrappedBiome = optionalBiomeHolder.get().value();
success = (unwrappedBiome != null);
biome = new Holder.Direct<>(unwrappedBiome);
}
else
{
success = false;
biome = null;
}
#endif
@@ -30,11 +30,11 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.level.block.BeaconBeamBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.SoundType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.Property;
import org.apache.logging.log4j.Logger;
import java.awt.*;
@@ -54,11 +54,11 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.Registry;
import net.minecraft.world.level.EmptyBlockGetter;
#else
import net.minecraft.client.Minecraft;
import net.minecraft.world.level.Level;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.EmptyBlockGetter;
import net.minecraft.core.Holder;
#endif
public class BlockStateWrapper implements IBlockStateWrapper
@@ -104,8 +104,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
private final boolean isBeaconBlock;
private final boolean isBeaconBaseBlock;
/** null if this block can't tint beacons */
private final Color beaconTintColor;
private final boolean isGlassBlock;
private final Color mapColor;
@@ -134,26 +133,6 @@ public class BlockStateWrapper implements IBlockStateWrapper
}
}
/**
* Can be faster than {@link BlockStateWrapper#fromBlockState(BlockState, ILevelWrapper)}
* in cases where the same block state is expected to be referenced multiple times.
*/
public static BlockStateWrapper fromBlockState(BlockState blockState, ILevelWrapper levelWrapper, IBlockStateWrapper guess)
{
BlockState guessBlockState = (guess == null || guess.isAir()) ? null : (BlockState) guess.getWrappedMcObject();
BlockState inputBlockState = (blockState == null || blockState.isAir()) ? null : blockState;
if (guess instanceof BlockStateWrapper
&& guessBlockState == inputBlockState)
{
return (BlockStateWrapper) guess;
}
else
{
return fromBlockState(blockState, levelWrapper);
}
}
private BlockStateWrapper(BlockState blockState, ILevelWrapper levelWrapper)
{
this.blockState = blockState;
@@ -161,7 +140,6 @@ public class BlockStateWrapper implements IBlockStateWrapper
this.hashCode = Objects.hash(this.serialString);
this.blockMaterialId = this.calculateEDhApiBlockMaterialId().index;
// beacon blocks
String lowercaseSerial = this.serialString.toLowerCase();
boolean isBeaconBaseBlock = false;
for (int i = 0; i < LodUtil.BEACON_BASE_BLOCK_NAME_LIST.size(); i++)
@@ -175,28 +153,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
}
this.isBeaconBaseBlock = isBeaconBaseBlock;
this.isBeaconBlock = lowercaseSerial.contains("minecraft:beacon");
// beacon tint color
Color beaconTintColor = null;
if (this.blockState != null
// beacon blocks also show up here, but since they block the beacon beam we don't want their color
&& !this.isBeaconBlock)
{
Block block = this.blockState.getBlock();
if (block instanceof BeaconBeamBlock)
{
int colorInt;
#if MC_VER <= MC_1_19_4
colorInt = ((BeaconBeamBlock) block).getColor().getMaterialColor().col;
#else
colorInt = ((BeaconBeamBlock) block).getColor().getMapColor().col;
#endif
beaconTintColor = ColorUtil.toColorObjRGB(colorInt);
}
}
this.beaconTintColor = beaconTintColor;
this.isGlassBlock = lowercaseSerial.contains("glass");
int mcColor = 0;
if (this.blockState != null)
@@ -236,7 +193,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
HashSet<String> baseIgnoredBlock = new HashSet<>();
baseIgnoredBlock.add(AIR_STRING);
rendererIgnoredBlocks = getBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderBlockCsv, baseIgnoredBlock, levelWrapper);
rendererIgnoredBlocks = getBlockWrappers(Config.Client.Advanced.LodBuilding.ignoredRenderBlockCsv, baseIgnoredBlock, levelWrapper);
return rendererIgnoredBlocks;
}
/**
@@ -253,7 +210,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
HashSet<String> baseIgnoredBlock = new HashSet<>();
baseIgnoredBlock.add(AIR_STRING);
rendererIgnoredCaveBlocks = getBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderCaveBlockCsv, baseIgnoredBlock, levelWrapper);
rendererIgnoredCaveBlocks = getBlockWrappers(Config.Client.Advanced.LodBuilding.ignoredRenderCaveBlockCsv, baseIgnoredBlock, levelWrapper);
return rendererIgnoredCaveBlocks;
}
@@ -277,7 +234,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
String ignoreBlockCsv = config.get();
if (ignoreBlockCsv != null)
{
blockStringList.addAll(Arrays.asList(ignoreBlockCsv.split(",")));
blockStringList.addAll(List.of(ignoreBlockCsv.split(",")));
}
return getBlockWrappers(blockStringList, levelWrapper);
@@ -363,11 +320,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
// +1 to indicate that the block is translucent (in between transparent and opaque)
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT + 1;
}
#if MC_VER < MC_1_21_3
else if (this.blockState.propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO))
#else
else if (this.blockState.propagatesSkylightDown())
#endif
{
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
}
@@ -420,11 +373,6 @@ public class BlockStateWrapper implements IBlockStateWrapper
@Override
public boolean isSolid()
{
if (this.isAir())
{
return false;
}
#if MC_VER < MC_1_20_1
return this.blockState.getMaterial().isSolid();
#else
@@ -452,12 +400,10 @@ public class BlockStateWrapper implements IBlockStateWrapper
@Override
public boolean isBeaconBaseBlock() { return this.isBeaconBaseBlock; }
@Override
public boolean isBeaconTintBlock() { return this.beaconTintColor != null; }
public boolean isGlassBlock() { return this.isGlassBlock; }
@Override
public Color getMapColor() { return this.mapColor; }
@Override
public Color getBeaconTintColor() { return this.beaconTintColor; }
@Override
public byte getMaterialId() { return this.blockMaterialId; }
@@ -491,10 +437,8 @@ public class BlockStateWrapper implements IBlockStateWrapper
resourceLocation = Registry.BLOCK.getKey(this.blockState.getBlock());
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(this.blockState.getBlock());
#elif MC_VER < MC_1_21_3
resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
#else
resourceLocation = registryAccess.lookupOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
#endif
@@ -518,7 +462,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
// we need the final string for the concurrent hash map later
final String finalResourceStateString = resourceStateString;
if (finalResourceStateString.equals(AIR_STRING) || finalResourceStateString.equals("")) // the empty string shouldn't normally happen, but just in case
if (finalResourceStateString.equals(AIR_STRING) || finalResourceStateString.isEmpty()) // the empty string shouldn't normally happen, but just in case
{
return AIR;
}
@@ -555,7 +499,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
ResourceLocation resourceLocation;
try
{
#if MC_VER < MC_1_21_1
#if MC_VER < MC_1_21
resourceLocation = new ResourceLocation(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
#else
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
@@ -573,8 +517,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
{
#if MC_VER > MC_1_17_1
LodUtil.assertTrue(levelWrapper != null && levelWrapper.getWrappedMcObject() != null);
Level level = (Level)levelWrapper.getWrappedMcObject();
// use the given level if possible, otherwise try using the currently loaded one
Level level = (levelWrapper != null ? (Level) levelWrapper.getWrappedMcObject() : null);
level = (level == null ? Minecraft.getInstance().level : level);
#endif
Block block;
@@ -583,13 +528,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
block = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).get(resourceLocation);
#elif MC_VER < MC_1_21_3
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
block = registryAccess.registryOrThrow(Registries.BLOCK).get(resourceLocation);
#else
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
Optional<Holder.Reference<Block>> optionalBlockHolder = registryAccess.lookupOrThrow(Registries.BLOCK).get(resourceLocation);
block = optionalBlockHolder.isPresent() ? optionalBlockHolder.get().value() : null;
block = registryAccess.registryOrThrow(Registries.BLOCK).get(resourceLocation);
#endif
@@ -662,7 +603,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
// 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);
sortedBlockPropteryList.sort((a, b) -> a.getName().compareTo(b.getName()));
sortedBlockPropteryList.sort(Comparator.comparing(Property::getName));
StringBuilder stringBuilder = new StringBuilder();
@@ -1,485 +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;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.Direction;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.FlowerBlock;
import net.minecraft.world.level.block.LeavesBlock;
import net.minecraft.world.level.block.RotatedPillarBlock;
#if MC_VER >= MC_1_19_2
import net.minecraft.util.RandomSource;
#else
import java.util.Random;
#endif
import net.minecraft.world.level.block.state.BlockState;
import org.apache.logging.log4j.Logger;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
/**
* This stores and calculates the colors
* the given {@link BlockState} should have based
* on the given {@link IClientLevelWrapper}.
*
* @see ColorUtil
*/
public class ClientBlockStateColorCache
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = 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
private static final Random RANDOM = new Random(0);
#else
/** Note: this object isn't thread safe and must be put in a lock */
private static final RandomSource RANDOM = RandomSource.create();
#endif
private final IClientLevelWrapper levelWrapper;
private final BlockState blockState;
private final LevelReader level;
private boolean isColorResolved = false;
private int baseColor = 0;
private boolean needShade = true;
private boolean needPostTinting = false;
private int tintIndex = 0;
//===========//
// constants //
//===========//
private static final int MIN_SRGB_BITS = 0x39000000; // 2^(-13)
private static final int MAX_SRGB_BITS = 0x3f7fffff; // 1.0 - f32::EPSILON
private static final float MIN_SRGB_BOUND = Float.intBitsToFloat(MIN_SRGB_BITS);
private static final float MAX_SRGB_BOUND = Float.intBitsToFloat(MAX_SRGB_BITS);
private static final int[] linearToSrgbTable = new int[]
{
0x0073000d, 0x007a000d, 0x0080000d, 0x0087000d, 0x008d000d, 0x0094000d, 0x009a000d, 0x00a1000d,
0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067,
0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5,
0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2,
0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143,
0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af,
0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240,
0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300,
0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
};
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.0047769533f, 0.005181517f, 0.0056053917f, 0.0060488326f, 0.006512091f, 0.00699541f, 0.0074990317f,
0.008023192f, 0.008568125f, 0.009134057f, 0.009721218f, 0.010329823f, 0.010960094f, 0.011612245f,
0.012286487f, 0.012983031f, 0.013702081f, 0.014443844f, 0.015208514f, 0.015996292f, 0.016807375f,
0.017641952f, 0.018500218f, 0.019382361f, 0.020288562f, 0.02121901f, 0.022173883f, 0.023153365f,
0.02415763f, 0.025186857f, 0.026241222f, 0.027320892f, 0.028426038f, 0.029556843f, 0.03071345f, 0.03189604f,
0.033104774f, 0.03433981f, 0.035601325f, 0.036889452f, 0.038204376f, 0.039546248f, 0.04091521f, 0.042311423f,
0.043735042f, 0.045186214f, 0.046665095f, 0.048171833f, 0.049706575f, 0.051269468f, 0.052860655f, 0.05448028f,
0.056128494f, 0.057805434f, 0.05951124f, 0.06124607f, 0.06301003f, 0.06480328f, 0.06662595f, 0.06847818f,
0.07036011f, 0.07227186f, 0.07421358f, 0.07618539f, 0.07818743f, 0.08021983f, 0.082282715f, 0.084376216f,
0.086500466f, 0.088655606f, 0.09084173f, 0.09305898f, 0.095307484f, 0.09758736f, 0.09989874f, 0.10224175f,
0.10461649f, 0.10702311f, 0.10946172f, 0.111932434f, 0.11443538f, 0.116970696f, 0.11953845f, 0.12213881f,
0.12477186f, 0.12743773f, 0.13013652f, 0.13286836f, 0.13563336f, 0.13843165f, 0.14126332f, 0.1441285f,
0.1470273f, 0.14995982f, 0.15292618f, 0.1559265f, 0.15896086f, 0.16202943f, 0.16513224f, 0.16826946f,
0.17144115f, 0.17464745f, 0.17788847f, 0.1811643f, 0.18447503f, 0.1878208f, 0.19120172f, 0.19461787f,
0.19806935f, 0.2015563f, 0.20507877f, 0.2086369f, 0.21223079f, 0.21586053f, 0.21952623f, 0.22322798f,
0.22696589f, 0.23074007f, 0.23455065f, 0.23839766f, 0.2422812f, 0.2462014f, 0.25015837f, 0.25415218f,
0.2581829f, 0.26225072f, 0.26635566f, 0.27049786f, 0.27467737f, 0.27889434f, 0.2831488f, 0.2874409f,
0.2917707f, 0.29613832f, 0.30054384f, 0.30498737f, 0.30946895f, 0.31398875f, 0.31854683f, 0.32314324f,
0.32777813f, 0.33245158f, 0.33716366f, 0.34191445f, 0.3467041f, 0.3515327f, 0.35640025f, 0.36130688f,
0.3662527f, 0.37123778f, 0.37626222f, 0.3813261f, 0.38642952f, 0.39157256f, 0.3967553f, 0.40197787f,
0.4072403f, 0.4125427f, 0.41788515f, 0.42326775f, 0.42869055f, 0.4341537f, 0.43965724f, 0.44520125f,
0.45078585f, 0.45641106f, 0.46207705f, 0.46778384f, 0.47353154f, 0.47932023f, 0.48514998f, 0.4910209f,
0.49693304f, 0.5028866f, 0.50888145f, 0.5149178f, 0.5209957f, 0.52711535f, 0.5332766f, 0.5394797f,
0.5457247f, 0.5520116f, 0.5583406f, 0.5647117f, 0.57112503f, 0.57758063f, 0.5840786f, 0.590619f, 0.597202f,
0.60382754f, 0.61049575f, 0.61720675f, 0.62396055f, 0.63075733f, 0.637597f, 0.6444799f, 0.6514058f,
0.65837497f, 0.66538745f, 0.67244333f, 0.6795426f, 0.68668544f, 0.69387203f, 0.70110214f, 0.70837605f,
0.7156938f, 0.72305536f, 0.730461f, 0.7379107f, 0.7454045f, 0.75294244f, 0.76052475f, 0.7681514f, 0.77582246f,
0.78353804f, 0.79129815f, 0.79910296f, 0.8069525f, 0.8148468f, 0.822786f, 0.8307701f, 0.83879924f, 0.84687346f,
0.8549928f, 0.8631574f, 0.87136734f, 0.8796226f, 0.8879232f, 0.89626956f, 0.90466136f, 0.913099f, 0.92158204f,
0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f
};
//=============//
// constructor //
//=============//
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper samplingLevel)
{
this.blockState = blockState;
this.levelWrapper = samplingLevel;
this.level = (LevelReader) samplingLevel.getWrappedMcObject();
this.resolveColors();
}
//===================//
// color calculation //
//===================//
private void resolveColors()
{
if (this.isColorResolved)
{
return;
}
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?
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode)
{
int count = 0;
int alpha = 0;
double red = 0;
double green = 0;
double blue = 0;
int tempColor;
// don't render Chiseled blocks.
// Since ColorMode is set per block, you only need to check this once.
if (colorMode != ColorMode.Chisel)
{
// textures normally use u and v instead of x and y
for (int v = 0; v < getTextureHeight(texture); v++)
{
for (int u = 0; u < getTextureWidth(texture); u++)
{
//note: Minecraft color format is: 0xAA BB GG RR
//________ DH mod color format is: 0xAA RR GG BB
//OpenGL RGBA format native order: 0xRR GG BB AA
//_ OpenGL RGBA format Java Order: 0xAA BB GG RR
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v);
int r = (tempColor & 0x000000FF);
int g = (tempColor & 0x0000FF00) >>> 8;
int b = (tempColor & 0x00FF0000) >>> 16;
int a = (tempColor & 0xFF000000) >>> 24;
int scale = 1;
if (colorMode == ColorMode.Leaves)
{
//switch (//FIXME add config option)
// case BLACK:
// a = 255; //simulate black background of fast leaves
// break;
// case IGNORE:
if (a == 0) {
continue; //same long grass
}
else
{
a = 255; //just in case there are semi transparent pixels
}
// break;
// case TRANSPARENT:
// break; //do nothing, let it count towards transparency
}
else if (a == 0 && colorMode != ColorMode.Glass)
{
continue;
}
else if (colorMode == ColorMode.Flower && (g + 25 < b || g + 25 < r))
{
scale = FLOWER_COLOR_SCALE;
}
count += scale;
//apparently alpha is linear
alpha += a * scale;
//gamma correction is complicated
red += srgbToLinearTable[r] * a * scale;
green += srgbToLinearTable[g] * a * scale;
blue += srgbToLinearTable[b] * a * scale;
}
}
}
if (count == 0)
{
// this block is entirely transparent
tempColor = ColorUtil.argbToInt(0, 255, 255, 255);
}
else
{
// determine the average color
tempColor = ColorUtil.argbToInt(
alpha / count,
linearToSrgb((float) (red / (double) alpha)),
linearToSrgb((float) (green / (double) alpha)),
linearToSrgb((float) (blue / (double) alpha)));
}
//check if not missing texture
if (tempColor == ColorUtil.argbToInt(255, 182, 0, 182))
{
//make it not render at all
tempColor = ColorUtil.argbToInt(0, 255, 255, 255);
}
return tempColor;
}
private static int getTextureWidth(TextureAtlasSprite texture)
{
#if MC_VER < MC_1_19_4
return texture.getWidth();
#else
return texture.contents().width();
#endif
}
private static int getTextureHeight(TextureAtlasSprite texture)
{
#if MC_VER < MC_1_19_4
return texture.getHeight();
#else
return texture.contents().height();
#endif
}
/**
* This method was suggested by IMS from the Iris/Sodium team.
* That's where the numbers and code are based.
*/
private static int linearToSrgb(float c)
{
if (!(c > MIN_SRGB_BOUND)) {
c = MIN_SRGB_BOUND;
}
if (c > MAX_SRGB_BOUND) {
c = MAX_SRGB_BOUND;
}
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 getter //
//===============//
public int getColor(BiomeWrapper biome, DhBlockPos pos)
{
// only get the tint if the block needs to be tinted
if (!this.needPostTinting)
{
return this.baseColor;
}
// don't try tinting blocks that don't support our method of tint getting
if (BROKEN_BLOCK_STATES.contains(this.blockState))
{
return this.baseColor;
}
// attempt to get the tint
int tintColor = -1;
try
{
// try to use the fast tint getter logic first
if (!BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
{
try
{
tintColor = Minecraft.getInstance().getBlockColors()
.getColor(this.blockState, new TintWithoutLevelOverrider(biome, this.levelWrapper), McObjectConverter.Convert(pos), this.tintIndex);
}
catch (UnsupportedOperationException e)
{
// this exception generally occurs if the tint requires other blocks besides itself
LOGGER.debug("Unable to use ["+TintWithoutLevelOverrider.class.getSimpleName()+"] to get the block tint for block: [" + this.blockState + "] and biome: [" + biome + "] at pos: " + pos + ". Error: [" + e.getMessage() + "]. Attempting to use backup method...", e);
BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState);
}
}
// use the level logic only if requested
if (BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
{
// this logic can't be used all the time due to it breaking some blocks tinting
// specifically oceans don't render correctly
tintColor = Minecraft.getInstance().getBlockColors()
.getColor(this.blockState, new TintGetterOverrideFast(this.level), McObjectConverter.Convert(pos), this.tintIndex);
}
}
catch (Exception e)
{
// only display the error once per block/biome type to reduce log spam
if (!BROKEN_BLOCK_STATES.contains(this.blockState))
{
LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biome + "] at pos: " + pos + ". Error: ["+e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e);
BROKEN_BLOCK_STATES.add(this.blockState);
}
}
if (tintColor != -1)
{
return ColorUtil.multiplyARGBwithRGB(this.baseColor, tintColor);
}
else
{
// unable to get the tinted color, use the base color instead
return this.baseColor;
}
}
//================//
// 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;
}
}
}
@@ -19,14 +19,8 @@
package com.seibel.distanthorizons.common.wrappers.block;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
#if MC_VER < MC_1_17_1
#elif MC_VER < MC_1_21_3
#else
import com.seibel.distanthorizons.core.util.ColorUtil;
import net.minecraft.client.renderer.texture.SpriteContents;
#endif
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
/**
* For wrapping/utilizing around TextureAtlasSprite
@@ -35,6 +29,15 @@ import net.minecraft.client.renderer.texture.SpriteContents;
*/
public class TextureAtlasSpriteWrapper
{
/**
* This code is from Minecraft Forge
* Which is licensed under the terms of GNU Lesser General Public License
* as published by the Free Software Foundation version 2.1
* of the License.
*
* The code has been modified to use TextureAtlasSprite
*/
public static int getPixelRGBA(TextureAtlasSprite sprite, int frameIndex, int x, int y)
{
#if MC_VER < MC_1_17_1
@@ -48,29 +51,13 @@ public class TextureAtlasSpriteWrapper
y += sprite.animatedTexture.getFrameY(frameIndex) * sprite.height;
}
return sprite.mainImage[0].getPixelRGBA(x, y);
#elif MC_VER < MC_1_21_3
#else
if (sprite.contents().animatedTexture != null)
{
x += sprite.contents().animatedTexture.getFrameX(frameIndex) * sprite.contents().width();
y += sprite.contents().animatedTexture.getFrameY(frameIndex) * sprite.contents().width();
}
return sprite.contents().originalImage.getPixelRGBA(x, y);
#else
SpriteContents content = sprite.contents(); // don't close, otherwise MC will be corrupted and you won't be able to re-access the texture
if (content.animatedTexture != null)
{
x += content.animatedTexture.getFrameX(frameIndex) * content.width();
y += content.animatedTexture.getFrameY(frameIndex) * content.width();
}
int abgr = content.originalImage.getPixel(x, y);
// re-pack the color so we can access it normally
int a = (abgr & 0xFF000000) >>> 24;
int b = (abgr & 0x00FF0000) >>> 16;
int g = (abgr & 0x0000FF00) >>> 8;
int r = (abgr & 0x000000FF);
return ColorUtil.argbToInt(a, r, g, b);
#endif
}
@@ -42,24 +42,15 @@ public class TintGetterOverrideFast implements BlockAndTintGetter
{
LevelReader parent;
//=============//
// constructor //
//=============//
public TintGetterOverrideFast(LevelReader parent) { this.parent = parent; }
//=========//
// methods //
//=========//
public TintGetterOverrideFast(LevelReader parent)
{
this.parent = parent;
}
private Biome _getBiome(BlockPos pos)
{
#if MC_VER >= MC_1_18_2
return this.parent.getBiome(pos).value();
return parent.getBiome(pos).value();
#else
return parent.getBiome(pos);
#endif
@@ -72,123 +63,169 @@ public class TintGetterOverrideFast implements BlockAndTintGetter
public float getShade(Direction direction, boolean bl) { return this.parent.getShade(direction, bl); }
@Override
public LevelLightEngine getLightEngine() { return this.parent.getLightEngine(); }
public LevelLightEngine getLightEngine()
{
return parent.getLightEngine();
}
@Override
public int getBrightness(LightLayer lightLayer, BlockPos blockPos) { return this.parent.getBrightness(lightLayer, blockPos); }
public int getBrightness(LightLayer lightLayer, BlockPos blockPos)
{
return parent.getBrightness(lightLayer, blockPos);
}
@Override
public int getRawBrightness(BlockPos blockPos, int i) { return this.parent.getRawBrightness(blockPos, i); }
public int getRawBrightness(BlockPos blockPos, int i)
{
return parent.getRawBrightness(blockPos, i);
}
@Override
public boolean canSeeSky(BlockPos blockPos) { return this.parent.canSeeSky(blockPos); }
public boolean canSeeSky(BlockPos blockPos)
{
return parent.canSeeSky(blockPos);
}
@Override
@Nullable
public BlockEntity getBlockEntity(BlockPos blockPos) { return this.parent.getBlockEntity(blockPos); }
public BlockEntity getBlockEntity(BlockPos blockPos)
{
return parent.getBlockEntity(blockPos);
}
@Override
public BlockState getBlockState(BlockPos blockPos) { return this.parent.getBlockState(blockPos); }
public BlockState getBlockState(BlockPos blockPos)
{
return parent.getBlockState(blockPos);
}
@Override
public FluidState getFluidState(BlockPos blockPos) { return this.parent.getFluidState(blockPos); }
public FluidState getFluidState(BlockPos blockPos)
{
return parent.getFluidState(blockPos);
}
@Override
public int getLightEmission(BlockPos blockPos) { return this.parent.getLightEmission(blockPos); }
public int getLightEmission(BlockPos blockPos)
{
return parent.getLightEmission(blockPos);
}
#if MC_VER < MC_1_21_3
@Override
public int getMaxLightLevel() { return parent.getMaxLightLevel(); }
#else
#endif
public int getMaxLightLevel()
{
return parent.getMaxLightLevel();
}
@Override
public Stream<BlockState> getBlockStates(AABB aABB)
{ return this.parent.getBlockStates(aABB); }
{
return parent.getBlockStates(aABB);
}
@Override
public BlockHitResult clip(ClipContext clipContext)
{ return this.parent.clip(clipContext); }
{
return parent.clip(clipContext);
}
@Override
@Nullable
public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState)
{ return this.parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState); }
{
return parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
}
@Override
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier)
{ return this.parent.getBlockFloorHeight(voxelShape, supplier); }
{
return parent.getBlockFloorHeight(voxelShape, supplier);
}
@Override
public double getBlockFloorHeight(BlockPos blockPos) { return this.parent.getBlockFloorHeight(blockPos); }
public double getBlockFloorHeight(BlockPos blockPos)
{
return parent.getBlockFloorHeight(blockPos);
}
#if MC_VER < MC_1_21_3
@Override
public int getMaxBuildHeight() { return this.parent.getMaxBuildHeight(); }
#else
@Override
public int getMaxY() { return this.parent.getMaxY(); }
#endif
//==============//
// post MC 1.17 //
//==============//
public int getMaxBuildHeight()
{
return parent.getMaxBuildHeight();
}
#if MC_VER >= MC_1_17_1
@Override
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType)
{ return this.parent.getBlockEntity(blockPos, blockEntityType); }
{
return parent.getBlockEntity(blockPos, blockEntityType);
}
@Override
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext)
{ return this.parent.isBlockInLine(clipBlockStateContext); }
{
return parent.isBlockInLine(clipBlockStateContext);
}
@Override
public int getHeight() { return this.parent.getHeight(); }
#if MC_VER < MC_1_21_3
@Override
public int getMinBuildHeight() { return this.parent.getMinBuildHeight(); }
#else
@Override
public int getMinY() { return this.parent.getMinY(); }
#endif
public int getHeight()
{
return parent.getHeight();
}
@Override
public int getSectionsCount() { return this.parent.getSectionsCount(); }
#if MC_VER < MC_1_21_3
@Override
public int getMinSection() { return this.parent.getMinSection(); }
#else
@Override
public int getMinSectionY() { return BlockAndTintGetter.super.getMinSectionY(); }
#endif
#if MC_VER < MC_1_21_3
@Override
public int getMaxSection() { return this.parent.getMaxSection(); }
#else
@Override
public int getMaxSectionY() { return this.parent.getMaxSectionY(); }
#endif
public int getMinBuildHeight()
{
return parent.getMinBuildHeight();
}
@Override
public boolean isOutsideBuildHeight(BlockPos blockPos) { return this.parent.isOutsideBuildHeight(blockPos); }
public int getSectionsCount()
{
return parent.getSectionsCount();
}
@Override
public boolean isOutsideBuildHeight(int i) { return this.parent.isOutsideBuildHeight(i); }
public int getMinSection()
{
return parent.getMinSection();
}
@Override
public int getSectionIndex(int i) { return this.parent.getSectionIndex(i); }
public int getMaxSection()
{
return parent.getMaxSection();
}
@Override
public int getSectionIndexFromSectionY(int i) { return this.parent.getSectionIndexFromSectionY(i); }
public boolean isOutsideBuildHeight(BlockPos blockPos)
{
return parent.isOutsideBuildHeight(blockPos);
}
@Override
public int getSectionYFromSectionIndex(int i) { return this.parent.getSectionYFromSectionIndex(i); }
public boolean isOutsideBuildHeight(int i)
{
return parent.isOutsideBuildHeight(i);
}
@Override
public int getSectionIndex(int i)
{
return parent.getSectionIndex(i);
}
@Override
public int getSectionIndexFromSectionY(int i)
{
return parent.getSectionIndexFromSectionY(i);
}
@Override
public int getSectionYFromSectionIndex(int i)
{
return parent.getSectionYFromSectionIndex(i);
}
#endif
}
@@ -19,7 +19,6 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.util.LodUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Cursor3D;
import net.minecraft.core.Direction;
@@ -45,28 +44,16 @@ public class TintGetterOverrideSmooth implements BlockAndTintGetter
LevelReader parent;
public int smoothingRange;
//=============//
// constructor //
//=============//
public TintGetterOverrideSmooth(LevelReader parent, int smoothingRange)
{
this.parent = parent;
this.smoothingRange = smoothingRange;
}
//=========//
// methods //
//=========//
private Biome _getBiome(BlockPos pos)
{
#if MC_VER >= MC_1_18_2
return this.parent.getBiome(pos).value();
return parent.getBiome(pos).value();
#else
return parent.getBiome(pos);
#endif
@@ -126,11 +113,8 @@ public class TintGetterOverrideSmooth implements BlockAndTintGetter
@Override
public int getLightEmission(BlockPos blockPos) { return this.parent.getLightEmission(blockPos); }
#if MC_VER < MC_1_21_3
@Override
public int getMaxLightLevel() { return this.parent.getMaxLightLevel(); }
#else
#endif
@Override
public Stream<BlockState> getBlockStates(AABB aABB) { return this.parent.getBlockStates(aABB); }
@@ -151,13 +135,8 @@ public class TintGetterOverrideSmooth implements BlockAndTintGetter
@Override
public double getBlockFloorHeight(BlockPos blockPos) { return this.parent.getBlockFloorHeight(blockPos); }
#if MC_VER < MC_1_21_3
@Override
public int getMaxBuildHeight() { return this.parent.getMaxBuildHeight(); }
#else
@Override
public int getMaxY() { return this.parent.getMaxY(); }
#endif
#if MC_VER >= MC_1_17_1
@Override
@@ -169,32 +148,17 @@ public class TintGetterOverrideSmooth implements BlockAndTintGetter
@Override
public int getHeight() { return this.parent.getHeight(); }
#if MC_VER < MC_1_21_3
@Override
public int getMinBuildHeight() { return this.parent.getMinBuildHeight(); }
#else
@Override
public int getMinY() { return this.parent.getMinY(); }
#endif
@Override
public int getSectionsCount() { return this.parent.getSectionsCount(); }
#if MC_VER < MC_1_21_3
@Override
public int getMinSection() { return this.parent.getMinSection(); }
#else
@Override
public int getMinSectionY() { return BlockAndTintGetter.super.getMinSectionY(); }
#endif
#if MC_VER < MC_1_21_3
@Override
public int getMaxSection() { return this.parent.getMaxSection(); }
#else
@Override
public int getMaxSectionY() { return this.parent.getMaxSectionY(); }
#endif
@Override
public boolean isOutsideBuildHeight(BlockPos blockPos) { return this.parent.isOutsideBuildHeight(blockPos); }
@@ -132,27 +132,17 @@ public class TintWithoutLevelOverrider implements BlockAndTintGetter
}
//==============//
// post MC 1.17 //
//==============//
#if MC_VER >= MC_1_17_1
@Override
public int getHeight()
{ throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelOverrider. Object is for tinting only."); }
#if MC_VER < MC_1_21_3
{
throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public int getMinBuildHeight()
{ throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelOverrider. Object is for tinting only."); }
#else
@Override
public int getMinY()
{ throw new UnsupportedOperationException("ERROR: getMinY() called on TintWithoutLevelOverrider. Object is for tinting only."); }
#endif
{
throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
#endif
}
@@ -39,24 +39,11 @@ public class TintWithoutLevelSmoothOverrider implements BlockAndTintGetter
final BiomeWrapper biome;
public int smoothingRange;
//=============//
// constructor //
//=============//
public TintWithoutLevelSmoothOverrider(BiomeWrapper biome, int smoothingRange)
{
this.biome = biome;
this.smoothingRange = smoothingRange;
}
//=========//
// methods //
//=========//
@Override
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
{
@@ -70,7 +57,7 @@ public class TintWithoutLevelSmoothOverrider implements BlockAndTintGetter
return biome;
#endif
}
//
// public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver)
// {
// int i = smoothingRange;
@@ -102,43 +89,44 @@ public class TintWithoutLevelSmoothOverrider implements BlockAndTintGetter
@Override
public float getShade(Direction direction, boolean shade)
{ throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
{
throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public LevelLightEngine getLightEngine()
{ throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
{
throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Nullable
@Override
public BlockEntity getBlockEntity(BlockPos pos)
{ throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
{
throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public BlockState getBlockState(BlockPos pos)
{ throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
{
throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public FluidState getFluidState(BlockPos pos)
{ throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
{
throw new UnsupportedOperationException("ERROR: getFluidState() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
//==============//
// post MC 1.17 //
//==============//
#if MC_VER >= MC_1_17_1
@Override
public int getHeight()
{ throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
#if MC_VER < MC_1_21_3
{
throw new UnsupportedOperationException("ERROR: getHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public int getMinBuildHeight()
{ throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
#else
@Override
public int getMinY()
{ throw new UnsupportedOperationException("ERROR: getMinY() called on TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
#endif
{
throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
#endif
}
@@ -0,0 +1,48 @@
/*
* 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);
}
}
@@ -0,0 +1,412 @@
/*
* 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.block.TextureAtlasSpriteWrapper;
import com.seibel.distanthorizons.common.wrappers.block.TintWithoutLevelOverrider;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.block.*;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.block.model.BakedQuad;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.FlowerBlock;
import net.minecraft.world.level.block.LeavesBlock;
import net.minecraft.world.level.block.RotatedPillarBlock;
#if MC_VER >= MC_1_19_2
import net.minecraft.util.RandomSource;
#else
import java.util.Random;
#endif
import net.minecraft.world.level.block.state.BlockState;
import org.apache.logging.log4j.Logger;
import java.util.HashSet;
import java.util.List;
/**
* @version 2022-9-16
*/
public class ClientBlockStateCache
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>();
private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>();
#if MC_VER < MC_1_19_2
public static final Random random = new Random(0);
#else
public static final RandomSource random = RandomSource.create();
#endif
public final IClientLevelWrapper levelWrapper;
public final BlockState blockState;
public final LevelReader level;
public final BlockPos pos;
public ClientBlockStateCache(BlockState blockState, IClientLevelWrapper samplingLevel, DhBlockPos samplingPos)
{
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;
int baseColor = 0; //TODO: Impl per-face color
boolean needShade = true;
boolean needPostTinting = false;
int tintIndex = 0;
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,
0x00a7001a, 0x00b4001a, 0x00c1001a, 0x00ce001a, 0x00da001a, 0x00e7001a, 0x00f4001a, 0x0101001a,
0x010e0033, 0x01280033, 0x01410033, 0x015b0033, 0x01750033, 0x018f0033, 0x01a80033, 0x01c20033,
0x01dc0067, 0x020f0067, 0x02430067, 0x02760067, 0x02aa0067, 0x02dd0067, 0x03110067, 0x03440067,
0x037800ce, 0x03df00ce, 0x044600ce, 0x04ad00ce, 0x051400ce, 0x057b00c5, 0x05dd00bc, 0x063b00b5,
0x06970158, 0x07420142, 0x07e30130, 0x087b0120, 0x090b0112, 0x09940106, 0x0a1700fc, 0x0a9500f2,
0x0b0f01cb, 0x0bf401ae, 0x0ccb0195, 0x0d950180, 0x0e56016e, 0x0f0d015e, 0x0fbc0150, 0x10630143,
0x11070264, 0x1238023e, 0x1357021d, 0x14660201, 0x156601e9, 0x165a01d3, 0x174401c0, 0x182401af,
0x18fe0331, 0x1a9602fe, 0x1c1502d2, 0x1d7e02ad, 0x1ed4028d, 0x201a0270, 0x21520256, 0x227d0240,
0x239f0443, 0x25c003fe, 0x27bf03c4, 0x29a10392, 0x2b6a0367, 0x2d1d0341, 0x2ebe031f, 0x304d0300,
0x31d105b0, 0x34a80555, 0x37520507, 0x39d504c5, 0x3c37048b, 0x3e7c0458, 0x40a8042a, 0x42bd0401,
0x44c20798, 0x488e071e, 0x4c1c06b6, 0x4f76065d, 0x52a50610, 0x55ac05cc, 0x5892058f, 0x5b590559,
0x5e0c0a23, 0x631c0980, 0x67db08f6, 0x6c55087f, 0x70940818, 0x74a007bd, 0x787d076c, 0x7c330723,
};
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.0047769533f, 0.005181517f, 0.0056053917f, 0.0060488326f, 0.006512091f, 0.00699541f, 0.0074990317f,
0.008023192f, 0.008568125f, 0.009134057f, 0.009721218f, 0.010329823f, 0.010960094f, 0.011612245f,
0.012286487f, 0.012983031f, 0.013702081f, 0.014443844f, 0.015208514f, 0.015996292f, 0.016807375f,
0.017641952f, 0.018500218f, 0.019382361f, 0.020288562f, 0.02121901f, 0.022173883f, 0.023153365f,
0.02415763f, 0.025186857f, 0.026241222f, 0.027320892f, 0.028426038f, 0.029556843f, 0.03071345f, 0.03189604f,
0.033104774f, 0.03433981f, 0.035601325f, 0.036889452f, 0.038204376f, 0.039546248f, 0.04091521f, 0.042311423f,
0.043735042f, 0.045186214f, 0.046665095f, 0.048171833f, 0.049706575f, 0.051269468f, 0.052860655f, 0.05448028f,
0.056128494f, 0.057805434f, 0.05951124f, 0.06124607f, 0.06301003f, 0.06480328f, 0.06662595f, 0.06847818f,
0.07036011f, 0.07227186f, 0.07421358f, 0.07618539f, 0.07818743f, 0.08021983f, 0.082282715f, 0.084376216f,
0.086500466f, 0.088655606f, 0.09084173f, 0.09305898f, 0.095307484f, 0.09758736f, 0.09989874f, 0.10224175f,
0.10461649f, 0.10702311f, 0.10946172f, 0.111932434f, 0.11443538f, 0.116970696f, 0.11953845f, 0.12213881f,
0.12477186f, 0.12743773f, 0.13013652f, 0.13286836f, 0.13563336f, 0.13843165f, 0.14126332f, 0.1441285f,
0.1470273f, 0.14995982f, 0.15292618f, 0.1559265f, 0.15896086f, 0.16202943f, 0.16513224f, 0.16826946f,
0.17144115f, 0.17464745f, 0.17788847f, 0.1811643f, 0.18447503f, 0.1878208f, 0.19120172f, 0.19461787f,
0.19806935f, 0.2015563f, 0.20507877f, 0.2086369f, 0.21223079f, 0.21586053f, 0.21952623f, 0.22322798f,
0.22696589f, 0.23074007f, 0.23455065f, 0.23839766f, 0.2422812f, 0.2462014f, 0.25015837f, 0.25415218f,
0.2581829f, 0.26225072f, 0.26635566f, 0.27049786f, 0.27467737f, 0.27889434f, 0.2831488f, 0.2874409f,
0.2917707f, 0.29613832f, 0.30054384f, 0.30498737f, 0.30946895f, 0.31398875f, 0.31854683f, 0.32314324f,
0.32777813f, 0.33245158f, 0.33716366f, 0.34191445f, 0.3467041f, 0.3515327f, 0.35640025f, 0.36130688f,
0.3662527f, 0.37123778f, 0.37626222f, 0.3813261f, 0.38642952f, 0.39157256f, 0.3967553f, 0.40197787f,
0.4072403f, 0.4125427f, 0.41788515f, 0.42326775f, 0.42869055f, 0.4341537f, 0.43965724f, 0.44520125f,
0.45078585f, 0.45641106f, 0.46207705f, 0.46778384f, 0.47353154f, 0.47932023f, 0.48514998f, 0.4910209f,
0.49693304f, 0.5028866f, 0.50888145f, 0.5149178f, 0.5209957f, 0.52711535f, 0.5332766f, 0.5394797f,
0.5457247f, 0.5520116f, 0.5583406f, 0.5647117f, 0.57112503f, 0.57758063f, 0.5840786f, 0.590619f, 0.597202f,
0.60382754f, 0.61049575f, 0.61720675f, 0.62396055f, 0.63075733f, 0.637597f, 0.6444799f, 0.6514058f,
0.65837497f, 0.66538745f, 0.67244333f, 0.6795426f, 0.68668544f, 0.69387203f, 0.70110214f, 0.70837605f,
0.7156938f, 0.72305536f, 0.730461f, 0.7379107f, 0.7454045f, 0.75294244f, 0.76052475f, 0.7681514f, 0.77582246f,
0.78353804f, 0.79129815f, 0.79910296f, 0.8069525f, 0.8148468f, 0.822786f, 0.8307701f, 0.83879924f, 0.84687346f,
0.8549928f, 0.8631574f, 0.87136734f, 0.8796226f, 0.8879232f, 0.89626956f, 0.90466136f, 0.913099f, 0.92158204f,
0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f
};
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)
{
#if MC_VER < MC_1_19_4
return texture.getWidth();
#else
return texture.contents().width();
#endif
}
private static int getHeight(TextureAtlasSprite texture)
{
#if MC_VER < MC_1_19_4
return texture.getHeight();
#else
return texture.contents().height();
#endif
}
//TODO: Perhaps make this not just use the first frame?
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode)
{
int count = 0;
int alpha = 0;
double red = 0;
double green = 0;
double blue = 0;
int tempColor;
//make Chiseled block not render. Since ColorMode is set per block, you only need to check it once
if (colorMode != ColorMode.Chisel)
{
// textures normally use u and v instead of x and y
for (int v = 0; v < getHeight(texture); v++)
{
for (int u = 0; u < getWidth(texture); u++)
{
//note: Minecraft color format is: 0xAA BB GG RR
//________ DH mod color format is: 0xAA RR GG BB
//OpenGL RGBA format native order: 0xRR GG BB AA
//_ OpenGL RGBA format Java Order: 0xAA BB GG RR
tempColor = TextureAtlasSpriteWrapper.getPixelRGBA(texture, 0, u, v);
int r = (tempColor & 0x000000FF);
int g = (tempColor & 0x0000FF00) >>> 8;
int b = (tempColor & 0x00FF0000) >>> 16;
int a = (tempColor & 0xFF000000) >>> 24;
int scale = 1;
if (colorMode == ColorMode.Leaves)
{
//switch (//FIXME add config option)
// case BLACK:
// a = 255; //simulate black background of fast leaves
// break;
// case IGNORE:
if (a == 0) {
continue; //same long grass
}
else
{
a = 255; //just in case there are semi transparent pixels
}
// break;
// case TRANSPARENT:
// break; //do nothing, let it count towards transparency
}
else if (a == 0 && colorMode != ColorMode.Glass)
{
continue;
}
else if (colorMode == ColorMode.Flower && (g + 25 < b || g + 25 < r))
{
scale = FLOWER_COLOR_SCALE;
}
count += scale;
//apparently alpha is linear
alpha += a * scale;
//gamma correction is complicated
red += srgbToLinearTable[r] * a * scale;
green += srgbToLinearTable[g] * a * scale;
blue += srgbToLinearTable[b] * a * scale;
}
}
}
if (count == 0)
// this block is entirely transparent
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
else
{
// determine the average color
tempColor = ColorUtil.rgbToInt(
alpha / count,
linearToSrgb((float) (red / (double) alpha)),
linearToSrgb((float) (green / (double) alpha)),
linearToSrgb((float) (blue / (double) alpha)));
}
//check if not missing texture
if (tempColor == ColorUtil.rgbToInt(255, 182, 0, 182))
{
//make it not render at all
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
}
return tempColor;
}
private static final Direction[] DIRECTION_ORDER = {Direction.UP, Direction.NORTH, Direction.EAST, Direction.WEST, Direction.SOUTH, Direction.DOWN};
private void resolveColors()
{
if (isColorResolved) return;
if (blockState.getFluidState().isEmpty())
{
List<BakedQuad> quads = null;
for (Direction direction : DIRECTION_ORDER)
{
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
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() &&
!(blockState.getBlock() instanceof RotatedPillarBlock && direction == Direction.UP))
break;
} ;
if (quads == null || quads.isEmpty())
{
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
getBlockModel(blockState).getQuads(blockState, null, random);
}
if (quads != null && !quads.isEmpty())
{
needPostTinting = quads.getFirst().isTinted();
needShade = quads.getFirst().isShade();
tintIndex = quads.getFirst().getTintIndex();
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
needPostTinting = true;
needShade = false;
tintIndex = 0;
baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(blockState),
ColorMode.getColorMode(blockState.getBlock()));
}
isColorResolved = true;
}
public int getAndResolveFaceColor(BiomeWrapper biome, DhBlockPos pos)
{
// FIXME: impl per-face colors
// only get the tint if the block needs to be tinted
if (!this.needPostTinting)
{
return this.baseColor;
}
// don't try tinting blocks that don't support our method of tint getting
if (BROKEN_BLOCK_STATES.contains(this.blockState))
{
return this.baseColor;
}
// attempt to get the tint
int tintColor = -1;
try
{
// try to use the fast tint getter logic first
if (!BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
{
try
{
tintColor = Minecraft.getInstance().getBlockColors()
.getColor(this.blockState, new TintWithoutLevelOverrider(biome, this.levelWrapper), McObjectConverter.Convert(pos), this.tintIndex);
}
catch (UnsupportedOperationException e)
{
// this exception generally occurs if the tint requires other blocks besides itself
LOGGER.debug("Unable to use ["+TintWithoutLevelOverrider.class.getSimpleName()+"] to get the block tint for block: [" + this.blockState + "] and biome: [" + biome + "] at pos: " + pos + ". Error: [" + e.getMessage() + "]. Attempting to use backup method...", e);
BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState);
}
}
// use the level logic only if requested
if (BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
{
// this logic can't be used all the time due to it breaking some blocks tinting
// specifically oceans don't render correctly
tintColor = Minecraft.getInstance().getBlockColors()
.getColor(this.blockState, new TintGetterOverrideFast(this.level), McObjectConverter.Convert(pos), this.tintIndex);
}
}
catch (Exception e)
{
// only display the error once per block/biome type to reduce log spam
if (!BROKEN_BLOCK_STATES.contains(this.blockState))
{
LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biome + "] at pos: " + pos + ". Error: ["+e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e);
BROKEN_BLOCK_STATES.add(this.blockState);
}
}
if (tintColor != -1)
{
return ColorUtil.multiplyARGBwithRGB(this.baseColor, tintColor);
}
else
{
// unable to get the tinted color, use the base color instead
return this.baseColor;
}
}
}
@@ -0,0 +1,43 @@
/*
* 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(); }
}
@@ -0,0 +1,104 @@
/*
* 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);
}
}
}
@@ -21,27 +21,29 @@ package com.seibel.distanthorizons.common.wrappers.chunk;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.MutableBlockPosWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IMutableBlockPosWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.client.multiplayer.ClientChunkCache;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.LightLayer;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
import net.minecraft.world.level.chunk.LevelChunk;
import net.minecraft.world.level.levelgen.Heightmap;
import org.apache.logging.log4j.Logger;
import java.util.*;
import java.util.concurrent.ConcurrentLinkedQueue;
#if MC_VER >= MC_1_17_1
import net.minecraft.core.QuartPos;
@@ -65,6 +67,8 @@ import net.minecraft.world.level.chunk.LevelChunkSection;
#if MC_VER >= MC_1_20_1
import net.minecraft.world.level.chunk.LevelChunkSection;
import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.core.SectionPos;
#endif
#if MC_VER <= MC_1_20_4
@@ -79,29 +83,42 @@ public class ChunkWrapper implements IChunkWrapper
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. */
private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(() -> new BlockPos.MutableBlockPos());
private static final ThreadLocal<MutableBlockPosWrapper> MUTABLE_BLOCK_POS_WRAPPER_REF = ThreadLocal.withInitial(() -> new MutableBlockPosWrapper());
private static final ThreadLocal<BlockPos.MutableBlockPos> MUTABLE_BLOCK_POS_REF = ThreadLocal.withInitial(BlockPos.MutableBlockPos::new);
private final ChunkAccess chunk;
private final DhChunkPos chunkPos;
private final LevelReader lightSource;
private final ILevelWrapper wrappedLevel;
private boolean isDhBlockLightCorrect = false;
private boolean isDhSkyLightCorrect = false;
private boolean isDhLightCorrect = false;
/** only used when connected to a dedicated server */
private boolean isMcClientLightingCorrect = false;
private ChunkLightStorage blockLightStorage;
private ChunkLightStorage skyLightStorage;
private ArrayList<DhBlockPos> blockLightPosList = null;
private boolean useDhLighting;
private int minNonEmptyHeight = Integer.MIN_VALUE;
private int maxNonEmptyHeight = Integer.MAX_VALUE;
/** will be null if we are using MC heightmaps */
private final int[][] solidHeightMap;
/** will be null if we are using MC heightmaps */
private final int[][] lightBlockingHeightMap;
private int blockBiomeHashCode = 0;
/**
* Due to vanilla `isClientLightReady()` not being designed for use by a non-render thread, it may return 'true'
* before the light engine has ticked, (right after all light changes is marked by the engine to be processed).
* To fix this, on client-only mode, we mixin-redirect the `isClientLightReady()` so that after the call, it will
* trigger a synchronous update of this flag here on all chunks that are wrapped. <br><br>
*
* Note: Using a static weak hash map to store the chunks that need to be updated, as instance of chunk wrapper
* can be duplicated, with same chunk instance. And the data stored here are all temporary, and thus will not be
* visible when a chunk is re-wrapped later. <br>
* (Also, thread safety done via a reader writer lock)
*/
private static final ConcurrentLinkedQueue<ChunkWrapper> chunksNeedingClientLightUpdating = new ConcurrentLinkedQueue<>();
@@ -109,25 +126,21 @@ public class ChunkWrapper implements IChunkWrapper
// constructor //
//=============//
public ChunkWrapper(ChunkAccess chunk, ILevelWrapper wrappedLevel)
public ChunkWrapper(ChunkAccess chunk, LevelReader lightSource, ILevelWrapper wrappedLevel)
{
this.chunk = chunk;
this.lightSource = lightSource;
this.wrappedLevel = wrappedLevel;
this.chunkPos = new DhChunkPos(chunk.getPos().x, chunk.getPos().z);
// use DH heightmaps if requested
if (Config.Common.LodBuilding.recalculateChunkHeightmaps.get())
{
this.solidHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
this.lightBlockingHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
// TODO is this the best way to differentiate between when we are generating chunks and when MC gave us a chunk?
boolean isDhGeneratedChunk = (this.lightSource.getClass() == DhLitWorldGenRegion.class);
// MC loaded chunks should get their lighting from MC, DH generated chunks should get their lighting from DH
this.useDhLighting = isDhGeneratedChunk;
this.recalculateDhHeightMapsIfNeeded();
}
else
{
this.solidHeightMap = null;
this.lightBlockingHeightMap = null;
}
// FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the relative position validator
chunksNeedingClientLightUpdating.add(this);
}
@@ -148,29 +161,19 @@ public class ChunkWrapper implements IChunkWrapper
}
@Override
public int getInclusiveMinBuildHeight() { return getInclusiveMinBuildHeight(this.chunk); }
public static int getInclusiveMinBuildHeight(ChunkAccess chunk)
public int getMinBuildHeight() { return getMinBuildHeight(this.chunk); }
public static int getMinBuildHeight(ChunkAccess chunk)
{
#if MC_VER < MC_1_17_1
return 0;
#elif MC_VER < MC_1_21_3
return chunk.getMinBuildHeight();
#else
return chunk.getMinY();
return chunk.getMinBuildHeight();
#endif
}
@Override
public int getExclusiveMaxBuildHeight() { return getExclusiveMaxBuildHeight(this.chunk); }
public static int getExclusiveMaxBuildHeight(ChunkAccess chunk)
{
#if MC_VER < MC_1_21_3
return chunk.getMaxBuildHeight();
#else
// +1 since Minecraft made the max value inclusive
return chunk.getMaxY() + 1;
#endif
}
public int getMaxBuildHeight() { return getMaxBuildHeight(this.chunk); }
public static int getMaxBuildHeight(ChunkAccess chunk) { return chunk.getMaxBuildHeight(); }
@Override
public int getMinNonEmptyHeight()
@@ -182,7 +185,7 @@ public class ChunkWrapper implements IChunkWrapper
// default if every section is empty or missing
this.minNonEmptyHeight = this.getInclusiveMinBuildHeight();
this.minNonEmptyHeight = this.getMinBuildHeight();
// determine the lowest empty section (bottom up)
LevelChunkSection[] sections = this.chunk.getSections();
@@ -214,7 +217,7 @@ public class ChunkWrapper implements IChunkWrapper
// default if every section is empty or missing
this.maxNonEmptyHeight = this.getExclusiveMaxBuildHeight();
this.maxNonEmptyHeight = this.getMaxBuildHeight();
// determine the highest empty section (top down)
LevelChunkSection[] sections = this.chunk.getSections();
@@ -247,92 +250,14 @@ public class ChunkWrapper implements IChunkWrapper
return section.hasOnlyAir();
#endif
}
private int getChunkSectionMinHeight(int index) { return (index * 16) + this.getInclusiveMinBuildHeight(); }
private int getChunkSectionMinHeight(int index) { return (index * 16) + this.getMinBuildHeight(); }
/** Will only run if the config says the MC heightmaps shouldn't be trusted. */
public void recalculateDhHeightMapsIfNeeded()
{
// re-calculate the min/max heights for consistency (during world gen these may be wrong)
this.minNonEmptyHeight = Integer.MIN_VALUE;
this.maxNonEmptyHeight = Integer.MAX_VALUE;
// recalculate heightmaps if needed
if (this.solidHeightMap != null)
{
for (int x = 0; x < LodUtil.CHUNK_WIDTH; x++)
{
for (int z = 0; z < LodUtil.CHUNK_WIDTH; z++)
{
int minInclusiveBuildHeight = this.getMinNonEmptyHeight();
// if no blocks are found the height map will be at the bottom of the world
int solidHeight = minInclusiveBuildHeight;
int lightBlockingHeight = minInclusiveBuildHeight;
int y = this.getMaxNonEmptyHeight(); //this.getExclusiveMaxBuildHeight();
IBlockStateWrapper block = this.getBlockState(x, y, z);
while (// go down until we reach the minimum build height
y > minInclusiveBuildHeight
// keep going until we find both height map values
&& (solidHeight == minInclusiveBuildHeight || lightBlockingHeight == minInclusiveBuildHeight))
{
// is this block solid?
if (solidHeight == minInclusiveBuildHeight
&& block.isSolid())
{
solidHeight = y;
}
// is this block light blocking?
if (lightBlockingHeight == minInclusiveBuildHeight
&& block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT)
{
lightBlockingHeight = y;
}
// get the next block down
y--;
block = this.getBlockState(x, y, z);
}
this.solidHeightMap[x][z] = solidHeight;
this.lightBlockingHeightMap[x][z] = lightBlockingHeight;
}
}
}
}
@Override
public int getSolidHeightMapValue(int xRel, int zRel)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(xRel, zRel);
// will be null if we want to use MC heightmaps
if (this.solidHeightMap == null)
{
return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE).getFirstAvailable(xRel, zRel);
}
else
{
return this.solidHeightMap[xRel][zRel];
}
}
public int getSolidHeightMapValue(int xRel, int zRel) { return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE).getFirstAvailable(xRel, zRel); }
@Override
public int getLightBlockingHeightMapValue(int xRel, int zRel)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(xRel, zRel);
if (this.lightBlockingHeightMap == null)
{
return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING).getFirstAvailable(xRel, zRel);
}
else
{
return this.lightBlockingHeightMap[xRel][zRel];
}
}
public int getLightBlockingHeightMapValue(int xRel, int zRel) { return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.MOTION_BLOCKING).getFirstAvailable(xRel, zRel); }
@Override
@@ -369,49 +294,18 @@ public class ChunkWrapper implements IChunkWrapper
blockPos.setY(relY);
blockPos.setZ(relZ);
// TODO copy into pooled array, this isn't thread safe and can cause MC to throw errors if the chunk is loaded
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(blockPos), this.wrappedLevel);
}
@Override
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ, IMutableBlockPosWrapper mcBlockPos, IBlockStateWrapper guess)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
BlockPos.MutableBlockPos pos = (BlockPos.MutableBlockPos)mcBlockPos.getWrappedMcObject();
pos.setX(relX);
pos.setY(relY);
pos.setZ(relZ);
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(pos), this.wrappedLevel, guess);
}
@Override
public IMutableBlockPosWrapper getMutableBlockPosWrapper() { return MUTABLE_BLOCK_POS_WRAPPER_REF.get(); }
@Override
public DhChunkPos getChunkPos() { return this.chunkPos; }
public ChunkAccess getChunk() { return this.chunk; }
public void trySetStatus(ChunkStatus status) { trySetStatus(this.getChunk(), status); }
/** does nothing if the chunk object doesn't support setting it's status */
public static void trySetStatus(ChunkAccess chunk, ChunkStatus status)
{
if (chunk instanceof ProtoChunk)
{
#if MC_VER < MC_1_21_1
((ProtoChunk) chunk).setStatus(status);
#else
((ProtoChunk) chunk).setPersistedStatus(status);
#endif
}
}
public ChunkStatus getStatus() { return getStatus(this.getChunk()); }
public static ChunkStatus getStatus(ChunkAccess chunk)
{
#if MC_VER < MC_1_21_1
#if MC_VER < MC_1_21
return chunk.getStatus();
#else
return chunk.getPersistedStatus();
@@ -434,14 +328,43 @@ public class ChunkWrapper implements IChunkWrapper
//==========//
@Override
public void setIsDhSkyLightCorrect(boolean isDhLightCorrect) { this.isDhSkyLightCorrect = isDhLightCorrect; }
@Override
public void setIsDhBlockLightCorrect(boolean isDhLightCorrect) { this.isDhBlockLightCorrect = isDhLightCorrect; }
public void setIsDhLightCorrect(boolean isDhLightCorrect) { this.isDhLightCorrect = isDhLightCorrect; }
@Override
public boolean isDhBlockLightingCorrect() { return this.isDhBlockLightCorrect; }
public void setUseDhLighting(boolean useDhLighting) { this.useDhLighting = useDhLighting; }
@Override
public boolean isDhSkyLightCorrect() { return this.isDhSkyLightCorrect; }
public boolean isLightCorrect()
{
if (this.useDhLighting)
{
return this.isDhLightCorrect;
}
#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
#else
if (this.chunk instanceof LevelChunk levelChunk)
{
if (levelChunk.getLevel() instanceof ClientLevel)
{
// connected to a server
return this.isMcClientLightingCorrect;
}
else
{
// in a single player world
return this.chunk.isLightCorrect() && levelChunk.loaded;
}
}
else
{
// called when in a single player world and the chunk is a proto chunk (in world gen, and not active)
return this.chunk.isLightCorrect();
}
#endif
}
@Override
@@ -466,8 +389,6 @@ public class ChunkWrapper implements IChunkWrapper
return this.blockLightStorage;
}
public void setBlockLightStorage(ChunkLightStorage lightStorage) { this.blockLightStorage = lightStorage; }
@Override
public void clearDhBlockLighting() { this.getBlockLightStorage().clear(); }
@Override
@@ -482,8 +403,6 @@ public class ChunkWrapper implements IChunkWrapper
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
this.getSkyLightStorage().set(relX, y, relZ, lightValue);
}
@Override
public void clearDhSkyLighting() { this.getSkyLightStorage().clear(); }
private ChunkLightStorage getSkyLightStorage()
{
@@ -496,12 +415,50 @@ public class ChunkWrapper implements IChunkWrapper
public void setSkyLightStorage(ChunkLightStorage lightStorage) { this.skyLightStorage = lightStorage; }
@Override
public int getBlockLight(int relX, int y, int relZ)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
// use the full lighting engine when the chunks are within render distance or the config requests it
if (this.useDhLighting)
{
// DH lighting method
return this.getBlockLightStorage().get(relX, y, relZ);
}
else
{
// note: this returns 0 if the chunk is unload
// MC lighting method
return this.lightSource.getBrightness(LightLayer.BLOCK, new BlockPos(relX + this.getMinBlockX(), y, relZ + this.getMinBlockZ()));
}
}
@Override
public int getSkyLight(int relX, int y, int relZ)
{
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, y, relZ);
// use the full lighting engine when the chunks are within render distance or the config requests it
if (this.useDhLighting)
{
// DH lighting method
return this.getSkyLightStorage().get(relX, y, relZ);
}
else
{
// MC lighting method
return this.lightSource.getBrightness(LightLayer.SKY, new BlockPos(relX + this.getMinBlockX(), y, relZ + this.getMinBlockZ()));
}
}
/**
* FIXME synchronized is necessary for a rare issue where this method is called from two separate threads at the same time
* before the list has finished populating.
*/
@Override
public synchronized ArrayList<DhBlockPos> getWorldBlockLightPosList()
public synchronized ArrayList<DhBlockPos> getBlockLightPosList()
{
// only populate the list once
if (this.blockLightPosList == null)
@@ -516,27 +473,101 @@ public class ChunkWrapper implements IChunkWrapper
});
#else
this.chunk.findBlockLightSources((blockPos, blockState) ->
{
DhBlockPos pos = new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ());
// this can be uncommented if MC decides to return relative block positions in the future instead of world positions
//pos.mutateToChunkRelativePos(pos);
//pos.mutateOffset(this.chunkPos.getMinBlockX(), 0, this.chunkPos.getMinBlockZ(), pos);
this.blockLightPosList.add(pos);
});
this.blockLightPosList.add(new DhBlockPos(blockPos.getX(), blockPos.getY(), blockPos.getZ())));
#endif
}
return this.blockLightPosList;
}
public static void syncedUpdateClientLightStatus()
{
#if MC_VER < MC_1_18_2
// TODO: Check what to do in 1.18.1 and older
// since we don't currently handle this list,
// clear it to prevent memory leaks
chunksNeedingClientLightUpdating.clear();
#else
// update the chunks client lighting
ChunkWrapper chunkWrapper = chunksNeedingClientLightUpdating.poll();
while (chunkWrapper != null)
{
chunkWrapper.updateIsClientLightingCorrect();
chunkWrapper = chunksNeedingClientLightUpdating.poll();
}
#endif
}
/** Should be called after client light updates are triggered. */
private void updateIsClientLightingCorrect()
{
if (this.chunk instanceof LevelChunk levelChunk && ((LevelChunk) this.chunk).getLevel() instanceof ClientLevel)
{
ClientChunkCache clientChunkCache = ((ClientLevel) levelChunk.getLevel()).getChunkSource();
this.isMcClientLightingCorrect = clientChunkCache.getChunkForLighting(this.chunk.getPos().x, this.chunk.getPos().z) != null &&
#if MC_VER <= MC_1_17_1
levelChunk.isLightCorrect();
#elif MC_VER < MC_1_20_1
levelChunk.isClientLightReady();
#else
checkLightSectionsOnChunk(levelChunk, levelChunk.getLevel().getLightEngine());
#endif
}
}
#if MC_VER >= MC_1_20_1
private static boolean checkLightSectionsOnChunk(LevelChunk chunk, LevelLightEngine engine)
{
LevelChunkSection[] sections = chunk.getSections();
int minY = chunk.getMinSection();
int maxY = chunk.getMaxSection();
for (int y = minY; y < maxY; ++y)
{
LevelChunkSection section = sections[chunk.getSectionIndexFromSectionY(y)];
if (section.hasOnlyAir()) continue;
if (!engine.lightOnInSection(SectionPos.of(chunk.getPos(), y)))
{
return false;
}
}
return true;
}
#endif
//===============//
// other methods //
//===============//
@Override
public boolean doNearbyChunksExist()
{
if (this.lightSource instanceof DhLitWorldGenRegion)
{
return true;
}
for (int dx = -1; dx <= 1; dx++)
{
for (int dz = -1; dz <= 1; dz++)
{
if (dx == 0 && dz == 0)
{
continue;
}
else if (this.lightSource.getChunk(dx + this.chunk.getPos().x, dz + this.chunk.getPos().z, ChunkStatus.BIOMES, false) == null)
{
return false;
}
}
}
return true;
}
@Override
public boolean isStillValid() { return this.wrappedLevel.tryGetChunk(this.chunkPos) == this; }
@@ -16,6 +16,7 @@ import java.util.regex.Pattern;
// Logger (for debug stuff)
import com.seibel.distanthorizons.api.enums.config.DisallowSelectingViaConfigGui;
import com.seibel.distanthorizons.api.enums.config.EDhApiUpdateBranch;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigBase;
import com.seibel.distanthorizons.core.config.types.*;
@@ -118,18 +119,14 @@ public class ClassicConfigGUI
*/
private static void textField(AbstractConfigType info, Function<String, Number> func, Pattern pattern, boolean cast)
{
boolean isNumber = pattern != null;
((EntryInfo) info.guiValue).widget = (BiFunction<EditBox, Button, Predicate<String>>) (editBox, button) -> stringValue ->
{
boolean isNumber = (pattern != null);
stringValue = stringValue.trim();
if (!(stringValue.isEmpty() || !isNumber || pattern.matcher(stringValue).matches()))
{
return false;
}
Number value = info.typeIsFloatingPointNumber() ? 0.0 : 0; // different default values are needed so implicit casting works correctly (if not done casting from 0 (an int) to a double will cause an exception)
Number value = 0;
((EntryInfo) info.guiValue).error = null;
if (isNumber && !stringValue.isEmpty() && !stringValue.equals("-") && !stringValue.equals("."))
{
@@ -146,22 +143,18 @@ public class ClassicConfigGUI
switch (isValid)
{
case 0:
((EntryInfo) info.guiValue).error = null;
break;
((EntryInfo) info.guiValue).error = null; break;
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:
((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:
((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;
}
}
((EntryInfo) info.guiValue).tempValue = stringValue;
editBox.setTextColor(((ConfigEntry) info).isValid(value) == 0 ? 0xFFFFFFFF : 0xFFFF7777); // white and red
editBox.setTextColor(((ConfigEntry) info).isValid(value) == 0 ? 0xFFFFFFFF : 0xFFFF7777);
// button.active = entries.stream().allMatch(e -> e.inLimits);
@@ -242,7 +235,7 @@ public class ClassicConfigGUI
ConfigBase.INSTANCE.configFileINSTANCE.saveToFile();
Objects.requireNonNull(this.minecraft).setScreen(this.parent);
CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run());
CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach(Runnable::run);
}
@Override
@@ -266,7 +259,7 @@ public class ClassicConfigGUI
0, 0,
// Some textuary stuff
0,
#if MC_VER < MC_1_21_1
#if MC_VER < MC_1_21
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
#else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
@@ -326,7 +319,7 @@ public class ClassicConfigGUI
CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach((listener) -> listener.run());
CONFIG_CORE_INTERFACE.onScreenChangeListenerList.forEach(Runnable::run);
}
@@ -339,7 +332,7 @@ public class ClassicConfigGUI
if (ConfigEntry.class.isAssignableFrom(info.getClass()))
{
Button.OnPress btnAction = button -> {
((ConfigEntry) info).uiSetWithoutSaving(((ConfigEntry) info).getDefaultValue());
((ConfigEntry) info).uiSetWithoutSaving(((ConfigEntry<?>) info).getDefaultValue());
((EntryInfo) info.guiValue).index = 0;
this.reload = true;
Objects.requireNonNull(minecraft).setScreen(this);
@@ -383,9 +376,7 @@ public class ClassicConfigGUI
}
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);
return;
}
@@ -394,9 +385,9 @@ public class ClassicConfigGUI
this.list.addButton(null, null, null, name);
return;
}
if (ConfigUiLinkedEntry.class.isAssignableFrom(info.getClass()))
if (ConfigLinkedEntry.class.isAssignableFrom(info.getClass()))
{
this.addMenuItem(((ConfigUiLinkedEntry) info).get());
this.addMenuItem(((ConfigLinkedEntry) info).get());
return;
}
@@ -445,8 +436,8 @@ public class ClassicConfigGUI
}
// A quick fix for tooltips on linked entries
AbstractConfigType newInfo = ConfigUiLinkedEntry.class.isAssignableFrom(info.getClass()) ?
((ConfigUiLinkedEntry) info).get() :
AbstractConfigType newInfo = ConfigLinkedEntry.class.isAssignableFrom(info.getClass()) ?
((ConfigLinkedEntry) info).get() :
info;
Component name = Translatable(this.translationPrefix + (info.category.isEmpty() ? "" : info.category + ".") + info.getName());
@@ -479,47 +470,47 @@ public class ClassicConfigGUI
private static void initEntry(AbstractConfigType configType, String translationPrefix)
private static void initEntry(AbstractConfigType info, String translationPrefix)
{
configType.guiValue = new EntryInfo();
Class<?> fieldClass = configType.getType();
info.guiValue = new EntryInfo();
Class<?> fieldClass = info.getType();
if (ConfigEntry.class.isAssignableFrom(configType.getClass()))
if (ConfigEntry.class.isAssignableFrom(info.getClass()))
{
if (fieldClass == Integer.class)
{
// For int
textField(configType, Integer::parseInt, INTEGER_ONLY_REGEX, true);
textField(info, Integer::parseInt, INTEGER_ONLY_REGEX, true);
}
else if (fieldClass == Double.class)
{
// For double
textField(configType, Double::parseDouble, DECIMAL_ONLY_REGEX, false);
textField(info, Double::parseDouble, DECIMAL_ONLY_REGEX, false);
}
else if (fieldClass == String.class || fieldClass == List.class)
{
// For string or list
textField(configType, String::length, null, true);
textField(info, String::length, null, true);
}
else if (fieldClass == Boolean.class)
{
// For boolean
Function<Object, Component> func = value -> Translatable("distanthorizons.general."+((Boolean) value ? "true" : "false")).withStyle((Boolean) value ? ChatFormatting.GREEN : ChatFormatting.RED);
((EntryInfo) configType.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> {
((ConfigEntry) configType).uiSetWithoutSaving(!(Boolean) configType.get());
button.setMessage(func.apply(configType.get()));
((EntryInfo) info.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> {
((ConfigEntry) info).uiSetWithoutSaving(!(Boolean) info.get());
button.setMessage(func.apply(info.get()));
}, func);
}
else if (fieldClass.isEnum())
{
// For enum
List<?> values = Arrays.asList(configType.getType().getEnumConstants());
Function<Object, Component> func = value -> Translatable(translationPrefix + "enum." + fieldClass.getSimpleName() + "." + configType.get().toString());
((EntryInfo) configType.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> {
List<?> values = Arrays.asList(info.getType().getEnumConstants());
Function<Object, Component> func = value -> Translatable(translationPrefix + "enum." + fieldClass.getSimpleName() + "." + info.get().toString());
((EntryInfo) info.guiValue).widget = new AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>>(button -> {
// get the currently selected enum and enum index
int startingIndex = values.indexOf(configType.get());
int startingIndex = values.indexOf(info.get());
Enum<?> enumValue = (Enum<?>) values.get(startingIndex);
// search for the next enum that is selectable
@@ -547,12 +538,12 @@ public class ClassicConfigGUI
}
((ConfigEntry<Enum<?>>) configType).uiSetWithoutSaving(enumValue);
button.setMessage(func.apply(configType.get()));
((ConfigEntry<Enum<?>>) info).uiSetWithoutSaving(enumValue);
button.setMessage(func.apply(info.get()));
}, func);
}
}
else if (ConfigCategory.class.isAssignableFrom(configType.getClass()))
else if (ConfigCategory.class.isAssignableFrom(info.getClass()))
{
// if (!info.info.getName().equals(""))
// info.name = new TranslatableComponent(info.info.getName());
@@ -25,19 +25,18 @@ public class GetConfigScreen
// 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));
switch (useScreen)
return switch (useScreen)
{
case Classic -> ClassicConfigGUI.getScreen(ConfigBase.INSTANCE, parent, "client");
case OpenGL ->
{
case Classic:
return ClassicConfigGUI.getScreen(ConfigBase.INSTANCE, parent, "client");
case OpenGL:
MinecraftScreen.getScreen(parent, new OpenGLConfigScreen(), ModInfo.ID + ".title");
return null;
// case JavaFX -> MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new JavaScreenHandlerScreen.ExampleScreen()), ModInfo.ID + ".title");
case JavaFX:
return MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new ConfigScreen()), ModInfo.ID + ".title");
default:
throw new IllegalArgumentException("No config screen implementation defined for ["+useScreen+"].");
yield null;
}
// 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");
default -> throw new IllegalArgumentException("No config screen implementation defined for [" + useScreen + "].");
};
}
}
@@ -19,37 +19,31 @@
package com.seibel.distanthorizons.common.wrappers.gui;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
#if MC_VER >= MC_1_17_1
import net.minecraft.client.gui.components.Button;
#endif
#if MC_VER < MC_1_17_1
import net.minecraft.client.gui.components.ImageButton;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
#elif MC_VER < MC_1_20_1
import net.minecraft.client.gui.components.ImageButton;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.renderer.GameRenderer;
#elif MC_VER < MC_1_20_2
import net.minecraft.client.gui.components.ImageButton;
import net.minecraft.client.gui.GuiGraphics;
#else
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.RenderType;
#endif
/**
* Creates a button with a texture on it (and a background) that works with all mc versions
*
* @author coolGi
* @version 2023-10-03
*/
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.gui.components.AbstractButton;
import net.minecraft.client.gui.components.Button;
import net.minecraft.client.gui.components.ImageButton;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
#if MC_VER >= MC_1_17_1
import net.minecraft.client.renderer.GameRenderer;
#endif
#if MC_VER < MC_1_20_1
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
#else
import net.minecraft.client.gui.GuiGraphics;
#endif
#if MC_VER < MC_1_20_2
public class TexturedButtonWidget extends ImageButton
#else
@@ -63,21 +57,20 @@ public class TexturedButtonWidget extends Button
private final int v;
private final int hoveredVOffset;
private final ResourceLocation textureResourceLocation;
private final ResourceLocation texture;
private final int textureWidth;
private final int textureHeight;
#endif
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation textureResourceLocation, int textureWidth, int textureHeight, OnPress pressAction, Component text)
{
this(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text, true);
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation texture, int textureWidth, int textureHeight, OnPress pressAction, Component text) {
this(x, y, width, height, u, v, hoveredVOffset, texture, textureWidth, textureHeight, pressAction, text, true);
}
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation textureResourceLocation, int textureWidth, int textureHeight, OnPress pressAction, Component text, boolean renderBackground)
public TexturedButtonWidget(int x, int y, int width, int height, int u, int v, int hoveredVOffset, ResourceLocation texture, int textureWidth, int textureHeight, OnPress pressAction, Component text, boolean renderBackground)
{
#if MC_VER < MC_1_20_2
super(x, y, width, height, u, v, hoveredVOffset, textureResourceLocation, textureWidth, textureHeight, pressAction, text);
super(x, y, width, height, u, v, hoveredVOffset, texture, textureWidth, textureHeight, pressAction, text);
#else
// We don't pass on the text option as otherwise it will render (we normally pass it for narration)
// TODO: Find a fix for it
@@ -87,7 +80,7 @@ public class TexturedButtonWidget extends Button
this.v = v;
this.hoveredVOffset = hoveredVOffset;
this.textureResourceLocation = textureResourceLocation;
this.texture = texture;
this.textureWidth = textureWidth;
this.textureHeight = textureHeight;
@@ -99,8 +92,7 @@ public class TexturedButtonWidget extends Button
#if MC_VER < MC_1_20_2
#if MC_VER < MC_1_19_4
@Override
public void renderButton(PoseStack matrices, int mouseX, int mouseY, float delta)
{
public void renderButton(PoseStack matrices, int mouseX, int mouseY, float delta) {
if (this.renderBackground) // Renders the background of the button
{
#if MC_VER < MC_1_17_1
@@ -127,7 +119,6 @@ public class TexturedButtonWidget extends Button
super.renderButton(matrices, mouseX, mouseY, delta);
}
#else
#if MC_VER < MC_1_20_1
@Override
@@ -163,49 +154,24 @@ public class TexturedButtonWidget extends Button
super.renderWidget(matrices, mouseX, mouseY, delta);
}
#endif
#else
@Override
public void renderWidget(GuiGraphics matrices, int mouseX, int mouseY, float delta)
{
if (this.renderBackground)
{
#if MC_VER < MC_1_21_3
//RenderSystem.enableBlend();
//RenderSystem.enableDepthTest();
matrices.blitSprite(SPRITES.get(this.active, this.isHoveredOrFocused()), this.getX(), this.getY(), this.getWidth(), this.getHeight());
#else
matrices.blitSprite(
RenderType::guiTextured,
SPRITES.get(this.active, this.isHoveredOrFocused()),
this.getX(), this.getY(),
this.getWidth(), this.getHeight());
#endif
}
// Renders the sprite
int i = 0;
if (!this.active)
{
i = 2;
}
else if (this.isHovered)
{
i = 1;
}
if (!this.active) i = 2;
else if (this.isHovered) i = 1;
#if MC_VER < MC_1_21_3
matrices.blit(this.textureResourceLocation, this.getX(), this.getY(), this.u, this.v + (this.hoveredVOffset * i), this.width, this.height, this.textureWidth, this.textureHeight);
#else
matrices.blit(
RenderType::guiTextured,
this.textureResourceLocation,
this.getX(), this.getY(),
this.u, this.v + (this.hoveredVOffset * i),
this.width, this.height,
this.textureWidth, this.textureHeight);
#endif
matrices.blit(this.texture, this.getX(), this.getY(), this.u, this.v + (this.hoveredVOffset * i), this.width, this.height, this.textureWidth, this.textureHeight);
}
#endif
}
@@ -134,9 +134,7 @@ public class ChangelogScreen extends DhScreen
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())
);
@@ -161,28 +159,16 @@ public class ChangelogScreen extends DhScreen
#else
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
#endif
if (!this.usable)
{
if (!usable)
return;
}
int maxScroll;
#if MC_VER <= MC_1_21_3
maxScroll = this.changelogArea.getMaxScroll();
#else
maxScroll = this.changelogArea.maxScrollAmount();
#endif
// Set the scroll position to the mouse height relative to the screen
// This is a bit of a hack as we cannot scroll on this area
double scrollAmount = ((double) mouseY) / ((double) this.height) * 1.1 * maxScroll;
double scrollAmount = ((double) mouseY) / ((double) this.height) * 1.1 * this.changelogArea.getMaxScroll();
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
this.changelogArea.setScrollAmount(scrollAmount);
#elif MC_VER <= MC_1_21_3
this.changelogArea.scrollAmount = scrollAmount;
#else
this.changelogArea.setScrollAmount(scrollAmount);
this.changelogArea.scrollAmount = scrollAmount;
#endif
@@ -36,7 +36,7 @@ public class UpdateModScreen extends DhScreen
private String nextVer;
public UpdateModScreen(Screen parent, String newVersionID) throws IllegalArgumentException
public UpdateModScreen(Screen parent, String newVersionID)
{
super(Translatable(ModInfo.ID + ".updater.title"));
this.parent = parent;
@@ -54,13 +54,6 @@ public class UpdateModScreen extends DhScreen
this.currentVer = ModJarInfo.Git_Commit.substring(0,7);
this.nextVer = this.newVersionID.substring(0,7);
}
// done to prevent trying to update to "null"
// (this can happen if no versions are available to check/download from modrinth/gitlab)
if (this.nextVer == null)
{
throw new IllegalArgumentException("No new version found with the ID ["+newVersionID+"].");
}
}
@Override
@@ -76,19 +69,19 @@ public class UpdateModScreen extends DhScreen
// Logo image
this.addBtn(new TexturedButtonWidget(
// Where the button is on the screen
this.width / 2 - 95, this.height / 2 - 110,
this.width / 2 - 65, this.height / 2 - 110,
// Width and height of the button
195, 65,
130, 65,
// Offset
0, 0,
// Some textuary stuff
0,
#if MC_VER < MC_1_21_1
#if MC_VER < MC_1_21
new ResourceLocation(ModInfo.ID, "logo.png"),
#else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "logo.png"),
#endif
195, 65,
130, 65,
// Create the button and tell it where to go
// For now it goes to the client option by default
(buttonWidget) -> System.out.println("Nice, you found an easter egg :)"), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti)
@@ -114,7 +107,7 @@ public class UpdateModScreen extends DhScreen
0, 0,
// Some textuary stuff
0,
#if MC_VER < MC_1_21_1
#if MC_VER < MC_1_21
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
#else
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
@@ -142,9 +135,7 @@ public class UpdateModScreen extends DhScreen
})
);
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
MakeBtn(Translatable(ModInfo.ID + ".updater.never"), this.width / 2 - 102, this.height / 2 + 70, 100, 20, (btn) -> {
@@ -1,22 +1,21 @@
package com.seibel.distanthorizons.common.wrappers.level;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.level.IKeyedClientLevelManager;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.client.multiplayer.ClientLevel;
import org.jetbrains.annotations.Nullable;
import java.util.Objects;
public class KeyedClientLevelManager implements IKeyedClientLevelManager
{
public static final KeyedClientLevelManager INSTANCE = new KeyedClientLevelManager();
/** This is set and managed by the ClientApi for servers with support for DH. */
@Nullable
private IServerKeyedClientLevel serverKeyedLevel = null;
/** Allows to keep level manager enabled between loading different keyed levels */
private boolean enabled = false;
private IServerKeyedClientLevel overrideWrapper = null;
private boolean useOverrideWrapper = false;
//=============//
@@ -32,24 +31,24 @@ public class KeyedClientLevelManager implements IKeyedClientLevelManager
//======================//
@Override
@Nullable
public IServerKeyedClientLevel getServerKeyedLevel() { return this.serverKeyedLevel; }
public void setServerKeyedLevel(IServerKeyedClientLevel clientLevel) { this.overrideWrapper = clientLevel; }
@Override
public IServerKeyedClientLevel getOverrideWrapper() { return this.overrideWrapper; }
@Override
public IServerKeyedClientLevel setServerKeyedLevel(IClientLevelWrapper clientLevel, String levelKey)
public IServerKeyedClientLevel getServerKeyedLevel(ILevelWrapper level, String serverLevelKey)
{
IServerKeyedClientLevel keyedLevel = new ServerKeyedClientLevel((ClientLevel) clientLevel.getWrappedMcObject(), levelKey);
this.serverKeyedLevel = keyedLevel;
this.enabled = true;
return keyedLevel;
Objects.requireNonNull(level);
Objects.requireNonNull(serverLevelKey);
return new ServerKeyedClientLevel((ClientLevel) level.getWrappedMcObject(), serverLevelKey);
}
@Override
public void clearKeyedLevel() { this.serverKeyedLevel = null; }
public void setUseOverrideWrapper(boolean useOverrideWrapper) { this.useOverrideWrapper = useOverrideWrapper; }
@Override
public boolean isEnabled() { return this.enabled; }
@Override
public void disable() { this.enabled = false; }
public boolean getUseOverrideWrapper() { return this.useOverrideWrapper; }
}
@@ -2,8 +2,6 @@ package com.seibel.distanthorizons.common.wrappers.level;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.coreapi.util.StringUtil;
import net.minecraft.client.multiplayer.ClientLevel;
public class ServerKeyedClientLevel extends ClientLevelWrapper implements IServerKeyedClientLevel
@@ -11,20 +9,13 @@ public class ServerKeyedClientLevel extends ClientLevelWrapper implements IServe
/** A unique identifier (generally the level's name) for differentiating multiverse levels */
private final String serverLevelKey;
public ServerKeyedClientLevel(ClientLevel level, String serverLevelKey)
{
super(level);
this.serverLevelKey = serverLevelKey;
}
@Override
public String getServerLevelKey() { return this.serverLevelKey; }
@Override
public String getDhIdentifier() { return this.getServerLevelKey(); }
}
@@ -22,7 +22,6 @@ package com.seibel.distanthorizons.common.wrappers.minecraft;
import java.io.File;
import java.lang.invoke.MethodHandles;
import java.util.ArrayList;
import java.util.Objects;
import java.util.UUID;
import com.mojang.blaze3d.platform.NativeImage;
@@ -40,30 +39,24 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftCli
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import net.minecraft.CrashReport;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.resources.model.ModelManager;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
#if MC_VER < MC_1_19_2
import net.minecraft.network.chat.TextComponent;
#endif
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.profiling.ProfilerFiller;
import net.minecraft.world.level.ChunkPos;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
#if MC_VER < MC_1_21_3
#else
import net.minecraft.util.profiling.Profiler;
#endif
/**
* A singleton that wraps the Minecraft object.
*
@@ -118,7 +111,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Override
public float getShade(EDhDirection lodDirection)
{
EDhApiLodShading lodShading = Config.Client.Advanced.Graphics.Quality.lodShading.get();
EDhApiLodShading lodShading = Config.Client.Advanced.Graphics.AdvancedGraphics.lodShading.get();
switch (lodShading)
{
default:
@@ -134,20 +127,12 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
}
case ENABLED:
switch (lodDirection)
{
case DOWN:
return 0.5F;
default:
case UP:
return 1.0F;
case NORTH:
case SOUTH:
return 0.8F;
case WEST:
case EAST:
return 0.6F;
}
return switch (lodDirection) {
case DOWN -> 0.5F;
default -> 1.0F;
case NORTH, SOUTH -> 0.8F;
case WEST, EAST -> 0.6F;
};
case DISABLED:
return 1.0F;
@@ -208,9 +193,6 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Override
public UUID getPlayerUUID() { return this.getPlayer().getUUID(); }
@Override
public String getUsername() { return MINECRAFT.getUser().getName(); }
@Override
public DhBlockPos getPlayerBlockPos()
{
@@ -231,38 +213,26 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Nullable
@Override
public IClientLevelWrapper getWrappedClientLevel() { return this.getWrappedClientLevel(false); }
@Override
@Nullable
public IClientLevelWrapper getWrappedClientLevel(boolean bypassLevelKeyManager)
public IClientLevelWrapper getWrappedClientLevel()
{
ClientLevel level = MINECRAFT.level;
if (level == null)
if (MINECRAFT.level == null)
{
return null;
}
return ClientLevelWrapper.getWrapper(level, bypassLevelKeyManager);
return ClientLevelWrapper.getWrapperIgnoringOverride(MINECRAFT.level);
}
@Override
public IProfilerWrapper getProfiler()
{
ProfilerFiller profiler;
#if MC_VER < MC_1_21_3
profiler = MINECRAFT.getProfiler();
#else
profiler = Profiler.get();
#endif
if (this.profilerWrapper == null)
{
this.profilerWrapper = new ProfilerWrapper(profiler);
this.profilerWrapper = new ProfilerWrapper(MINECRAFT.getProfiler());
}
else if (profiler != this.profilerWrapper.profiler)
else if (MINECRAFT.getProfiler() != this.profilerWrapper.profiler)
{
this.profilerWrapper.profiler = profiler;
this.profilerWrapper.profiler = MINECRAFT.getProfiler();
}
return this.profilerWrapper;
@@ -272,7 +242,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Override
public ArrayList<ILevelWrapper> getAllServerWorlds()
{
ArrayList<ILevelWrapper> worlds = new ArrayList<ILevelWrapper>();
ArrayList<ILevelWrapper> worlds = new ArrayList<>();
Iterable<ServerLevel> serverWorlds = MINECRAFT.getSingleplayerServer().getAllLevels();
for (ServerLevel world : serverWorlds)
@@ -289,16 +259,11 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
public void sendChatMessage(String string)
{
LocalPlayer player = this.getPlayer();
if (player == null)
{
return;
}
if (player == null) return;
#if MC_VER < MC_1_19_2
player.sendMessage(new TextComponent(string), getPlayer().getUUID());
#elif MC_VER < MC_1_21_3
player.sendSystemMessage(net.minecraft.network.chat.Component.translatable(string));
#else
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false);
player.sendSystemMessage(net.minecraft.network.chat.Component.translatable(string));
#endif
}
@@ -334,18 +299,4 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Override
public void executeOnRenderThread(Runnable runnable) { MINECRAFT.execute(runnable); }
@Override
public int getPlayerCount()
{
// can be null if the server hasn't finished booting up yet
if (MINECRAFT.getSingleplayerServer() == null)
{
return 1;
}
else
{
return MINECRAFT.getSingleplayerServer().getPlayerCount();
}
}
}
@@ -6,47 +6,26 @@ import net.minecraft.server.dedicated.DedicatedServer;
import java.io.File;
//@Environment(EnvType.SERVER)
public class MinecraftServerWrapper implements IMinecraftSharedWrapper
public class MinecraftDedicatedServerWrapper implements IMinecraftSharedWrapper
{
public static final MinecraftServerWrapper INSTANCE = new MinecraftServerWrapper();
public static final MinecraftDedicatedServerWrapper INSTANCE = new MinecraftDedicatedServerWrapper();
private MinecraftDedicatedServerWrapper() { }
public DedicatedServer dedicatedServer = null;
//=============//
// constructor //
//=============//
private MinecraftServerWrapper() { }
//=========//
// methods //
//=========//
@Override
public boolean isDedicatedServer() { return true; }
@Override
public File getInstallationDirectory()
{
if (this.dedicatedServer == null)
{
throw new IllegalStateException("Trying to get Installation Direction before Dedicated server completed initialization!");
throw new IllegalStateException("Trying to get Installation Direction before Dedicated server complete initialization!");
}
#if MC_VER < MC_1_21_1
#if MC_VER < MC_1_21
return this.dedicatedServer.getServerDirectory();
#else
return this.dedicatedServer.getServerDirectory().toFile();
#endif
}
@Override
public int getPlayerCount()
{
return this.dedicatedServer.getPlayerCount();
}
}
@@ -1,173 +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.minecraft;
import com.mojang.blaze3d.platform.GlStateManager;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import org.apache.logging.log4j.Logger;
import org.lwjgl.opengl.GL32;
/**
* A singleton that contains everything
* related to rendering in Minecraft.
*
* @author James Seibel
* @version 12-12-2021
*/
//@Environment(EnvType.CLIENT)
public class MinecraftGLWrapper implements IMinecraftGLWrapper
{
public static final MinecraftGLWrapper INSTANCE = new MinecraftGLWrapper();
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
/*
private static final StencilState STENCIL;
*/
// scissor //
/** @see GL32#GL_SCISSOR_TEST */
@Override
public void enableScissorTest() { GlStateManager._enableScissorTest(); }
/** @see GL32#GL_SCISSOR_TEST */
@Override
public void disableScissorTest() { GlStateManager._disableScissorTest(); }
// stencil //
//
// /** @see GL32#GL_SCISSOR_TEST */
// public void enableScissorTest() { GlStateManager._stencilFunc(); }
// /** @see GL32#GL_SCISSOR_TEST */
// public void disableScissorTest() { GlStateManager._disableScissorTest(); }
// depth //
/** @see GL32#GL_DEPTH_TEST */
@Override
public void enableDepthTest() { GlStateManager._enableDepthTest(); }
/** @see GL32#GL_DEPTH_TEST */
@Override
public void disableDepthTest() { GlStateManager._disableDepthTest(); }
/** @see GL32#glDepthFunc(int) */
@Override
public void glDepthFunc(int func) { GlStateManager._depthFunc(func); }
/** @see GL32#glDepthMask(boolean) */
@Override
public void enableDepthMask() { GlStateManager._depthMask(true); }
/** @see GL32#glDepthMask(boolean) */
@Override
public void disableDepthMask() { GlStateManager._depthMask(false); }
// blending //
/** @see GL32#GL_BLEND */
@Override
public void enableBlend() { GlStateManager._enableBlend(); }
/** @see GL32#GL_BLEND */
@Override
public void disableBlend() { GlStateManager._disableBlend(); }
/** @see GL32#glBlendFunc */
@Override
public void glBlendFunc(int sfactor, int dfactor) { GlStateManager._blendFunc(sfactor, dfactor); }
/** @see GL32#glBlendFuncSeparate */
@Override
public void glBlendFuncSeparate(int sfactorRGB, int dfactorRGB, int sfactorAlpha, int dfactorAlpha)
{ GlStateManager._blendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha); }
// frame buffers //
/** @see GL32#glBindFramebuffer */
@Override
public void glBindFramebuffer(int target, int framebuffer)
{ GlStateManager._glBindFramebuffer(target, framebuffer); }
// buffers //
/** @see GL32#glGenBuffers() */
@Override
public int glGenBuffers()
{ return GlStateManager._glGenBuffers(); }
/** @see GL32#glDeleteBuffers(int) */
@Override
public void glDeleteBuffers(int buffer)
{ GlStateManager._glDeleteBuffers(buffer); }
// culling //
/** @see GL32#GL_CULL_FACE */
@Override
public void enableFaceCulling() { GlStateManager._enableCull(); }
/** @see GL32#GL_CULL_FACE */
@Override
public void disableFaceCulling() { GlStateManager._disableCull(); }
// textures //
/** @see GL32#glGenTextures() */
@Override
public int glGenTextures() { return GlStateManager._genTexture(); }
/** @see GL32#glDeleteTextures(int) */
@Override
public void glDeleteTextures(int texture) { GlStateManager._deleteTexture(texture); }
/** @see GL32#glActiveTexture(int) */
@Override
public void glActiveTexture(int textureId) { GlStateManager._activeTexture(textureId); }
/** only works for textures bound via this system or MC's {@link GlStateManager} */
@Override
public int getActiveTexture()
{
#if MC_VER <= MC_1_16_5
return GL32.glGetInteger(GL32.GL_ACTIVE_TEXTURE);
#else
return GlStateManager._getActiveTexture();
#endif
}
/**
* Always binds to {@link GL32#GL_TEXTURE_2D}
* @see GL32#glBindTexture(int, int)
*/
@Override
public void glBindTexture(int texture) { GlStateManager._bindTexture(texture); }
}
@@ -21,43 +21,52 @@ package com.seibel.distanthorizons.common.wrappers.minecraft;
import java.awt.Color;
import java.lang.invoke.MethodHandles;
import java.util.Collection;
import java.util.HashSet;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
import com.mojang.blaze3d.pipeline.RenderTarget;
import com.mojang.blaze3d.platform.NativeImage;
import com.mojang.blaze3d.systems.RenderSystem;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.WrapperFactory;
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
#if MC_VER >= MC_1_17_1
import net.minecraft.client.renderer.FogRenderer;
import com.mojang.blaze3d.systems.RenderSystem;
#endif
#if MC_VER < MC_1_19_4
import org.joml.Matrix4f;
import org.joml.Vector3f;
#else
import org.joml.Matrix4f;
import org.joml.Vector3f;
#endif
#if MC_VER >= MC_1_20_2
import net.minecraft.client.renderer.chunk.SectionRenderDispatcher;
#endif
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.math.Vec3f;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.core.BlockPos;
import net.minecraft.world.effect.MobEffects;
#if MC_VER < MC_1_17_1
import net.minecraft.tags.FluidTags;
@@ -67,9 +76,9 @@ import org.lwjgl.opengl.GL15;
#else
import net.minecraft.world.level.material.FogType;
#endif
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import org.apache.logging.log4j.Logger;
import org.joml.Vector4f;
/**
@@ -111,6 +120,14 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
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
/** Unless you really need to know if the player is blind, use {@link MinecraftRenderWrapper#isFogStateSpecial()}/{@link IMinecraftRenderWrapper#isFogStateSpecial()} instead */
public boolean playerHasBlindingEffect()
@@ -131,36 +148,59 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
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
public Color getFogColor(float partialTicks)
{
#if MC_VER < MC_1_17_1
float[] colorValues = new float[4];
GL15.glGetFloatv(GL15.GL_FOG_COLOR, colorValues);
return new Color(
Math.max(0f, Math.min(colorValues[0], 1f)), // r
Math.max(0f, Math.min(colorValues[1], 1f)), // g
Math.max(0f, Math.min(colorValues[2], 1f)), // b
Math.max(0f, Math.min(colorValues[3], 1f)) // a
);
#elif MC_VER < MC_1_21_3
#else
FogRenderer.setupColor(MC.gameRenderer.getMainCamera(), partialTicks, MC.level, 1, MC.gameRenderer.getDarkenWorldAmount(partialTicks));
float[] colorValues = RenderSystem.getShaderFogColor();
return new Color(
Math.max(0f, Math.min(colorValues[0], 1f)), // r
Math.max(0f, Math.min(colorValues[1], 1f)), // g
Math.max(0f, Math.min(colorValues[2], 1f)), // b
Math.max(0f, Math.min(colorValues[3], 1f)) // a
);
#else
Vector4f colorValues = FogRenderer.computeFogColor(MC.gameRenderer.getMainCamera(), partialTicks, MC.level, 1, MC.gameRenderer.getDarkenWorldAmount(partialTicks));
return new Color(
Math.max(0f, Math.min(colorValues.x, 1f)), // r
Math.max(0f, Math.min(colorValues.y, 1f)), // g
Math.max(0f, Math.min(colorValues.z, 1f)), // b
Math.max(0f, Math.min(colorValues.w, 1f)) // a
);
#endif
return new Color(
Math.max(0f, Math.min(colorValues[0], 1f)),
Math.max(0f, Math.min(colorValues[1], 1f)),
Math.max(0f, Math.min(colorValues[2], 1f)),
Math.max(0f, Math.min(colorValues[3], 1f))
);
}
// getSpecialFogColor() is the same as getFogColor()
@@ -170,24 +210,18 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
if (MC.level.dimensionType().hasSkyLight())
{
float frameTime;
#if MC_VER < MC_1_21_1
#if MC_VER < MC_1_21
frameTime = MC.getFrameTime();
#elif MC_VER < MC_1_21_3
frameTime = MC.getTimer().getRealtimeDeltaTicks();
#else
frameTime = MC.deltaTracker.getGameTimeDeltaTicks();
frameTime = MC.getTimer().getRealtimeDeltaTicks();
#endif
#if MC_VER < MC_1_17_1
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getBlockPosition(), frameTime);
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
#elif MC_VER < MC_1_21_3
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
#else
int argbColorInt = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);;
return ColorUtil.toColorObjARGB(argbColorInt); // TODO MC changed color formats
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);
#endif
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
}
else
{
@@ -267,8 +301,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
@Override
public int getDepthTextureId() { return this.getRenderTarget().getDepthTextureId(); }
@Override
public int getColorTextureId() { return this.getRenderTarget().getColorTextureId(); }
@Override
public int getTargetFrameBufferViewportWidth()
@@ -282,6 +314,77 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
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
public ILightMapWrapper getLightmapWrapper(ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); }
@@ -302,10 +405,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
#endif
}
/**
* It's better to use {@link MinecraftRenderWrapper#setLightmapId(int, IClientLevelWrapper)} if possible,
* however old MC versions don't support it.
*/
public void updateLightmap(NativeImage lightPixels, IClientLevelWrapper level)
{
// Using ClientLevelWrapper as the key would be better, but we don't have a consistent way to create the same
@@ -316,15 +415,5 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
LightMapWrapper wrapper = this.lightmapByDimensionType.computeIfAbsent(dimensionType, (dimType) -> new LightMapWrapper());
wrapper.uploadLightmap(lightPixels);
}
public void setLightmapId(int tetxureId, IClientLevelWrapper level)
{
// Using ClientLevelWrapper as the key would be better, but we don't have a consistent way to create the same
// object for the same MC level and/or the same hash,
// so this will have to do for now
IDimensionTypeWrapper dimensionType = level.getDimensionType();
LightMapWrapper wrapper = this.lightmapByDimensionType.computeIfAbsent(dimensionType, (dimType) -> new LightMapWrapper());
wrapper.setLightmapId(tetxureId);
}
}
@@ -31,19 +31,31 @@ public class ProfilerWrapper implements IProfilerWrapper
{
public ProfilerFiller profiler;
public ProfilerWrapper(ProfilerFiller newProfiler) { this.profiler = newProfiler; }
public ProfilerWrapper(ProfilerFiller newProfiler)
{
profiler = newProfiler;
}
/** starts a new section inside the currently running section */
@Override
public void push(String newSection) { this.profiler.push(newSection); }
public void push(String newSection)
{
profiler.push(newSection);
}
/** ends the currently running section and starts a new one */
@Override
public void popPush(String newSection) { this.profiler.popPush(newSection); }
public void popPush(String newSection)
{
profiler.popPush(newSection);
}
/** ends the currently running section */
@Override
public void pop() { this.profiler.pop(); }
public void pop()
{
profiler.pop();
}
}
@@ -1,15 +0,0 @@
package com.seibel.distanthorizons.common.wrappers.misc;
import net.minecraft.server.level.ServerLevel;
import org.jetbrains.annotations.Nullable;
public interface IMixinServerPlayer
{
@Nullable
ServerLevel distantHorizons$getDimensionChangeDestination();
#if MC_VER == MC_1_16_5
void distantHorizons$setDimensionChangeDestination(ServerLevel dimensionChangeDestination);
#endif
}
@@ -20,8 +20,6 @@
package com.seibel.distanthorizons.common.wrappers.misc;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
import org.lwjgl.opengl.GL32;
@@ -29,12 +27,9 @@ import java.nio.ByteBuffer;
public class LightMapWrapper implements ILightMapWrapper
{
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
private int textureId = 0;
//==============//
// constructors //
//==============//
@@ -43,53 +38,41 @@ public class LightMapWrapper implements ILightMapWrapper
//==================//
// lightmap syncing //
//==================//
//=========//
// methods //
//=========//
public void uploadLightmap(NativeImage image)
{
int currentTexture = GLMC.getActiveTexture();
int currentBind = GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D);
if (this.textureId == 0)
{
this.createLightmap(image);
}
else
{
GLMC.glBindTexture(this.textureId);
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId);
}
image.upload(0, 0, 0, false);
GLMC.glBindTexture(currentTexture);
GL32.glBindTexture(GL32.GL_TEXTURE_2D, currentBind);
}
private void createLightmap(NativeImage image)
{
this.textureId = GLMC.glGenTextures();
GLMC.glBindTexture(this.textureId);
this.textureId = GL32.glGenTextures();
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId);
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, image.format().glFormat(), image.getWidth(), image.getHeight(),
0, image.format().glFormat(), GL32.GL_UNSIGNED_BYTE, (ByteBuffer) null);
}
public void setLightmapId(int minecraftLightmapTetxureId)
{
// just use the MC texture ID
this.textureId = minecraftLightmapTetxureId;
}
//==============//
// lightmap use //
//==============//
@Override
public void bind()
{
GLMC.glActiveTexture(GL32.GL_TEXTURE0);
GLMC.glBindTexture(this.textureId);
GL32.glActiveTexture(GL32.GL_TEXTURE0);
GL32.glBindTexture(GL32.GL_TEXTURE_2D, this.textureId);
}
@Override
public void unbind() { GLMC.glBindTexture(0); }
public void unbind() { GL32.glBindTexture(GL32.GL_TEXTURE_2D, 0); }
}
@@ -1,30 +0,0 @@
package com.seibel.distanthorizons.common.wrappers.misc;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IMutableBlockPosWrapper;
import net.minecraft.core.BlockPos;
public class MutableBlockPosWrapper implements IMutableBlockPosWrapper
{
public final BlockPos.MutableBlockPos pos;
//=============//
// constructor //
//=============//
public MutableBlockPosWrapper()
{
this.pos = new BlockPos.MutableBlockPos();
}
//===========//
// overrides //
//===========//
@Override
public Object getWrappedMcObject() { return this.pos; }
}
@@ -1,115 +1,54 @@
package com.seibel.distanthorizons.common.wrappers.misc;
import com.google.common.base.Objects;
import com.google.common.collect.MapMaker;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import com.seibel.distanthorizons.core.util.math.Vec3d;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.world.phys.Vec3;
import java.net.SocketAddress;
import java.util.UUID;
import java.util.concurrent.ConcurrentMap;
/**
* This wrapper transparently ensures that the underlying {@link ServerPlayer} is always valid,
* unless the player has disconnected.
*/
public class ServerPlayerWrapper implements IServerPlayerWrapper
{
private static final ConcurrentMap<ServerGamePacketListenerImpl, ServerPlayerWrapper> serverPlayerWrapperMap = new MapMaker().weakKeys().weakValues().makeMap();
private static final ConcurrentMap<ServerPlayer, ServerPlayerWrapper>
serverPlayerWrapperMap = new MapMaker().weakKeys().makeMap();
private final ServerGamePacketListenerImpl connection;
//=============//
// constructor //
//=============//
private final ServerPlayer serverPlayer;
public static ServerPlayerWrapper getWrapper(ServerPlayer serverPlayer)
{ return serverPlayerWrapperMap.computeIfAbsent(serverPlayer.connection, ignored -> new ServerPlayerWrapper(serverPlayer.connection)); }
{
return serverPlayerWrapperMap.computeIfAbsent(serverPlayer, ServerPlayerWrapper::new);
}
private ServerPlayerWrapper(ServerGamePacketListenerImpl connection) { this.connection = connection; }
private ServerPlayerWrapper(ServerPlayer serverPlayer)
{
this.serverPlayer = serverPlayer;
}
public UUID getUUID()
{
return serverPlayer.getUUID();
}
//=========//
// getters //
//=========//
private ServerPlayer getServerPlayer() { return this.connection.player; }
@Override
public String getName() { return this.getServerPlayer().getName().getString(); }
@Override
public IServerLevelWrapper getLevel()
{
ServerLevel level = ((IMixinServerPlayer) this.getServerPlayer()).distantHorizons$getDimensionChangeDestination();
if (level == null)
{
#if MC_VER < MC_1_20_1
level = this.getServerPlayer().getLevel();
return ServerLevelWrapper.getWrapper(this.serverPlayer.getLevel());
#else
level = this.getServerPlayer().serverLevel();
return ServerLevelWrapper.getWrapper(this.serverPlayer.serverLevel());
#endif
}
return ServerLevelWrapper.getWrapper(level);
}
@Override
public Vec3d getPosition()
public Object getWrappedMcObject()
{
Vec3 position = this.getServerPlayer().position();
return new Vec3d(position.x, position.y, position.z);
return serverPlayer;
}
@Override
public int getViewDistance() { return this.getServerPlayer().server.getPlayerList().getViewDistance(); }
@Override
public SocketAddress getRemoteAddress()
public String toString()
{
#if MC_VER >= MC_1_19_4
return this.getServerPlayer().connection.getRemoteAddress();
#else // < 1.19.4
return this.getServerPlayer().connection.connection.getRemoteAddress();
#endif
return "Wrapped{" + serverPlayer.toString() + "}";
}
//================//
// base overrides //
//================//
@Override
public Object getWrappedMcObject() { return this.getServerPlayer(); }
@Override
public String toString() { return "Wrapped{" + this.getServerPlayer() + "}"; }
@Override
public boolean equals(Object obj)
{
if (this == obj)
{
return true;
}
if (!(obj instanceof ServerPlayerWrapper))
{
return false;
}
ServerPlayerWrapper that = (ServerPlayerWrapper) obj;
return Objects.equal(this.connection, that.connection);
}
@Override
public int hashCode() { return Objects.hashCode(this.connection); }
}
@@ -5,13 +5,13 @@ import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegist
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
import com.seibel.distanthorizons.common.wrappers.block.BlockStateWrapper;
import com.seibel.distanthorizons.common.wrappers.block.ClientBlockStateColorCache;
import com.seibel.distanthorizons.common.wrappers.block.cache.ClientBlockDetailMap;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.*;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
@@ -22,16 +22,12 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapp
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
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.ChunkSource;
import net.minecraft.world.phys.Vec3;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;
@@ -41,11 +37,6 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
#if MC_VER < MC_1_21_3
#else
import com.seibel.distanthorizons.core.util.ColorUtil;
#endif
public class ClientLevelWrapper implements IClientLevelWrapper
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger(ClientLevelWrapper.class.getSimpleName());
@@ -55,7 +46,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
private static final Minecraft MINECRAFT = Minecraft.getInstance();
private final ClientLevel level;
private final ConcurrentHashMap<BlockState, ClientBlockStateColorCache> blockCache = new ConcurrentHashMap<>();
private final ClientBlockDetailMap blockMap = new ClientBlockDetailMap(this);
private BlockStateWrapper dirtBlockWrapper;
private BiomeWrapper plainsBiomeWrapper;
@@ -76,12 +67,8 @@ public class ClientLevelWrapper implements IClientLevelWrapper
// wrapper logic //
//===============//
public static IClientLevelWrapper getWrapper(@NotNull ClientLevel level) { return getWrapper(level, false); }
@Nullable
public static IClientLevelWrapper getWrapper(@Nullable ClientLevel level, boolean bypassLevelKeyManager)
{
if (!bypassLevelKeyManager)
public static IClientLevelWrapper getWrapper(@Nullable ClientLevel level)
{
if (level == null)
{
@@ -89,15 +76,14 @@ public class ClientLevelWrapper implements IClientLevelWrapper
}
// used if the client is connected to a server that defines the currently loaded level
IServerKeyedClientLevel overrideLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel();
if (overrideLevel != null)
if (KEYED_CLIENT_LEVEL_MANAGER.getUseOverrideWrapper())
{
return overrideLevel;
}
return KEYED_CLIENT_LEVEL_MANAGER.getOverrideWrapper();
}
return LEVEL_WRAPPER_BY_CLIENT_LEVEL.computeIfAbsent(level, ClientLevelWrapper::new);
return getWrapperIgnoringOverride(level);
}
public static IClientLevelWrapper getWrapperIgnoringOverride(@NotNull ClientLevel level) { return LEVEL_WRAPPER_BY_CLIENT_LEVEL.computeIfAbsent(level, ClientLevelWrapper::new); }
@Nullable
@Override
@@ -125,7 +111,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
}
catch (Exception e)
{
LOGGER.error("Failed to get server side wrapper for client level: " + this.level);
LOGGER.error("Failed to get server side wrapper for client level: " + level);
return null;
}
}
@@ -137,13 +123,9 @@ public class ClientLevelWrapper implements IClientLevelWrapper
//====================//
@Override
public int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockWrapper)
public int computeBaseColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockState)
{
ClientBlockStateColorCache blockColorCache = this.blockCache.computeIfAbsent(
((BlockStateWrapper) blockWrapper).blockState,
(block) -> new ClientBlockStateColorCache(block, this));
return blockColorCache.getColor((BiomeWrapper) biome, pos);
return this.blockMap.getColor(((BlockStateWrapper) blockState).blockState, (BiomeWrapper) biome, pos);
}
@Override
@@ -163,12 +145,9 @@ public class ClientLevelWrapper implements IClientLevelWrapper
}
}
return this.getBlockColor(DhBlockPos.ZERO,BiomeWrapper.EMPTY_WRAPPER, this.dirtBlockWrapper);
return this.blockMap.getColor(this.dirtBlockWrapper.blockState, BiomeWrapper.EMPTY_WRAPPER, DhBlockPos.ZERO);
}
@Override
public void clearBlockColorCache() { this.blockCache.clear(); }
@Override
public IBiomeWrapper getPlainsBiomeWrapper()
{
@@ -192,16 +171,6 @@ public class ClientLevelWrapper implements IClientLevelWrapper
@Override
public IDimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); }
@Override
public String getDimensionName() { return this.level.dimension().location().toString(); }
@Override
public long getHashedSeed() { return this.level.getBiomeManager().biomeZoomSeed; }
@Override
public String getDhIdentifier() { return this.getHashedSeedEncoded() + "@" + this.getDimensionName(); }
@Override
public EDhApiLevelType getLevelType() { return EDhApiLevelType.CLIENT_LEVEL; }
@@ -221,28 +190,26 @@ public class ClientLevelWrapper implements IClientLevelWrapper
{
#if MC_VER < MC_1_17_1
return 0;
#elif MC_VER < MC_1_21_3
return this.level.getMinBuildHeight();
#else
return this.level.getMinY();
return this.level.getMinBuildHeight();
#endif
}
@Override
public IChunkWrapper tryGetChunk(DhChunkPos pos)
{
if (!this.level.hasChunk(pos.getX(), pos.getZ()))
if (!this.level.hasChunk(pos.x, pos.z))
{
return null;
}
ChunkAccess chunk = this.level.getChunk(pos.getX(), pos.getZ(), ChunkStatus.EMPTY, false);
ChunkAccess chunk = this.level.getChunk(pos.x, pos.z, ChunkStatus.EMPTY, false);
if (chunk == null)
{
return null;
}
return new ChunkWrapper(chunk, this);
return new ChunkWrapper(chunk, this.level, this);
}
@Override
@@ -254,7 +221,9 @@ public class ClientLevelWrapper implements IClientLevelWrapper
@Override
public IBlockStateWrapper getBlockState(DhBlockPos pos)
{ return BlockStateWrapper.fromBlockState(this.level.getBlockState(McObjectConverter.Convert(pos)), this); }
{
return BlockStateWrapper.fromBlockState(this.level.getBlockState(McObjectConverter.Convert(pos)), this);
}
@Override
public IBiomeWrapper getBiome(DhBlockPos pos) { return BiomeWrapper.getBiomeWrapper(this.level.getBiome(McObjectConverter.Convert(pos)), this); }
@@ -269,18 +238,6 @@ public class ClientLevelWrapper implements IClientLevelWrapper
this.parentDhLevel = null;
}
@Override
public File getDhSaveFolder()
{
if (this.parentDhLevel == null)
{
return null;
}
return this.parentDhLevel.getSaveStructure().getSaveFolder(this);
}
//===================//
@@ -301,18 +258,6 @@ public class ClientLevelWrapper implements IClientLevelWrapper
return this.parentDhLevel.getGenericRenderer();
}
@Override
public Color getCloudColor(float tickDelta)
{
#if MC_VER < MC_1_21_3
Vec3 colorVec3 = this.level.getCloudColor(tickDelta);
return new Color((float)colorVec3.x, (float)colorVec3.y, (float)colorVec3.z);
#else
int argbColor = this.level.getCloudColor(tickDelta);
return ColorUtil.toColorObjARGB(argbColor);
#endif
}
//================//
@@ -327,7 +272,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
return "Wrapped{null}";
}
return "Wrapped{" + this.level.toString() + "@" + this.getDhIdentifier() + "}";
return "Wrapped{" + this.level.toString() + "@" + this.getDimensionType().getDimensionName() + "}";
}
}
@@ -44,9 +44,7 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
{
//first we check if the biome has already been wrapped
if (dimensionTypeWrapperMap.containsKey(dimensionType) && dimensionTypeWrapperMap.get(dimensionType) != null)
{
return dimensionTypeWrapperMap.get(dimensionType);
}
//if it hasn't been created yet, we create it and save it in the map
@@ -63,26 +61,22 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
}
private String getDimensionName()
@Override
public String getDimensionName()
{
#if MC_VER <= MC_1_16_5
// effectsLocation() is marked as client only, so using the backing field directly
return dimensionType.effectsLocation.getPath();
#else
return this.dimensionType.effectsLocation().getPath();
#endif
return dimensionType.effectsLocation().getPath();
}
@Override
public boolean hasCeiling()
{
return this.dimensionType.hasCeiling();
return dimensionType.hasCeiling();
}
@Override
public boolean hasSkyLight()
{
return this.dimensionType.hasSkyLight();
return dimensionType.hasSkyLight();
}
@Override
@@ -91,9 +85,7 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
return this.dimensionType;
}
// there's definitely a better way of doing this, but it should work well enough for now
@Override
public boolean isTheEnd() { return this.getDimensionName().equalsIgnoreCase("the_end"); }
@Override
public boolean equals(Object obj)
@@ -27,14 +27,17 @@ import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegist
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.block.BiomeWrapper;
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.minecraft.MinecraftClientWrapper;
import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhBlockPos;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
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 net.minecraft.server.level.ServerLevel;
@@ -47,13 +50,12 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
#if MC_VER < MC_1_21_3
#else
import java.nio.file.Path;
#endif
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
/**
* @version 2022-9-16
*/
public class ServerLevelWrapper implements IServerLevelWrapper
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
@@ -69,10 +71,12 @@ public class ServerLevelWrapper implements IServerLevelWrapper
// constructors //
//==============//
public static ServerLevelWrapper getWrapper(ServerLevel level)
{ return LEVEL_WRAPPER_BY_SERVER_LEVEL.computeIfAbsent(level, ServerLevelWrapper::new); }
public static ServerLevelWrapper getWrapper(ServerLevel level) { return LEVEL_WRAPPER_BY_SERVER_LEVEL.computeIfAbsent(level, ServerLevelWrapper::new); }
public ServerLevelWrapper(ServerLevel level) { this.level = level; }
public ServerLevelWrapper(ServerLevel level)
{
this.level = level;
}
@@ -81,98 +85,80 @@ public class ServerLevelWrapper implements IServerLevelWrapper
//=========//
@Override
public File getMcSaveFolder()
public File getSaveFolder()
{
#if MC_VER < MC_1_21_3
return this.level.getChunkSource().getDataStorage().dataFolder;
#else
return this.level.getChunkSource().getDataStorage().dataFolder.toFile();
#endif
return level.getChunkSource().getDataStorage().dataFolder;
}
@Override
public String getWorldFolderName()
public DimensionTypeWrapper getDimensionType()
{
#if MC_VER >= MC_1_17_1
return this.level.getServer().getWorldScreenshotFile().get().getParent().getFileName().toString();
#else // <= 1.16.5
return this.level.getServer().getWorldScreenshotFile().getParentFile().getName();
#endif
return DimensionTypeWrapper.getDimensionTypeWrapper(level.dimensionType());
}
@Override
public DimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); }
@Override
public String getDimensionName() { return this.level.dimension().location().toString(); }
@Override
public long getHashedSeed() { return this.level.getBiomeManager().biomeZoomSeed; }
@Override
public String getDhIdentifier() { return this.getDimensionName(); }
@Override
public EDhApiLevelType getLevelType() { return EDhApiLevelType.SERVER_LEVEL; }
public ServerLevel getLevel() { return this.level; }
public ServerLevel getLevel()
{
return level;
}
@Override
public boolean hasCeiling() { return this.level.dimensionType().hasCeiling(); }
public boolean hasCeiling()
{
return level.dimensionType().hasCeiling();
}
@Override
public boolean hasSkyLight() { return this.level.dimensionType().hasSkyLight(); }
public boolean hasSkyLight()
{
return level.dimensionType().hasSkyLight();
}
@Override
public int getMaxHeight() { return this.level.getHeight(); }
public int getMaxHeight()
{
return level.getHeight();
}
@Override
public int getMinHeight()
{
#if MC_VER < MC_1_17_1
return 0;
#elif MC_VER < MC_1_21_3
return this.level.getMinBuildHeight();
#else
return this.level.getMinY();
return level.getMinBuildHeight();
#endif
}
@Override
public IChunkWrapper tryGetChunk(DhChunkPos pos)
{
if (!this.level.hasChunk(pos.getX(), pos.getZ()))
{
return null;
}
ChunkAccess chunk = this.level.getChunk(pos.getX(), pos.getZ(), ChunkStatus.FULL, false);
if (chunk == null)
{
return null;
}
return new ChunkWrapper(chunk, this);
if (!level.hasChunk(pos.x, pos.z)) return null;
ChunkAccess chunk = level.getChunk(pos.x, pos.z, ChunkStatus.FULL, false);
if (chunk == null) return null;
return new ChunkWrapper(chunk, level, this);
}
@Override
public boolean hasChunkLoaded(int chunkX, int chunkZ)
{
// world.hasChunk(chunkX, chunkZ); THIS DOES NOT WORK FOR CLIENT LEVEL CAUSE MOJANG ALWAYS RETURN TRUE FOR THAT!
ChunkSource source = this.level.getChunkSource();
ChunkSource source = level.getChunkSource();
return source.hasChunk(chunkX, chunkZ);
}
@Override
public IBlockStateWrapper getBlockState(DhBlockPos pos)
{
return BlockStateWrapper.fromBlockState(this.level.getBlockState(McObjectConverter.Convert(pos)), this);
return BlockStateWrapper.fromBlockState(level.getBlockState(McObjectConverter.Convert(pos)), this);
}
@Override
public IBiomeWrapper getBiome(DhBlockPos pos)
{
return BiomeWrapper.getBiomeWrapper(this.level.getBiome(McObjectConverter.Convert(pos)), this);
return BiomeWrapper.getBiomeWrapper(level.getBiome(McObjectConverter.Convert(pos)), this);
}
@Override
@@ -196,18 +182,6 @@ public class ServerLevelWrapper implements IServerLevelWrapper
return this.parentDhLevel.getGenericRenderer();
}
@Override
public File getDhSaveFolder()
{
if (this.parentDhLevel == null)
{
return null;
}
return this.parentDhLevel.getSaveStructure().getSaveFolder(this);
}
//================//
@@ -215,6 +189,6 @@ public class ServerLevelWrapper implements IServerLevelWrapper
//================//
@Override
public String toString() { return "Wrapped{" + this.level.toString() + "@" + this.getDhIdentifier() + "}"; }
public String toString() { return "Wrapped{" + this.level.toString() + "@" + this.getDimensionType().getDimensionName() + "}"; }
}
@@ -20,10 +20,11 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
import java.lang.invoke.MethodHandles;
import java.util.concurrent.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.function.Consumer;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiDistantGeneratorMode;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiWorldGenerationStep;
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
@@ -42,10 +43,8 @@ public final class GenerationEvent
public final int id;
public final ThreadedParameters threadedParam;
public final DhChunkPos minPos;
/** the number of chunks wide this event is */
public final int size;
public final EDhApiWorldGenerationStep targetGenerationStep;
public final EDhApiDistantGeneratorMode generatorMode;
public EventTimer timer = null;
public long inQueueTime;
public long timeoutTime = -1;
@@ -56,13 +55,12 @@ public final class GenerationEvent
public GenerationEvent(
DhChunkPos minPos, int size, BatchGenerationEnvironment generationGroup,
EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep targetGenerationStep, Consumer<IChunkWrapper> resultConsumer)
EDhApiWorldGenerationStep targetGenerationStep, Consumer<IChunkWrapper> resultConsumer)
{
this.inQueueTime = System.nanoTime();
this.id = generationFutureDebugIDs++;
this.minPos = minPos;
this.size = size;
this.generatorMode = generatorMode;
this.targetGenerationStep = targetGenerationStep;
this.threadedParam = ThreadedParameters.getOrMake(generationGroup.params);
this.resultConsumer = resultConsumer;
@@ -72,11 +70,17 @@ public final class GenerationEvent
public static GenerationEvent startEvent(
DhChunkPos minPos, int size, BatchGenerationEnvironment genEnvironment,
EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer,
EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer,
ExecutorService worldGeneratorThreadPool)
{
GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, generatorMode, target, resultConsumer);
generationEvent.future = CompletableFuture.supplyAsync(() ->
if (size % 2 == 0)
{
size += 1; // size must be odd for vanilla world gen regions to work
}
GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, target, resultConsumer);
generationEvent.future = CompletableFuture.runAsync(() ->
{
long runStartTime = System.nanoTime();
generationEvent.timeoutTime = runStartTime;
@@ -84,75 +88,21 @@ public final class GenerationEvent
generationEvent.timer = new EventTimer("setup");
BatchGenerationEnvironment.isDistantGeneratorThread.set(true);
try
{
genEnvironment.generateLodFromListAsync(generationEvent, (runnable) ->
{
worldGeneratorThreadPool.execute(() ->
{
boolean alreadyMarked = BatchGenerationEnvironment.isCurrentThreadDistantGeneratorThread();
if (!alreadyMarked)
{
BatchGenerationEnvironment.isDistantGeneratorThread.set(true);
//LOGGER.info("generating [{}]", event.minPos);
genEnvironment.generateLodFromList(generationEvent);
}
try
catch (InterruptedException ignored)
{
runnable.run();
}
catch (Throwable throwable)
{
handleWorldGenThrowable(generationEvent, throwable);
}
finally
{
if (!alreadyMarked)
{
BatchGenerationEnvironment.isDistantGeneratorThread.set(false);
}
}
});
});
}
catch (Throwable initialThrowable)
{
handleWorldGenThrowable(generationEvent, initialThrowable);
}
finally
{
BatchGenerationEnvironment.isDistantGeneratorThread.remove();
}
return null;
}, worldGeneratorThreadPool);
return generationEvent;
}
/** There's probably a better way to handle this, but it'll work for now */
private static void handleWorldGenThrowable(GenerationEvent generationEvent, Throwable initialThrowable)
{
Throwable throwable = initialThrowable;
while (throwable instanceof CompletionException)
{
throwable = throwable.getCause();
}
if (throwable instanceof InterruptedException
|| throwable instanceof UncheckedInterruptedException
|| throwable instanceof RejectedExecutionException)
{
// these exceptions can be ignored, generally they just mean
// the thread is busy so it'll need to try again later.
// FIXME this should cause the world gen task to be re-queued so we can try again later
// however, currently it can cause large gaps in the world gen instead.
// These gaps will generate correctly if the level is reloaded and the world gen is re-queued,
// however this is makes it look like the generator isn't working or skipped something.
}
else
{
generationEvent.future.completeExceptionally(throwable);
}
}
public boolean isComplete() { return this.future.isDone(); }
@@ -176,6 +126,21 @@ public final class GenerationEvent
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()
{
this.timeoutTime = System.nanoTime();
@@ -50,26 +50,23 @@ import net.minecraft.world.level.storage.WorldData;
public final class GlobalParameters
{
public final ChunkGenerator generator;
public final IDhServerLevel lodLevel;
public final ServerLevel level;
public final Registry<Biome> biomes;
public final RegistryAccess registry;
public final long worldSeed;
public final DataFixer fixerUpper;
#if MC_VER < MC_1_19_2
public final StructureManager structures;
#else
public final StructureTemplateManager structures;
public final RandomState randomState;
#endif
#if MC_VER < MC_1_19_4
public final WorldGenSettings worldGenSettings;
#else
public final WorldOptions worldOptions;
#endif
public final IDhServerLevel lodLevel;
public final ServerLevel level;
public final Registry<Biome> biomes;
public final RegistryAccess registry;
public final long worldSeed;
public final DataFixer fixerUpper;
#if MC_VER >= MC_1_18_2
public final BiomeManager biomeManager;
public final ChunkScanAccess chunkScanner; // FIXME: Figure out if this is actually needed
@@ -79,34 +76,29 @@ public final class GlobalParameters
{
this.lodLevel = lodLevel;
this.level = ((ServerLevelWrapper) lodLevel.getServerLevelWrapper()).getWrappedMcObject();
MinecraftServer server = this.level.getServer();
level = ((ServerLevelWrapper) lodLevel.getServerLevelWrapper()).getWrappedMcObject();
MinecraftServer server = level.getServer();
WorldData worldData = server.getWorldData();
this.registry = server.registryAccess();
registry = server.registryAccess();
#if MC_VER < MC_1_19_4
this.worldGenSettings = worldData.worldGenSettings();
this.biomes = registry.registryOrThrow(Registry.BIOME_REGISTRY);
this.worldSeed = worldGenSettings.seed();
#elif MC_VER < MC_1_21_3
this.worldOptions = worldData.worldGenOptions();
this.biomes = registry.registryOrThrow(Registries.BIOME);
this.worldSeed = worldOptions.seed();
worldGenSettings = worldData.worldGenSettings();
biomes = registry.registryOrThrow(Registry.BIOME_REGISTRY);
worldSeed = worldGenSettings.seed();
#else
this.worldOptions = worldData.worldGenOptions();
this.biomes = this.registry.lookupOrThrow(Registries.BIOME);
this.worldSeed = this.worldOptions.seed();
worldOptions = worldData.worldGenOptions();
biomes = registry.registryOrThrow(Registries.BIOME);
worldSeed = worldOptions.seed();
#endif
#if MC_VER >= MC_1_18_2
this.biomeManager = new BiomeManager(this.level, BiomeManager.obfuscateSeed(this.worldSeed));
this.chunkScanner = this.level.getChunkSource().chunkScanner();
biomeManager = new BiomeManager(level, BiomeManager.obfuscateSeed(worldSeed));
chunkScanner = level.getChunkSource().chunkScanner();
#endif
this.structures = server.getStructureManager();
this.generator = this.level.getChunkSource().getGenerator();
this.fixerUpper = server.getFixerUpper();
structures = server.getStructureManager();
generator = level.getChunkSource().getGenerator();
fixerUpper = server.getFixerUpper();
#if MC_VER >= MC_1_19_2
this.randomState = this.level.getChunkSource().randomState();
randomState = level.getChunkSource().randomState();
#endif
}
@@ -19,6 +19,7 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import com.google.common.collect.Maps;
import com.mojang.serialization.Codec;
import com.mojang.serialization.Dynamic;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
@@ -26,13 +27,21 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGeneratio
import com.seibel.distanthorizons.core.logging.ConfigBasedLogger;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import it.unimi.dsi.fastutil.shorts.ShortList;
import net.minecraft.core.Registry;
import net.minecraft.core.SectionPos;
#if MC_VER >= MC_1_19_4
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
@@ -41,6 +50,7 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.nbt.NbtOps;
import net.minecraft.nbt.Tag;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome;
@@ -49,22 +59,20 @@ import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.*;
#if MC_VER < MC_1_21_3
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
#else
#endif
import net.minecraft.world.level.levelgen.Heightmap;
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.levelgen.blending.BlendingData;
#if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.feature.StructureFeature;
#endif
import net.minecraft.world.level.levelgen.structure.StructureStart;
import net.minecraft.world.level.levelgen.structure.pieces.StructurePieceSerializationContext;
import net.minecraft.world.ticks.LevelChunkTicks;
#endif
#if MC_VER >= MC_1_18_2
import net.minecraft.core.Holder;
import net.minecraft.core.RegistryAccess;
#if MC_VER < MC_1_19_2
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
#endif
@@ -77,7 +85,7 @@ import net.minecraft.world.level.material.Fluids;
#if MC_VER == MC_1_20_6
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkType;
#elif MC_VER >= MC_1_21_1
#elif MC_VER == MC_1_21
import net.minecraft.world.level.chunk.status.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkType;
#endif
@@ -120,7 +128,7 @@ public class ChunkLoader
ChunkPos actualPos = new ChunkPos(tagLevel.getInt("xPos"), tagLevel.getInt("zPos"));
if (!Objects.equals(chunkPos, actualPos))
{
#if MC_VER >= MC_1_18_2
#if MC_VER > MC_1_17_1
if (actualPos.equals(ChunkPos.ZERO))
#else
if (actualPos.equals(ChunkPos.INVALID_CHUNK_POS))
@@ -230,10 +238,8 @@ public class ChunkLoader
#if MC_VER >= MC_1_18_2
#if MC_VER < MC_1_19_4
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
#elif MC_VER < MC_1_21_3
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registries.BIOME);
#else
Registry<Biome> biomes = level.registryAccess().lookupOrThrow(Registries.BIOME);
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registries.BIOME);
#endif
#if MC_VER < MC_1_18_2
Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec(
@@ -241,12 +247,9 @@ public class ChunkLoader
#elif MC_VER < MC_1_19_2
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codec(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#elif MC_VER < MC_1_21_3
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#else
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#endif
#endif
int sectionYIndex = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif
@@ -291,7 +294,7 @@ public class ChunkLoader
#else
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null))
#endif
: new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
: new PalettedContainer<>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
#if MC_VER < MC_1_18_2
biomeContainer = tagSection.contains("biomes", 10)
@@ -299,28 +302,14 @@ public class ChunkLoader
: new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#else
if (tagSection.contains("biomes", 10))
{
biomeContainer =
biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
biomeContainer = tagSection.contains("biomes", 10)
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
#if MC_VER < MC_1_20_6
.getOrThrow(false, LOGGER::error);
.getOrThrow(false, LOGGER::error)
#else
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null));
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null))
#endif
}
else
{
biomeContainer = new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(),
#if MC_VER < MC_1_21_3
biomes.getHolderOrThrow(Biomes.PLAINS),
#else
biomes.getOrThrow(Biomes.PLAINS),
#endif
PalettedContainer.Strategy.SECTION_BIOMES);
}
: new PalettedContainer<>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#endif
#if MC_VER < MC_1_20_1
@@ -336,7 +325,7 @@ public class ChunkLoader
}
private static
#if MC_VER < MC_1_20_6 ChunkStatus.ChunkType
#elif MC_VER < MC_1_21_1 ChunkType
#elif MC_VER < MC_1_21 ChunkType
#else ChunkType #endif
readChunkType(CompoundTag tagLevel)
{
@@ -364,16 +353,12 @@ public class ChunkLoader
private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData)
{
ListTag tagPostProcessings = chunkData.getList("PostProcessing", 9);
for (int i = 0; i < tagPostProcessings.size(); ++i)
for (int n = 0; n < tagPostProcessings.size(); ++n)
{
ListTag listTag3 = tagPostProcessings.getList(i);
for (int j = 0; j < listTag3.size(); ++j)
ListTag listTag3 = tagPostProcessings.getList(n);
for (int o = 0; o < listTag3.size(); ++o)
{
#if MC_VER < MC_1_21_3
chunk.addPackedPostProcess(listTag3.getShort(j), i);
#else
chunk.addPackedPostProcess(ShortList.of(listTag3.getShort(j)), i);
#endif
chunk.addPackedPostProcess(listTag3.getShort(o), n);
}
}
}
@@ -385,12 +370,7 @@ public class ChunkLoader
{
@SuppressWarnings({"unchecked", "rawtypes"})
Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data"));
#if MC_VER < MC_1_21_3
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial(LOGGER::error).orElse(null);
#else
blendingData = BlendingData.unpack(BlendingData.Packed.CODEC.parse(blendingDataTag).resultOrPartial(LOGGER::error).orElse(null));
#endif
}
return blendingData;
}
@@ -412,7 +392,7 @@ public class ChunkLoader
return null;
#else
CombinedChunkLightStorage combinedStorage = new CombinedChunkLightStorage(ChunkWrapper.getInclusiveMinBuildHeight(chunk), ChunkWrapper.getExclusiveMaxBuildHeight(chunk));
CombinedChunkLightStorage combinedStorage = new CombinedChunkLightStorage(ChunkWrapper.getMinBuildHeight(chunk), ChunkWrapper.getMaxBuildHeight(chunk));
ChunkLightStorage blockLightStorage = combinedStorage.blockLightStorage;
ChunkLightStorage skyLightStorage = combinedStorage.skyLightStorage;
@@ -454,7 +434,7 @@ public class ChunkLoader
for (int sectionIndex = 0; sectionIndex < chunkSectionListTag.size(); sectionIndex++)
{
Tag chunkSectionTag = chunkSectionListTag.get(sectionIndex);
if (!(chunkSectionTag instanceof CompoundTag))
if (!(chunkSectionTag instanceof CompoundTag chunkSectionCompoundTag))
{
if (!lightingSectionErrorLogged)
{
@@ -463,7 +443,6 @@ public class ChunkLoader
}
return null;
}
CompoundTag chunkSectionCompoundTag = (CompoundTag) chunkSectionTag;
// if null all lights = 0
@@ -491,7 +470,7 @@ public class ChunkLoader
skyLight = LodUtil.MAX_MC_LIGHT;
}
int y = relY + (sectionIndex * LodUtil.CHUNK_WIDTH) + ChunkWrapper.getInclusiveMinBuildHeight(chunk);
int y = relY + (sectionIndex * LodUtil.CHUNK_WIDTH) + ChunkWrapper.getMinBuildHeight(chunk);
blockLightStorage.set(relX, y, relZ, blockLight);
skyLightStorage.set(relX, y, relZ, skyLight);
}
@@ -1,28 +1,23 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
#if MC_VER >= MC_1_21_1
#if MC_VER >= MC_1_21
import net.minecraft.server.level.GenerationChunkHolder;
import net.minecraft.world.level.ChunkPos;
import java.util.concurrent.CompletableFuture;
public class DhGenerationChunkHolder extends GenerationChunkHolder
{
public DhGenerationChunkHolder(ChunkPos pos) { super(pos); }
public DhGenerationChunkHolder(ChunkPos pos)
{
super(pos);
}
@Override
public int getTicketLevel() { return 0; }
@Override
public int getQueueLevel() { return 0; }
#if MC_VER < MC_1_21_3
#else
@Override
protected void addSaveDependency(CompletableFuture<?> completableFuture) { }
#endif
}
#endif
@@ -20,6 +20,7 @@
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
import java.lang.invoke.MethodHandles;
import java.util.EnumSet;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
@@ -62,19 +63,12 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.*;
#endif
#if MC_VER >= MC_1_21_1
#if MC_VER == MC_1_21
import net.minecraft.util.StaticCache2D;
import com.google.common.collect.ImmutableList;
import net.minecraft.server.level.GenerationChunkHolder;
#endif
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.ticks.BlackholeTickAccess;
import net.minecraft.world.ticks.LevelTickAccess;
#endif
public class DhLitWorldGenRegion extends WorldGenRegion
{
@@ -83,15 +77,14 @@ public class DhLitWorldGenRegion extends WorldGenRegion
private static ChunkStatus debugTriggeredForStatus = null;
public final ServerLevel serverLevel;
public final DummyLightEngine lightEngine;
public final BatchGenerationEnvironment.IEmptyChunkRetrievalFunc generator;
public final BatchGenerationEnvironment.IEmptyChunkGeneratorFunc generator;
public final int writeRadius;
public final int size;
private final ChunkPos firstPos;
private final List<ChunkAccess> cache;
private final Long2ObjectOpenHashMap<ChunkAccess> chunkMap = new Long2ObjectOpenHashMap<ChunkAccess>();
private final Long2ObjectOpenHashMap<ChunkAccess> chunkMap = new Long2ObjectOpenHashMap<>();
/**
* Present to reduce the chance that we accidentally break underlying MC code that isn't thread safe,
@@ -130,11 +123,11 @@ public class DhLitWorldGenRegion extends WorldGenRegion
ChunkAccess centerChunk,
ServerLevel serverLevel, DummyLightEngine lightEngine,
List<ChunkAccess> chunkList, ChunkStatus chunkStatus, int writeRadius,
BatchGenerationEnvironment.IEmptyChunkRetrievalFunc generator)
BatchGenerationEnvironment.IEmptyChunkGeneratorFunc generator)
{
#if MC_VER == MC_1_16_5
super(serverLevel, chunkList);
#elif MC_VER < MC_1_21_1
#elif MC_VER < MC_1_21
super(serverLevel, chunkList, chunkStatus, writeRadius);
#else
super(serverLevel,
@@ -147,10 +140,9 @@ public class DhLitWorldGenRegion extends WorldGenRegion
new ChunkDependencies(ImmutableList.copyOf(ChunkStatus.getStatusList()).reverse()),
writeRadius, (WorldGenContext var1, ChunkStep var2, StaticCache2D<GenerationChunkHolder> var3, ChunkAccess var4) -> null),
centerChunk);
#endif
this.firstPos = chunkList.get(0).getPos();
this.serverLevel = serverLevel;
#endif
this.firstPos = chunkList.getFirst().getPos();
this.generator = generator;
this.lightEngine = lightEngine;
this.writeRadius = writeRadius;
@@ -179,18 +171,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
if (center.isUpgrading())
{
LevelHeightAccessor levelHeightAccessor = center.getHeightAccessorForGeneration();
int minY;
int maxY;
#if MC_VER < MC_1_21_3
minY = levelHeightAccessor.getMinBuildHeight();
maxY = levelHeightAccessor.getMaxBuildHeight();
#else
minY = levelHeightAccessor.getMinY();
maxY = levelHeightAccessor.getMaxY();
#endif
if (blockPos.getY() < minY || blockPos.getY() >= maxY)
if (blockPos.getY() < levelHeightAccessor.getMinBuildHeight() || blockPos.getY() >= levelHeightAccessor.getMaxBuildHeight())
{
return false;
}
@@ -200,22 +181,6 @@ public class DhLitWorldGenRegion extends WorldGenRegion
}
#endif
#if MC_VER >= MC_1_18_2
@Override
@NotNull
public LevelTickAccess<Block> getBlockTicks()
{
// DH world gen doesn't need ticking, so return the BlackholeTickAccess list (which causes all ticks to be ignored).
// If this isn't done the server may attempt to tick chunks outside the vanilla render distance,
// which can throw warnings or cause other issues
return BlackholeTickAccess.emptyLevelList();
}
@Override
@NotNull
public LevelTickAccess<Fluid> getFluidTicks() { return BlackholeTickAccess.emptyLevelList(); }
#endif
// TODO Check this
// @Override
// public List<? extends StructureStart<?>> startsForFeature(SectionPos sectionPos,
@@ -230,7 +195,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
ChunkAccess chunkAccess = this.getChunk(blockPos);
if (chunkAccess instanceof LevelChunk)
return true;
chunkAccess.setBlockState(blockPos, blockState, /*isBlockMoving*/false);
chunkAccess.setBlockState(blockPos, blockState, false);
// This is for post ticking for water on gen and stuff like that. Not enabled
// for now.
// if (blockState.hasPostProcess(this, blockPos))
@@ -270,22 +235,6 @@ public class DhLitWorldGenRegion extends WorldGenRegion
#endif
}
/**
* This needs to be manually overridden to make sure Lithium 0.11.2 and lower
* don't try to get null chunks. <br><br>
*
* Problematic Lithium code was removed in 0.13.0 (MC 1.21.1) and higher: <br>
* https://github.com/CaffeineMC/lithium-fabric/commit/b7cfd53a1ed0197e1d13dea2799b898eb52ecab3
*/
@NotNull
@Override
public BlockState getBlockState(BlockPos blockPos)
{
int chunkX = SectionPos.blockToSectionCoord(blockPos.getX());
int chunkZ = SectionPos.blockToSectionCoord(blockPos.getZ());
return this.getChunk(chunkX, chunkZ).getBlockState(blockPos);
}
/** Skip BlockEntity stuff. They aren't needed for our use case. */
@Override
public boolean addFreshEntity(@NotNull Entity entity) { return true; }
@@ -375,7 +324,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
if (chunk == null)
{
// chunk isn't in memory, generate a new one
chunk = this.generator.getChunk(chunkX, chunkZ);
chunk = this.generator.generate(chunkX, chunkZ);
if (chunk == null)
{
throw new NullPointerException("The provided generator should not return null!");
@@ -426,10 +375,14 @@ public class DhLitWorldGenRegion extends WorldGenRegion
/** Overriding allows us to use our own lighting engine */
@Override
public boolean canSeeSky(@NotNull BlockPos blockPos)
{ return (this.getBrightness(LightLayer.SKY, blockPos) >= LodUtil.MAX_MC_LIGHT); }
{
return (this.getBrightness(LightLayer.SKY, blockPos) >= this.getMaxLightLevel());
}
public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
{ return this.calculateBlockTint(blockPos, colorResolver); }
{
return this.calculateBlockTint(blockPos, colorResolver);
}
private Biome _getBiome(BlockPos pos)
{
@@ -22,11 +22,11 @@ import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
#endif
/**
* Shouldn't be used when the C2ME mod is present,
* otherwise there may be potential file corruption.
* When C2ME is present use (via MC ServerLevel) level.getChunkSource().chunkMap.worker#loadAsync()
* instead.
* @deprecated should be replaced with net.minecraft.world.level.chunk.storage.IOWorker to
* prevent potential file corruption and issues with the C2ME mod.
* Generally this would be done via (MC ServerLevel) level.getChunkSource().chunkMap.worker#loadAsync()
*/
@Deprecated
public class RegionFileStorageExternalCache implements AutoCloseable
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
@@ -46,11 +46,22 @@ public class RegionFileStorageExternalCache implements AutoCloseable
static class RegionFileCache
{
public final long pos;
public final RegionFile file;
public RegionFileCache(long pos, RegionFile file)
{
this.pos = pos;
this.file = file;
}
}
private final ConcurrentLinkedQueue<RegionFileCache> regionFileCache = new ConcurrentLinkedQueue<>();
public ConcurrentLinkedQueue<RegionFileCache> regionFileCache = new ConcurrentLinkedQueue<>();
public RegionFileStorageExternalCache(RegionFileStorage storage) { this.storage = storage; }
@@ -139,7 +150,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
if (retryCount >= maxRetryCount)
{
BatchGenerationEnvironment.LOAD_LOGGER.warn("Concurrency issue detected when getting region file for chunk at [" + pos + "].");
BatchGenerationEnvironment.LOAD_LOGGER.warn("Concurrency issue detected when getting region file for chunk at " + pos + ".");
}
@@ -226,22 +237,4 @@ public class RegionFileStorageExternalCache implements AutoCloseable
}
//================//
// helper classes //
//================//
private static class RegionFileCache
{
public final long pos;
public final RegionFile file;
public RegionFileCache(long pos, RegionFile file)
{
this.pos = pos;
this.file = file;
}
}
}
@@ -231,11 +231,9 @@ public class WorldGenStructFeatManager extends #if MC_VER < MC_1_19_2 StructureF
Map<Structure, LongSet> map = chunk.getAllReferences();
ImmutableList.Builder<StructureStart> builder = ImmutableList.builder();
Iterator<Map.Entry<Structure, LongSet>> var5 = map.entrySet().iterator();
while (var5.hasNext())
for (Map.Entry<Structure, LongSet> entry : map.entrySet())
{
Map.Entry<Structure, LongSet> entry = var5.next();
Structure configuredStructureFeature = entry.getKey();
if (predicate.test(configuredStructureFeature))
{
@@ -68,50 +68,33 @@ public final class StepBiomes
}
else if (chunk instanceof ProtoChunk)
{
chunkWrapper.trySetStatus(STATUS);
#if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk);
}
}
for (ChunkAccess chunk : chunksToDo)
{
// System.out.println("StepBiomes: "+chunk.getPos());
#if MC_VER < MC_1_18_2
this.environment.params.generator.createBiomes(this.environment.params.biomes, chunk);
#elif MC_VER < MC_1_19_2
chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.createBiomes(
this.environment.params.biomes,
Runnable::run,
Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion),
chunk)
);
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(this.environment.params.biomes, Runnable::run, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif MC_VER < MC_1_19_4
chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.createBiomes(
this.environment.params.biomes,
Runnable::run,
this.environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion),
chunk)
);
#elif MC_VER < MC_1_21_1
chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.createBiomes(
Runnable::run,
this.environment.params.randomState,
Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion),
chunk)
);
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(this.environment.params.biomes, Runnable::run, this.environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif MC_VER < MC_1_21
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(Runnable::run, this.environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#else
chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.createBiomes(
this.environment.params.randomState,
Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion),
chunk)
);
chunk = this.environment.joinSync(this.environment.params.generator.createBiomes(this.environment.params.randomState, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#endif
}
}
@@ -66,7 +66,11 @@ public final class StepFeatures
}
else if (chunk instanceof ProtoChunk)
{
chunkWrapper.trySetStatus(STATUS);
#if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
}
@@ -76,7 +80,7 @@ public final class StepFeatures
worldGenRegion.setOverrideCenter(chunk.getPos());
environment.params.generator.applyBiomeDecoration(worldGenRegion, tParams.structFeat);
#else
if (worldGenRegion.hasChunk(chunkWrapper.getChunkPos().getX(), chunkWrapper.getChunkPos().getZ()))
if (worldGenRegion.hasChunk(chunkWrapper.getChunkPos().x, chunkWrapper.getChunkPos().z))
{
this.environment.params.generator.applyBiomeDecoration(worldGenRegion, chunk, tParams.structFeat.forWorldGenRegion(worldGenRegion));
}
@@ -87,6 +91,7 @@ public final class StepFeatures
#endif
Heightmap.primeHeightmaps(chunk, STATUS.heightmapsAfter());
BatchGenerationEnvironment.clearDistantGenerationMixinData();
}
catch (Exception e)
{
@@ -67,42 +67,32 @@ public final class StepNoise
{
continue;
}
chunkWrapper.trySetStatus(STATUS);
#if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk);
}
for (ChunkAccess chunk : chunksToDo)
{
// System.out.println("StepNoise: "+chunk.getPos());
#if MC_VER < MC_1_17_1
this.environment.params.generator.fillFromNoise(worldGenRegion, tParams.structFeat, chunk);
#elif MC_VER < MC_1_18_2
chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.fillFromNoise(
Runnable::run,
tParams.structFeat.forWorldGenRegion(worldGenRegion),
chunk));
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run,
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif MC_VER < MC_1_19_2
chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.fillFromNoise(
Runnable::run,
Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion),
chunk));
#elif MC_VER < MC_1_21_1
chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.fillFromNoise(
Runnable::run,
Blender.of(worldGenRegion),
this.environment.params.randomState,
tParams.structFeat.forWorldGenRegion(worldGenRegion),
chunk));
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion),
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#elif MC_VER < MC_1_21
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run, Blender.of(worldGenRegion), this.environment.params.randomState,
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#else
chunk = this.environment.confirmFutureWasRunSynchronously(
this.environment.params.generator.fillFromNoise(
Blender.of(worldGenRegion),
this.environment.params.randomState,
tParams.structFeat.forWorldGenRegion(worldGenRegion),
chunk));
chunk = this.environment.joinSync(this.environment.params.generator.fillFromNoise(Blender.of(worldGenRegion), this.environment.params.randomState,
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
#endif
UncheckedInterruptedException.throwIfInterrupted();
}
@@ -54,7 +54,7 @@ public final class StepStructureReference
List<ChunkWrapper> chunkWrappers)
{
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
for (ChunkWrapper chunkWrapper : chunkWrappers)
{
@@ -66,7 +66,12 @@ public final class StepStructureReference
}
else if (chunk instanceof ProtoChunk)
{
chunkWrapper.trySetStatus(STATUS);
#if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk);
}
}
@@ -28,9 +28,7 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGeneratio
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ProtoChunk;
import org.apache.logging.log4j.Logger;
@@ -85,7 +83,12 @@ public final class StepStructureStart
}
else if (chunk instanceof ProtoChunk)
{
chunkWrapper.trySetStatus(STATUS);
#if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk);
}
}
@@ -93,8 +96,7 @@ public final class StepStructureStart
if (this.environment.params.worldGenSettings.generateFeatures())
{
#elif MC_VER < MC_1_19_4
if (this.environment.params.worldGenSettings.generateStructures())
{
if (this.environment.params.worldGenSettings.generateStructures()) {
#else
if (this.environment.params.worldOptions.generateStructures())
{
@@ -112,20 +114,15 @@ public final class StepStructureStart
STRUCTURE_PLACEMENT_LOCK.lock();
#if MC_VER < MC_1_19_2
this.environment.params.generator.createStructures(this.environment.params.registry, tParams.structFeat, chunk, this.environment.params.structures,
this.environment.params.worldSeed);
environment.params.generator.createStructures(environment.params.registry, tParams.structFeat, chunk, environment.params.structures,
environment.params.worldSeed);
#elif MC_VER < MC_1_19_4
this.environment.params.generator.createStructures(this.environment.params.registry, this.environment.params.randomState, tParams.structFeat, chunk, this.environment.params.structures,
this.environment.params.worldSeed);
#elif MC_VER <= MC_1_21_3
this.environment.params.generator.createStructures(this.environment.params.registry,
this.environment.params.level.getChunkSource().getGeneratorState(),
tParams.structFeat, chunk, this.environment.params.structures);
environment.params.generator.createStructures(environment.params.registry, environment.params.randomState, tParams.structFeat, chunk, environment.params.structures,
environment.params.worldSeed);
#else
this.environment.params.generator.createStructures(this.environment.params.registry,
this.environment.params.level.getChunkSource().getGeneratorState(),
tParams.structFeat, chunk, this.environment.params.structures,
this.environment.params.level.dimension());
environment.params.generator.createStructures(environment.params.registry,
environment.params.level.getChunkSource().getGeneratorState(),
tParams.structFeat, chunk, environment.params.structures);
#endif
#if MC_VER >= MC_1_18_2
@@ -65,7 +65,12 @@ public final class StepSurface
}
else if (chunk instanceof ProtoChunk)
{
chunkWrapper.trySetStatus(STATUS);
#if MC_VER < MC_1_21
((ProtoChunk) chunk).setStatus(STATUS);
#else
((ProtoChunk) chunk).setPersistedStatus(STATUS);
#endif
chunksToDo.add(chunk);
}
}
@@ -2,8 +2,6 @@ accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering
accessible field com/mojang/blaze3d/vertex/VertexBuffer vertexCount I
@@ -33,9 +31,6 @@ accessible field net/minecraft/world/level/biome/Biome biomeCategory Lnet/minecr
#accessible field net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator settings Lnet/minecraft/core/Holder;
#accessible method net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator doCreateBiomes (Lnet/minecraft/core/Registry;Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/StructureFeatureManager;Lnet/minecraft/world/level/chunk/ChunkAccess;)V
accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
# lod generation from save file
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
@@ -52,7 +47,4 @@ accessible field net/minecraft/client/renderer/texture/TextureAtlasSprite frames
accessible field net/minecraft/client/renderer/texture/TextureAtlasSprite framesY [I
accessible field net/minecraft/client/renderer/texture/TextureAtlasSprite mainImage [Lcom/mojang/blaze3d/platform/NativeImage;
# DimensionTypeWrapper workaround
accessible field net/minecraft/world/level/dimension/DimensionType effectsLocation Lnet/minecraft/resources/ResourceLocation;
extendable class com/mojang/math/Matrix4f
@@ -1,9 +1,8 @@
accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering
accessible field com/mojang/blaze3d/vertex/VertexBuffer indexCount I
@@ -32,11 +31,6 @@ accessible field net/minecraft/world/level/biome/Biome biomeCategory Lnet/minecr
# accessible field net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator settings Lnet/minecraft/core/Holder;
#accessible method net/minecraft/world/level/levelgen/NoiseBasedChunkGenerator doCreateBiomes (Lnet/minecraft/core/Registry;Lnet/minecraft/world/level/levelgen/blending/Blender;Lnet/minecraft/world/level/StructureFeatureManager;Lnet/minecraft/world/level/chunk/ChunkAccess;)V
accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# lod generation from save file
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
@@ -2,8 +2,6 @@ accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D
@@ -17,11 +15,6 @@ accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chu
# world generation
accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# lod generation from save file
accessible field net/minecraft/world/level/chunk/storage/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
@@ -2,8 +2,6 @@ accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D
@@ -17,11 +15,6 @@ accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chu
# world generation
accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# lod generation from save file
accessible field net/minecraft/world/level/chunk/storage/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
@@ -2,8 +2,6 @@ accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D
@@ -17,11 +15,6 @@ accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chu
# world generation
accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# lod generation from save file
accessible field net/minecraft/world/level/chunk/storage/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
@@ -2,8 +2,6 @@ accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used to help determine what folder a clientLevel iss
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D
@@ -19,11 +17,6 @@ accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chu
accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z
accessible field net/minecraft/world/level/lighting/LightEngine storage Lnet/minecraft/world/level/lighting/LayerLightSectionStorage;
accessible method net/minecraft/world/level/lighting/LayerLightSectionStorage lightOnInSection (J)Z
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# lod generation from save file
accessible field net/minecraft/world/level/chunk/storage/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
@@ -2,8 +2,6 @@ accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D
@@ -18,11 +16,6 @@ accessible field net/minecraft/client/renderer/LevelRenderer visibleSections Lit
accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z
accessible field net/minecraft/world/level/lighting/LightEngine storage Lnet/minecraft/world/level/lighting/LayerLightSectionStorage;
accessible method net/minecraft/world/level/lighting/LayerLightSectionStorage lightOnInSection (J)Z
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# lod generation from save file
accessible field net/minecraft/world/level/chunk/storage/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
@@ -2,8 +2,6 @@ accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/io/File;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)D
@@ -18,11 +16,6 @@ accessible field net/minecraft/client/renderer/LevelRenderer visibleSections Lit
accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z
accessible field net/minecraft/world/level/lighting/LightEngine storage Lnet/minecraft/world/level/lighting/LayerLightSectionStorage;
accessible method net/minecraft/world/level/lighting/LayerLightSectionStorage lightOnInSection (J)Z
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# lod generation from save file
accessible field net/minecraft/world/level/chunk/storage/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
@@ -1,53 +0,0 @@
accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/nio/file/Path;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)F
accessible field net/minecraft/client/Minecraft deltaTracker Lnet/minecraft/client/DeltaTracker$Timer;
# used for grabbing vanilla rendered chunks
accessible field net/minecraft/client/renderer/LevelRenderer visibleSections Lit/unimi/dsi/fastutil/objects/ObjectArrayList;
# world generation
# accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z
accessible field net/minecraft/world/level/lighting/LightEngine storage Lnet/minecraft/world/level/lighting/LayerLightSectionStorage;
accessible method net/minecraft/world/level/lighting/LayerLightSectionStorage lightOnInSection (J)Z
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# lod generation from save file
accessible field net/minecraft/world/level/chunk/storage/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
accessible field net/minecraft/world/level/chunk/storage/IOWorker storage Lnet/minecraft/world/level/chunk/storage/RegionFileStorage;
accessible field net/minecraft/world/level/chunk/storage/RegionFileStorage regionCache Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
accessible field net/minecraft/world/level/chunk/storage/RegionFileStorage folder Ljava/nio/file/Path;
# grabbing textures
accessible class net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture
accessible method net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture getFrameX (I)I
accessible method net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture getFrameY (I)I
accessible field net/minecraft/client/renderer/texture/SpriteContents animatedTexture Lnet/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture;
accessible field net/minecraft/client/renderer/texture/SpriteContents originalImage Lcom/mojang/blaze3d/platform/NativeImage;
# UI stuff
accessible field net/minecraft/client/gui/components/AbstractButton SPRITES Lnet/minecraft/client/gui/components/WidgetSprites;
# Handles inserting the config button
accessible field net/minecraft/client/gui/layouts/HeaderAndFooterLayout headerFrame Lnet/minecraft/client/gui/layouts/FrameLayout;
accessible field net/minecraft/client/gui/layouts/FrameLayout children Ljava/util/List;
accessible class net/minecraft/client/gui/layouts/FrameLayout$ChildContainer
accessible field net/minecraft/client/gui/layouts/LinearLayout wrapped Lnet/minecraft/client/gui/layouts/GridLayout;
# hacky stuff
accessible field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
mutable field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
accessible field net/minecraft/client/gui/components/AbstractSelectionList scrollAmount D # Hack to bypass vanilla's setScrollAmount's clamp
@@ -1,52 +0,0 @@
accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/nio/file/Path;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)F
accessible field net/minecraft/client/Minecraft deltaTracker Lnet/minecraft/client/DeltaTracker$Timer;
# used for grabbing vanilla rendered chunks
accessible field net/minecraft/client/renderer/LevelRenderer visibleSections Lit/unimi/dsi/fastutil/objects/ObjectArrayList;
# world generation
# accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z
accessible field net/minecraft/world/level/lighting/LightEngine storage Lnet/minecraft/world/level/lighting/LayerLightSectionStorage;
accessible method net/minecraft/world/level/lighting/LayerLightSectionStorage lightOnInSection (J)Z
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# lod generation from save file
accessible field net/minecraft/world/level/chunk/storage/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
accessible field net/minecraft/world/level/chunk/storage/IOWorker storage Lnet/minecraft/world/level/chunk/storage/RegionFileStorage;
accessible field net/minecraft/world/level/chunk/storage/RegionFileStorage regionCache Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
accessible field net/minecraft/world/level/chunk/storage/RegionFileStorage folder Ljava/nio/file/Path;
# grabbing textures
accessible class net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture
accessible method net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture getFrameX (I)I
accessible method net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture getFrameY (I)I
accessible field net/minecraft/client/renderer/texture/SpriteContents animatedTexture Lnet/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture;
accessible field net/minecraft/client/renderer/texture/SpriteContents originalImage Lcom/mojang/blaze3d/platform/NativeImage;
# UI stuff
accessible field net/minecraft/client/gui/components/AbstractButton SPRITES Lnet/minecraft/client/gui/components/WidgetSprites;
# Handles inserting the config button
accessible field net/minecraft/client/gui/layouts/HeaderAndFooterLayout headerFrame Lnet/minecraft/client/gui/layouts/FrameLayout;
accessible field net/minecraft/client/gui/layouts/FrameLayout children Ljava/util/List;
accessible class net/minecraft/client/gui/layouts/FrameLayout$ChildContainer
accessible field net/minecraft/client/gui/layouts/LinearLayout wrapped Lnet/minecraft/client/gui/layouts/GridLayout;
# hacky stuff
accessible field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
mutable field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
+6 -22
View File
@@ -1,5 +1,5 @@
plugins {
id "fabric-loom" version "1.8-SNAPSHOT"
id "fabric-loom" version "1.6-SNAPSHOT"
}
loom {
@@ -10,32 +10,21 @@ loom {
client {
client()
setConfigName("Fabric Client")
ideConfigGenerated(true) // When true a run configuration file will be generated for IDE's. By default only set to true for the root project.
runDir("../run/client")
vmArgs(
// https://github.com/FabricMC/fabric-loom/issues/915#issuecomment-1609154390
"-Dminecraft.api.auth.host=https://nope.invalid",
"-Dminecraft.api.account.host=https://nope.invalid",
"-Dminecraft.api.session.host=https://nope.invalid",
"-Dminecraft.api.services.host=https://nope.invalid",
// https://netty.io/wiki/reference-counted-objects.html#leak-detection-levels
"-Dio.netty.leakDetection.level=advanced"
)
programArgs("--username", "Dev")
ideConfigGenerated(true)
runDir("../run")
}
server {
server()
setConfigName("Fabric Server")
ideConfigGenerated(true)
runDir("../run/server")
vmArgs("-Dio.netty.leakDetection.level=advanced")
runDir("../run")
}
}
}
remapJar {
inputFile = shadowJar.archiveFile
dependsOn shadowJar
inputFile = shadeDowngradedApi.archiveFile
dependsOn shadeDowngradedApi
}
@@ -80,11 +69,6 @@ dependencies {
addModJar(fabricApi.module("fabric-events-interaction-v0", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-rendering-v1", rootProject.fabric_api_version)) // TODO: Remove this as it is only needed in 1 line (FabricClientProxy)
addModJar(fabricApi.module("fabric-networking-api-v1", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-entity-events-v1", rootProject.fabric_api_version))
if (minecraft_version >= "1.19.2")
addModJar(fabricApi.module("fabric-command-api-v2", rootProject.fabric_api_version))
else // < 1.19.2
addModJar(fabricApi.module("fabric-command-api-v1", rootProject.fabric_api_version))
// used by mod menu in MC 1.20.6+
addModJar(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version))
@@ -19,8 +19,9 @@
package com.seibel.distanthorizons.fabric;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.AbstractPluginPacketSender;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
@@ -37,29 +38,21 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAcce
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.fabric.wrappers.modAccessor.SodiumAccessor;
//import io.netty.buffer.ByteBuf;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientChunkEvents;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.fabricmc.fabric.api.event.player.AttackBlockCallback;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.TitleScreen;
#if MC_VER >= MC_1_20_6
import com.seibel.distanthorizons.common.CommonPacketPayload;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
#else
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
#endif
#if MC_VER < MC_1_19_4
import java.nio.FloatBuffer;
#endif
import java.util.HashSet;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import net.minecraft.client.multiplayer.ClientLevel;
@@ -67,6 +60,7 @@ import net.minecraft.world.InteractionResult;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.phys.HitResult;
import org.apache.logging.log4j.Logger;
import org.joml.Matrix4f;
import org.lwjgl.glfw.GLFW;
/**
@@ -95,7 +89,6 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
* Registers Fabric Events
* @author Ran
*/
@Override
public void registerEvents()
{
LOGGER.info("Registering Fabric Client Events");
@@ -113,7 +106,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// tick events //
//=============//
ClientTickEvents.START_CLIENT_TICK.register((client) -> { ClientApi.INSTANCE.clientTickEvent(); });
ClientTickEvents.START_CLIENT_TICK.register((client) -> ClientApi.INSTANCE.clientTickEvent());
@@ -123,12 +116,9 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// ClientChunkLoadEvent
ClientChunkEvents.CHUNK_LOAD.register((level, chunk) ->
{
if (MC.clientConnectedToDedicatedServer())
{
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level);
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, wrappedLevel), wrappedLevel);
}
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel);
});
// (kinda) block break event
@@ -143,7 +133,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// executor to prevent locking up the render/event thread
// if the getChunk() takes longer than expected
// (which can be caused by certain mods)
AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor();
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
@@ -155,7 +145,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level);
SharedApi.INSTANCE.chunkBlockChangedEvent(
new ChunkWrapper(chunk, wrappedLevel),
new ChunkWrapper(chunk, level, wrappedLevel),
wrappedLevel
);
}
@@ -183,7 +173,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// executor to prevent locking up the render/event thread
// if the getChunk() takes longer than expected
// (which can be caused by certain mods)
AbstractExecutorService executor = ThreadPoolUtil.getFileHandlerExecutor();
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor != null)
{
executor.execute(() ->
@@ -195,7 +185,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper((ClientLevel) level);
SharedApi.INSTANCE.chunkBlockChangedEvent(
new ChunkWrapper(chunk, wrappedLevel),
new ChunkWrapper(chunk, level, wrappedLevel),
wrappedLevel
);
}
@@ -210,6 +200,14 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
});
// Client Chunk Save
ClientChunkEvents.CHUNK_UNLOAD.register((level, chunk) ->
{
IClientLevelWrapper wrappedLevel = ClientLevelWrapper.getWrapper(level);
SharedApi.INSTANCE.chunkUnloadEvent(new ChunkWrapper(chunk, level, wrappedLevel), wrappedLevel);
});
//==============//
// render event //
@@ -229,7 +227,7 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
this.clientApi.renderLods(ClientLevelWrapper.getWrapper(renderContext.world()),
modelViewMatrix,
projectionMatrix,
#if MC_VER < MC_1_21_1
#if MC_VER < MC_1_21
renderContext.tickDelta()
#else
renderContext.tickCounter().getGameTimeDeltaTicks()
@@ -237,56 +235,6 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
);
});
// TODO add to forge and neo
WorldRenderEvents.AFTER_ENTITIES.register((renderContext) ->
{
Mat4f projectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix());
Mat4f modelViewMatrix;
#if MC_VER < MC_1_20_6
modelViewMatrix = McObjectConverter.Convert(renderContext.matrixStack().last().pose());
#else
modelViewMatrix = McObjectConverter.Convert(renderContext.positionMatrix());
#endif
this.clientApi.renderFadeOpaque(
modelViewMatrix,
projectionMatrix,
#if MC_VER < MC_1_21_1
renderContext.tickDelta(),
#else
renderContext.tickCounter().getGameTimeDeltaTicks(),
#endif
ClientLevelWrapper.getWrapper(renderContext.world())
);
});
// TODO add to forge and neo
WorldRenderEvents.AFTER_TRANSLUCENT.register((renderContext) ->
{
Mat4f projectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix());
Mat4f modelViewMatrix;
#if MC_VER < MC_1_20_6
modelViewMatrix = McObjectConverter.Convert(renderContext.matrixStack().last().pose());
#else
modelViewMatrix = McObjectConverter.Convert(renderContext.positionMatrix());
#endif
this.clientApi.renderFade(
modelViewMatrix,
projectionMatrix,
#if MC_VER < MC_1_21_1
renderContext.tickDelta(),
#else
renderContext.tickCounter().getGameTimeDeltaTicks(),
#endif
ClientLevelWrapper.getWrapper(renderContext.world())
);
});
// Debug keyboard event
// FIXME: Use better hooks so it doesn't trigger key press events in text boxes
ClientTickEvents.END_CLIENT_TICK.register(client ->
@@ -303,28 +251,17 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// networking event //
//==================//
#if MC_VER >= MC_1_20_6
PayloadTypeRegistry.playS2C().register(CommonPacketPayload.TYPE, new CommonPacketPayload.Codec());
ClientPlayNetworking.registerGlobalReceiver(CommonPacketPayload.TYPE, (payload, context) ->
{
if (payload.message() == null)
{
return;
}
ClientApi.INSTANCE.pluginMessageReceived(payload.message());
});
#else
ClientPlayNetworking.registerGlobalReceiver(AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE, (client, handler, buffer, packetSender) ->
{
// Forge packet ID
buffer.readByte();
AbstractNetworkMessage message = AbstractPluginPacketSender.decodeMessage(buffer);
if (message != null)
{
ClientApi.INSTANCE.pluginMessageReceived(message);
}
});
#endif
// ClientPlayNetworking.registerGlobalReceiver(new ResourceLocation(ModInfo.NETWORKING_RESOURCE_NAMESPACE, ModInfo.MULTIVERSE_PLUGIN_NAMESPACE),
// (Minecraft client, ClientPacketListener handler, FriendlyByteBuf friendlyByteBuf, PacketSender responseSender) ->
// {
// // converting to a ByteBuf is necessary otherwise Fabric will complain when the game boots
// ByteBuf nettyByteBuf = friendlyByteBuf.asByteBuf();
//
// // remove the Bukkit/Forge packet ID byte
// nettyByteBuf.readByte();
//
// ClientApi.INSTANCE.serverMessageReceived(nettyByteBuf);
// });
}
public void onKeyInput()
@@ -353,14 +290,14 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// Diff and trigger events
for (int keyCode : currentKeyDown)
{
if (!this.previouslyPressKeyCodes.contains(keyCode))
if (!previouslyPressKeyCodes.contains(keyCode))
{
ClientApi.INSTANCE.keyPressedEvent(keyCode);
}
}
// Update the set
this.previouslyPressKeyCodes = currentKeyDown;
previouslyPressKeyCodes = currentKeyDown;
}
}
@@ -27,7 +27,6 @@ import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.*;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.fabric.wrappers.modAccessor.*;
@@ -40,13 +39,6 @@ import net.minecraft.commands.CommandSourceStack;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.MinecraftServer;
import org.apache.logging.log4j.Logger;
import org.lwjgl.util.tinyfd.TinyFileDialogs;
#if MC_VER >= MC_1_19_2
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
#else // < 1.19.2
import net.fabricmc.fabric.api.command.v1.CommandRegistrationCallback;
#endif
import javax.swing.*;
import java.awt.*;
@@ -59,22 +51,14 @@ import java.util.function.Consumer;
*/
public class FabricMain extends AbstractModInitializer implements ClientModInitializer, DedicatedServerModInitializer
{
#if MC_VER >= MC_1_21_1
private static final ResourceLocation INITIAL_PHASE = ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.DEDICATED_SERVER_INITIAL_PATH);
#else
private static final ResourceLocation INITIAL_PHASE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.DEDICATED_SERVER_INITIAL_PATH);
#endif
private static final ResourceLocation INITIAL_PHASE = ResourceLocation.tryParse("distanthorizons:dedicated_server_initial");
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
@Override
protected void createInitialBindings()
{
SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE);
SingletonInjector.INSTANCE.bind(IPluginPacketSender.class, new FabricPluginPacketSender());
}
protected void createInitialBindings() { SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE); }
@Override
protected IEventProxy createClientProxy() { return new FabricClientProxy(); }
@@ -96,7 +80,10 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
String indiumMissingMessage = ModInfo.READABLE_NAME + " needs Indium to work with Sodium.\nPlease download Indium from https://modrinth.com/mod/indium";
LOGGER.fatal(indiumMissingMessage);
TinyFileDialogs.tinyfd_messageBox(ModInfo.READABLE_NAME, indiumMissingMessage, "ok", "error", false);
if (!GraphicsEnvironment.isHeadless())
{
JOptionPane.showMessageDialog(null, indiumMissingMessage, ModInfo.READABLE_NAME, JOptionPane.INFORMATION_MESSAGE);
}
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
String errorMessage = "loading Distant Horizons. Distant Horizons requires Indium in order to run with Sodium.";
@@ -115,15 +102,7 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
}
@Override
protected void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler)
{
CommandRegistrationCallback.EVENT.register(
(dispatcher, registryAccess #if MC_VER >= MC_1_19_2 , environment #endif ) ->
{
eventHandler.accept(dispatcher);
}
);
}
protected void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler) { }
@Override
protected void subscribeClientStartedEvent(Runnable eventHandler) { ClientLifecycleEvents.CLIENT_STARTED.register((mc) -> eventHandler.run()); }
@@ -140,22 +119,16 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
{
SingletonInjector.INSTANCE.runDelayedSetup();
if (!Config.Client.Advanced.Graphics.Fog.enableVanillaFog.get() && SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("bclib"))
{
if (Config.Client.Advanced.Graphics.Fog.disableVanillaFog.get() && SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("bclib"))
ModAccessorInjector.INSTANCE.get(IBCLibAccessor.class).setRenderCustomFog(false); // Remove BCLib's fog
}
#if MC_VER >= MC_1_20_1
if (SingletonInjector.INSTANCE.get(IModChecker.class).isModLoaded("sodium"))
{
ModAccessorInjector.INSTANCE.get(ISodiumAccessor.class).setFogOcclusion(false);
}
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
#endif
if (ConfigBase.INSTANCE == null)
{
throw new IllegalStateException("Config was not initialized. Make sure to call LodCommonMain.initConfig() before calling this method.");
}
}
}
@@ -1,46 +0,0 @@
package com.seibel.distanthorizons.fabric;
import com.seibel.distanthorizons.common.AbstractPluginPacketSender;
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.server.level.ServerPlayer;
#if MC_VER >= MC_1_20_6
import com.seibel.distanthorizons.common.CommonPacketPayload;
#else // < 1.20.6
import net.fabricmc.fabric.api.networking.v1.PacketByteBufs;
import net.minecraft.network.FriendlyByteBuf;
#endif
public class FabricPluginPacketSender extends AbstractPluginPacketSender
{
@Override
public void sendToServer(AbstractNetworkMessage message)
{
#if MC_VER >= MC_1_20_6
ClientPlayNetworking.send(new CommonPacketPayload(message));
#else // < 1.20.6
FriendlyByteBuf buffer = PacketByteBufs.create();
// Forge packet ID
buffer.writeByte(0);
AbstractPluginPacketSender.encodeMessage(buffer, message);
ClientPlayNetworking.send(WRAPPER_PACKET_RESOURCE, buffer);
#endif
}
@Override
public void sendToClient(ServerPlayer serverPlayer, AbstractNetworkMessage message)
{
#if MC_VER >= MC_1_20_6
ServerPlayNetworking.send(serverPlayer, new CommonPacketPayload(message));
#else // < 1.20.6
FriendlyByteBuf buffer = PacketByteBufs.create();
// Forge packet ID
buffer.writeByte(0);
AbstractPluginPacketSender.encodeMessage(buffer, message);
ServerPlayNetworking.send(serverPlayer, WRAPPER_PACKET_RESOURCE, buffer);
#endif
}
}
@@ -1,7 +1,5 @@
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.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
@@ -12,14 +10,11 @@ import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.fabric.testing.TestWorldGenBindingEvent;
import net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerWorldEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayConnectionEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.client.multiplayer.ClientLevel;
@@ -27,14 +22,6 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import org.apache.logging.log4j.Logger;
#if MC_VER >= MC_1_20_6
import com.seibel.distanthorizons.common.CommonPacketPayload;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
#else
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import com.seibel.distanthorizons.common.AbstractPluginPacketSender;
#endif
import java.util.function.Supplier;
/**
@@ -50,25 +37,21 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
private static final ServerApi SERVER_API = ServerApi.INSTANCE;
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private final boolean isDedicatedServer;
private final boolean isDedicated;
public static Supplier<Boolean> isGenerationThreadChecker = null;
//=============//
// constructor //
//=============//
public FabricServerProxy(boolean isDedicatedServer)
public FabricServerProxy(boolean isDedicated)
{
this.isDedicatedServer = isDedicatedServer;
this.isDedicated = isDedicated;
}
// TODO rename
private boolean isValidTime()
{
if (this.isDedicatedServer)
if (isDedicated)
{
return true;
}
@@ -82,37 +65,29 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
private ServerPlayerWrapper getServerPlayerWrapper(ServerPlayer player) { return ServerPlayerWrapper.getWrapper(player); }
/** Registers Fabric Events */
@Override
public void registerEvents()
{
LOGGER.info("Registering Fabric Server Events");
isGenerationThreadChecker = BatchGenerationEnvironment::isCurrentThreadDistantGeneratorThread;
/* Register the mod needed event callbacks */
// 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
//TODO: Check if both of these use the correct timed events. (i.e. is it 'ed' or 'ing' one?)
ServerLifecycleEvents.SERVER_STARTING.register((server) ->
{
if (this.isValidTime())
if (isValidTime())
{
ServerApi.INSTANCE.serverLoadEvent(this.isDedicatedServer);
ServerApi.INSTANCE.serverLoadEvent(isDedicated);
}
});
// ServerWorldUnloadEvent
ServerLifecycleEvents.SERVER_STOPPED.register((server) ->
{
if (this.isValidTime())
if (isValidTime())
{
ServerApi.INSTANCE.serverUnloadEvent();
}
@@ -121,28 +96,28 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
// ServerLevelLoadEvent
ServerWorldEvents.LOAD.register((server, level) ->
{
if (this.isValidTime())
if (isValidTime())
{
ServerApi.INSTANCE.serverLevelLoadEvent(this.getServerLevelWrapper(level));
ServerApi.INSTANCE.serverLevelLoadEvent(getServerLevelWrapper(level));
}
});
// ServerLevelUnloadEvent
ServerWorldEvents.UNLOAD.register((server, level) ->
{
if (this.isValidTime())
if (isValidTime())
{
ServerApi.INSTANCE.serverLevelUnloadEvent(this.getServerLevelWrapper(level));
ServerApi.INSTANCE.serverLevelUnloadEvent(getServerLevelWrapper(level));
}
});
// ServerChunkLoadEvent
ServerChunkEvents.CHUNK_LOAD.register((server, chunk) ->
{
ILevelWrapper level = this.getServerLevelWrapper((ServerLevel) chunk.getLevel());
if (this.isValidTime())
ILevelWrapper level = getServerLevelWrapper((ServerLevel) chunk.getLevel());
if (isValidTime())
{
ServerApi.INSTANCE.serverChunkLoadEvent(
new ChunkWrapper(chunk, level),
new ChunkWrapper(chunk, chunk.getLevel(), level),
level);
}
});
@@ -150,57 +125,18 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
ServerPlayConnectionEvents.JOIN.register((handler, sender, server) ->
{
if (this.isValidTime())
if (isValidTime())
{
ServerApi.INSTANCE.serverPlayerJoinEvent(this.getServerPlayerWrapper(handler.player));
ServerApi.INSTANCE.serverPlayerJoinEvent(getServerPlayerWrapper(handler.player));
}
});
ServerPlayConnectionEvents.DISCONNECT.register((handler, server) ->
{
if (this.isValidTime())
if (isValidTime())
{
ServerApi.INSTANCE.serverPlayerDisconnectEvent(this.getServerPlayerWrapper(handler.player));
ServerApi.INSTANCE.serverPlayerDisconnectEvent(getServerPlayerWrapper(handler.player));
}
});
ServerEntityWorldChangeEvents.AFTER_PLAYER_CHANGE_WORLD.register((player, originLevel, destinationLevel) ->
{
if (this.isValidTime())
{
ServerApi.INSTANCE.serverPlayerLevelChangeEvent(
this.getServerPlayerWrapper(player),
this.getServerLevelWrapper(originLevel),
this.getServerLevelWrapper(destinationLevel)
);
}
});
#if MC_VER >= MC_1_20_6
PayloadTypeRegistry.playC2S().register(CommonPacketPayload.TYPE, new CommonPacketPayload.Codec());
if (this.isDedicatedServer)
{
PayloadTypeRegistry.playS2C().register(CommonPacketPayload.TYPE, new CommonPacketPayload.Codec());
}
ServerPlayNetworking.registerGlobalReceiver(CommonPacketPayload.TYPE, (payload, context) ->
{
if (payload.message() == null)
{
return;
}
ServerApi.INSTANCE.pluginMessageReceived(ServerPlayerWrapper.getWrapper(context.player()), payload.message());
});
#else
ServerPlayNetworking.registerGlobalReceiver(AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE, (server, serverPlayer, handler, buffer, packetSender) ->
{
// Forge packet ID
buffer.readByte();
AbstractNetworkMessage message = AbstractPluginPacketSender.decodeMessage(buffer);
if (message != null)
{
ServerApi.INSTANCE.pluginMessageReceived(ServerPlayerWrapper.getWrapper(serverPlayer), message);
}
});
#endif
}
}
@@ -41,6 +41,15 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ClientLevel.class)
public class MixinClientLevel
{
// //Moved to MixinClientPacketListener
// @Inject(method = "<init>", at = @At("TAIL"))
// private void loadWorldEvent(ClientPacketListener clientPacketListener, ClientLevel.ClientLevelData clientLevelData, ResourceKey resourceKey,
// #if MC_VER >= MC_1_18_2 Holder holder, #else DimensionType dimensionType, #endif int i,
// #if MC_VER >= MC_1_18_2 int j, #endif Supplier supplier, LevelRenderer levelRenderer, boolean bl, long l, CallbackInfo ci)
// {
// ClientApi.INSTANCE.clientLevelLoadEvent(WorldWrapper.getWorldWrapper((ClientLevel)(Object)this));
// }
// Moved to overriding the enableChunkLight(...) method over at ClientPacketListener for 1.20+
#if MC_VER >= MC_1_18_2 && MC_VER < MC_1_20_1 // Only the setLightReady is only available after 1.18. This ensures the light data is ready.
@Inject(method = "setLightReady", at = @At("HEAD"))
@@ -51,9 +60,7 @@ public class MixinClientLevel
if (chunk != null && !chunk.isClientLightReady())
{
SharedApi.INSTANCE.chunkLoadEvent(
new ChunkWrapper(chunk, ClientLevelWrapper.getWrapper(clientLevel)),
ClientLevelWrapper.getWrapper(clientLevel));
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, clientLevel, ClientLevelWrapper.getWrapper(clientLevel)), ClientLevelWrapper.getWrapper(clientLevel));
}
}
#endif
@@ -24,11 +24,28 @@ public class MixinClientPacketListener
@Shadow
private ClientLevel level;
@Unique
private ClientLevel previousLevel;
@Inject(method = "handleLogin", at = @At("RETURN"))
void onHandleLoginEnd(CallbackInfo ci)
void onHandleLoginEnd(CallbackInfo ci) { ClientApi.INSTANCE.onClientOnlyConnected(); }
@Inject(method = "handleRespawn", at = @At("HEAD"))
void onHandleRespawnStart(CallbackInfo ci) { this.previousLevel = this.level; }
@Inject(method = "handleRespawn", at = @At("RETURN"))
void onHandleRespawnEnd(CallbackInfo ci)
{
ClientApi.INSTANCE.onClientOnlyConnected();
ClientApi.INSTANCE.clientLevelLoadEvent(ClientLevelWrapper.getWrapper(this.level, true));
// If the player changes dimensions the "this.level" will be changed halfway through the respawn method.
// By checking if the object references are the same, we can see if the previous level should be unloaded
// or if the player just respawned in the same level.
if (this.previousLevel != this.level)
{
ClientApi.INSTANCE.clientLevelUnloadEvent(ClientLevelWrapper.getWrapper(this.previousLevel));
ClientApi.INSTANCE.clientLevelLoadEvent(ClientLevelWrapper.getWrapper(this.level));
}
this.previousLevel = null;
}
#if MC_VER < MC_1_19_4
@@ -38,6 +55,10 @@ public class MixinClientPacketListener
#endif
void onCleanupStart(CallbackInfo ci)
{
if (this.level != null)
{
ClientApi.INSTANCE.clientLevelUnloadEvent(ClientLevelWrapper.getWrapper(this.level));
}
ClientApi.INSTANCE.onClientOnlyDisconnected();
}
@@ -46,7 +67,7 @@ public class MixinClientPacketListener
void onEnableChunkLight(LevelChunk chunk, int x, int z, CallbackInfo ci)
{
IClientLevelWrapper clientLevel = ClientLevelWrapper.getWrapper((ClientLevel) chunk.getLevel());
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, clientLevel), clientLevel);
SharedApi.INSTANCE.chunkLoadEvent(new ChunkWrapper(chunk, chunk.getLevel(), clientLevel), clientLevel);
}
#endif

Some files were not shown because too many files have changed in this diff Show More