Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 44cf6f6955 |
+2
-1
@@ -5,13 +5,14 @@ root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = crlf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = false
|
||||
max_line_length = 1000
|
||||
tab_width = 4
|
||||
trim_trailing_whitespace = false
|
||||
ij_continuation_indent_size = 4
|
||||
ij_continuation_indent_size = 8
|
||||
ij_formatter_off_tag = @formatter:off
|
||||
ij_formatter_on_tag = @formatter:on
|
||||
ij_formatter_tags_enabled = true
|
||||
|
||||
@@ -26,10 +26,6 @@ Merged/
|
||||
# Folder created by the buildAll scripts
|
||||
buildAllJars/
|
||||
|
||||
relocate_natives/.venv/
|
||||
relocate_natives/__pycache__/
|
||||
relocate_natives/apple-codesign/
|
||||
|
||||
# file from notepad++
|
||||
*.bak
|
||||
|
||||
|
||||
+26
-20
@@ -18,7 +18,7 @@ variables:
|
||||
.build_java:
|
||||
#image: eclipse-temurin:17
|
||||
cache:
|
||||
key: "gradleCache_$CI_JOB_NAME_SLUG"
|
||||
key: "gradleCache"
|
||||
policy: pull-push
|
||||
paths:
|
||||
- .gradle
|
||||
@@ -35,28 +35,34 @@ build:
|
||||
stage: build
|
||||
parallel:
|
||||
matrix:
|
||||
- MC_VER: [
|
||||
"1.21.11", "1.21.10", "1.21.9", "1.21.8", "1.21.6", "1.21.5", "1.21.4", "1.21.3", "1.21.1",
|
||||
"1.20.6", "1.20.4", "1.20.2", "1.20.1",
|
||||
"1.19.4", "1.19.2",
|
||||
"1.18.2",
|
||||
"1.17.1",
|
||||
"1.16.5"
|
||||
]
|
||||
- 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"]
|
||||
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/;
|
||||
- ./gradlew build -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
|
||||
- ./gradlew mergeJars -PmcVer="${MC_VER}" -PinfoGitCommit="${CI_COMMIT_SHA}" -PinfoGitBranch="${CI_COMMIT_BRANCH}" -PinfoBuildSource="GitLab CI (${CI_PIPELINE_ID})" --gradle-user-home cache/;
|
||||
- cp ./fabric/build/libs/* ./forge/build/libs/* ./neoforge/build/libs/* ./build/merged/* . || true
|
||||
artifacts:
|
||||
name: "NightlyBuild_${MC_VER}-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
|
||||
paths:
|
||||
- ./*.jar
|
||||
- Merged/*.jar
|
||||
- quilt/build/libs/*.jar
|
||||
- fabric/build/libs/*.jar
|
||||
- forge/build/libs/*.jar
|
||||
- neoforge/build/libs/*.jar
|
||||
exclude:
|
||||
- ./*-all.jar
|
||||
- ./*-dev.jar
|
||||
- ./*-sources.jar
|
||||
# TODO: There is a lot of duplicate stuff here, try to maybe make it smaller
|
||||
- fabric/build/libs/*-all.jar
|
||||
- fabric/build/libs/*-dev.jar
|
||||
- fabric/build/libs/*-sources.jar
|
||||
- quilt/build/libs/*-all.jar
|
||||
- quilt/build/libs/*-dev.jar
|
||||
- quilt/build/libs/*-sources.jar
|
||||
- forge/build/libs/*-all.jar
|
||||
- forge/build/libs/*-dev.jar
|
||||
- forge/build/libs/*-sources.jar
|
||||
- neoforge/build/libs/*-all.jar
|
||||
- neoforge/build/libs/*-dev.jar
|
||||
- neoforge/build/libs/*-sources.jar
|
||||
expire_in: 14 days
|
||||
when: always
|
||||
extends: .build_java
|
||||
@@ -71,15 +77,15 @@ api:
|
||||
# this also runs unit tests
|
||||
- ./gradlew api:build --gradle-user-home cache/;
|
||||
- ./gradlew api:addSourcesToCompiledJar --gradle-user-home cache/;
|
||||
- cp ./coreSubProjects/api/build/libs/merged/* .
|
||||
artifacts:
|
||||
name: "NightlyBuild_Api-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
|
||||
name: "Api_NightlyBuild-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
|
||||
paths:
|
||||
- ./*.jar
|
||||
- coreSubProjects/api/build/libs/merged/*.jar
|
||||
# can be uncommented if we don't want a jar with the source code
|
||||
# - coreSubProjects/api/build/libs/*.jar
|
||||
exclude:
|
||||
- ./*-all.jar
|
||||
- ./*-dev.jar
|
||||
- ./*-sources.jar
|
||||
- coreSubProjects/api/build/libs/merged/*-all.jar
|
||||
- coreSubProjects/api/build/libs/merged/*-sources.jar
|
||||
expire_in: 1 day
|
||||
when: always
|
||||
extends: .build_java
|
||||
|
||||
@@ -6,13 +6,13 @@ Or click the checkbox once the issue has been created.
|
||||
-->
|
||||
|
||||
1. [ ] Check the FAQ to see if your issue has already been reported and has a solution:
|
||||
[Problems-and-solutions](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions)
|
||||
[Problems-and-solutions](https://gitlab.com/jeseibel/distant-horizons/-/wikis/2-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions)
|
||||
|
||||
2. [ ] Make sure you are not using any mods on the incompatible list:
|
||||
[Mod support FAQ](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/4-mod-support/Mod-Support)
|
||||
[Mod support FAQ](https://gitlab.com/jeseibel/distant-horizons/-/wikis/2-frequently-asked-questions/4-mod-support/Mod-Support)
|
||||
|
||||
3. [ ] Check the existing issues to verify that your bug hasn't already been submitted:
|
||||
[Issues](https://gitlab.com/distant-horizons-team/distant-horizons/-/issues)
|
||||
[Issues](https://gitlab.com/jeseibel/distant-horizons/-/issues/)
|
||||
|
||||
4. [ ] Upload Minecraft's crash report and/or log. \
|
||||
Minecraft crash reports are located in: `.minecraft/crash-reports` \
|
||||
|
||||
@@ -1,37 +1,3 @@
|
||||
## Check off each item in this list before submitting:
|
||||
|
||||
<!--
|
||||
To mark a section as complete either put an "x" in between the square brackets, example: "[x]"
|
||||
Or click the checkbox once the issue has been created.
|
||||
-->
|
||||
|
||||
1. [ ] Check the FAQ to see if your issue has already been reported and has a solution:
|
||||
[Problems-and-solutions](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions)
|
||||
|
||||
2. [ ] Make sure you are not using any mods on the incompatible list:
|
||||
[Mod support FAQ](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/4-mod-support/Mod-Support)
|
||||
|
||||
3. [ ] Check the existing issues to verify that your bug hasn't already been submitted:
|
||||
[Issues](https://gitlab.com/distant-horizons-team/distant-horizons/-/issues)
|
||||
|
||||
4. [ ] Upload Minecraft's crash report and/or log. \
|
||||
Minecraft crash reports are located in: `.minecraft/crash-reports` \
|
||||
Minecraft logs are located in: `.minecraft/logs`
|
||||
|
||||
5. [ ] Upload your Distant Horizons Config. \
|
||||
The config is found in: `.minecraft/configs/DistantHorizons.toml`
|
||||
|
||||
6. [ ] Fill out the information below:
|
||||
|
||||
* **minecraft version**:
|
||||
|
||||
* **Distant Horizons version**:
|
||||
|
||||
* **Mod loader**:
|
||||
|
||||
* **Installed mods (list the smallest number of mods that you can use to re-create the bug)**:
|
||||
|
||||
* **Describe the bug**:
|
||||
|
||||
* **Steps to reproduce the bug**:
|
||||
Before creating an issue, please select the appropriate template from the dropdown above.
|
||||
|
||||
The template will walk you through submitting a bug, feature, or improvement request.
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
- [ ] Check the existing [feature requests](https://gitlab.com/distant-horizons-team/distant-horizons/-/issues?sort=updated_desc&state=opened&label_name%5B%5D=Feature) to verify that your feature hasn't already been suggested.
|
||||
- [ ] Check the existing [feature requests](https://gitlab.com/jeseibel/distant-horizons/-/issues/?sort=updated_desc&state=opened&label_name%5B%5D=Feature) to verify that your feature hasn't already been suggested.
|
||||
|
||||
1. **Describe the feature**:
|
||||
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
1. Check the existing [improvement requests](https://gitlab.com/distant-horizons-team/distant-horizons/-/issues?sort=updated_desc&state=all&label_name%5B%5D=Improvement) to verify that your improvement hasn't already been suggested.
|
||||
1. Check the existing [improvement requests](https://gitlab.com/jeseibel/distant-horizons/-/issues/?sort=updated_desc&state=all&label_name%5B%5D=Improvement) to verify that your improvement hasn't already been suggested.
|
||||
|
||||
2. **Describe the improvement**:
|
||||
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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 +0,0 @@
|
||||
Distant Horizons logos © 2024 by Pankakes are licensed under CC BY-SA 4.0
|
||||
@@ -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`
|
||||
|
||||
+49
-137
@@ -1,14 +1,3 @@
|
||||
import com.github.jengelman.gradle.plugins.shadow.transformers.Transformer
|
||||
import com.github.jengelman.gradle.plugins.shadow.transformers.TransformerContext
|
||||
import org.apache.tools.zip.ZipEntry
|
||||
|
||||
import javax.annotation.Nonnull
|
||||
import org.apache.tools.zip.ZipOutputStream
|
||||
|
||||
import java.util.function.Function
|
||||
import java.util.function.Predicate
|
||||
|
||||
|
||||
plugins {
|
||||
id "java"
|
||||
|
||||
@@ -16,13 +5,13 @@ plugins {
|
||||
id "com.github.johnrengelman.shadow" version '8.1.1' apply false
|
||||
|
||||
// Plugin to create merged jars
|
||||
id "io.github.pacifistmc.forgix" version "1.3.4"
|
||||
id "io.github.pacifistmc.forgix" version "1.2.9"
|
||||
|
||||
// Manifold preprocessor
|
||||
id "systems.manifold.manifold-gradle-plugin" version "0.0.2-alpha"
|
||||
|
||||
// Architectury is used here only as a replacement for forge's own loom
|
||||
id "dev.architectury.loom" version "1.13-SNAPSHOT" apply false
|
||||
id "dev.architectury.loom" version "1.6-SNAPSHOT" apply false
|
||||
}
|
||||
|
||||
|
||||
@@ -45,10 +34,8 @@ def writeBuildGradlePredefine(List<String> mcVers, int mcIndex)
|
||||
String verStr = mcVers[i].replace(".", "_");
|
||||
sb.append("MC_" + verStr + "=" + i.toString() + "\n");
|
||||
|
||||
if (mcIndex == i)
|
||||
{
|
||||
if (mcIndex == i)
|
||||
sb.append("MC_VER=" + i.toString() + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,71 +59,38 @@ project.gradle.ext.getProperties().each { prop ->
|
||||
// Sets up manifold stuff
|
||||
writeBuildGradlePredefine(rootProject.mcVers, rootProject.mcIndex)
|
||||
|
||||
|
||||
|
||||
|
||||
// Sets up the version string (the name we use for our jar)
|
||||
rootProject.versionStr = rootProject.mod_version + "-" + rootProject.minecraft_version // + "-" + new Date().format("yyyy_MM_dd_HH_mm")
|
||||
// Forgix settings (used for merging jars)
|
||||
forgix {
|
||||
group = "com.seibel.distanthorizons"
|
||||
mergedJarName = "DistantHorizons-${rootProject.versionStr}.jar"
|
||||
|
||||
class NativeTransformer implements Transformer {
|
||||
private Predicate<String> fileMatcher
|
||||
private Function<String, String> filePathMapper
|
||||
|
||||
private final HashMap<String, String> replacements = new HashMap()
|
||||
private final HashMap<String, byte[]> rewrittenFiles = new HashMap()
|
||||
private var nativeRelocator
|
||||
|
||||
public File rootDir
|
||||
|
||||
void matchFiles(Predicate<String> matcher) {
|
||||
fileMatcher = matcher
|
||||
}
|
||||
|
||||
void mapPaths(Function<String, String> mapper) {
|
||||
filePathMapper = mapper
|
||||
}
|
||||
|
||||
void relocateNative(String target, String replacement) {
|
||||
if (replacement.length() > target.length()) {
|
||||
throw new GradleException("Length of value \"${replacement}\" exceeds the length of \"${target}\": ${replacement.length()} > ${target.length()}")
|
||||
if (findProject(":forge"))
|
||||
forge {
|
||||
jarLocation = "build/libs/DistantHorizons-forge-${rootProject.versionStr}.jar"
|
||||
}
|
||||
|
||||
if (findProject(":neoforge"))
|
||||
custom {
|
||||
projectName = "neoforge"
|
||||
jarLocation = "build/libs/DistantHorizons-neoforge-${rootProject.versionStr}.jar"
|
||||
}
|
||||
|
||||
replacements.put(target, replacement)
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean canTransformResource(@Nonnull FileTreeElement element) {
|
||||
return fileMatcher.test(element.name)
|
||||
}
|
||||
|
||||
@Override
|
||||
void transform(@Nonnull TransformerContext context) {
|
||||
byte[] content = context.is.readAllBytes()
|
||||
|
||||
if (nativeRelocator == null) {
|
||||
nativeRelocator = new NativeRelocator(rootDir.toPath().resolve("relocate_natives"))
|
||||
if (findProject(":fabric"))
|
||||
fabric {
|
||||
jarLocation = "build/libs/DistantHorizons-fabric-${rootProject.versionStr}.jar"
|
||||
}
|
||||
|
||||
try {
|
||||
String path = filePathMapper != null
|
||||
? filePathMapper.apply(context.path)
|
||||
: context.path
|
||||
content = nativeRelocator.processBinary(path, content, replacements)
|
||||
|
||||
rewrittenFiles.put(path, content)
|
||||
if (findProject(":quilt"))
|
||||
quilt {
|
||||
jarLocation = "build/libs/DistantHorizons-quilt-${rootProject.versionStr}.jar"
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throw new GradleException("Failed to relocate", e)
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
boolean hasTransformedResource() { return !rewrittenFiles.isEmpty() }
|
||||
|
||||
@Override
|
||||
void modifyOutputStream(@Nonnull ZipOutputStream os, boolean preserveFileTimestamps) {
|
||||
for (Map.Entry<String, byte[]> rewrittenFile : rewrittenFiles.entrySet()) {
|
||||
os.putNextEntry(new ZipEntry(rewrittenFile.key))
|
||||
os.write(rewrittenFile.value)
|
||||
}
|
||||
}
|
||||
removeDuplicate "com.seibel.distanthorizons"
|
||||
}
|
||||
|
||||
subprojects { p ->
|
||||
@@ -205,7 +159,6 @@ subprojects { p ->
|
||||
if (findProject(":neoforge"))
|
||||
developmentNeoForge.extendsFrom coreProjects
|
||||
|
||||
// TODO remove unused fabricLike
|
||||
if (findProject(":fabricLike") && p != project(":fabricLike")) {
|
||||
// Shadow fabricLike
|
||||
fabricLike
|
||||
@@ -226,21 +179,12 @@ subprojects { p ->
|
||||
if (isMinecraftSubProject) {
|
||||
annotationProcessor("systems.manifold:manifold-preprocessor:${rootProject.manifold_version}")
|
||||
}
|
||||
|
||||
// Log4j
|
||||
if (p == project(":core"))
|
||||
{
|
||||
// the standalone core jar needs logging shaded otherwise it won't run
|
||||
forgeShadowMe("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}")
|
||||
forgeShadowMe("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}")
|
||||
}
|
||||
else
|
||||
{
|
||||
// When running in MC, MC already includes logging
|
||||
implementation("org.apache.logging.log4j:log4j-api:${rootProject.log4j_version}")
|
||||
implementation("org.apache.logging.log4j:log4j-core:${rootProject.log4j_version}")
|
||||
}
|
||||
|
||||
// Log4j
|
||||
// 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")
|
||||
@@ -257,8 +201,7 @@ subprojects { p ->
|
||||
// Note: MC 1.16 uses 8.2.1, and versions after use 8.5.12
|
||||
// We cannot relocate this library since we call some MC classes that reference it
|
||||
implementation("it.unimi.dsi:fastutil:${rootProject.fastutil_version}")
|
||||
|
||||
forgeShadowMe("com.github.luben:zstd-jni:${rootProject.zstd_version}")
|
||||
|
||||
|
||||
// Compression
|
||||
forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}") // LZ4
|
||||
@@ -348,44 +291,9 @@ subprojects { p ->
|
||||
// Compression (LZ4)
|
||||
relocate "net.jpountz", "${librariesLocation}.jpountz"
|
||||
|
||||
// Logging
|
||||
relocate "org.slf4j", "${librariesLocation}.slf4j"
|
||||
|
||||
// Sqlite Database
|
||||
// librariesLocation isn't used because it's too long for replacing paths in native libraries
|
||||
// Allowing strings larger than the original string would require shifting the entire binary's contents
|
||||
relocate "org.sqlite", "dh_sqlite", {
|
||||
exclude "org/sqlite/native/**"
|
||||
}
|
||||
relocate "jdbc:sqlite", "jdbc:dh_sqlite"
|
||||
|
||||
transform(NativeTransformer) {
|
||||
rootDir = project.rootDir
|
||||
|
||||
matchFiles { it.startsWith("org/sqlite") }
|
||||
mapPaths { it.replace("org/sqlite", "dh_sqlite") }
|
||||
|
||||
relocateNative "org/sqlite", "dh_sqlite"
|
||||
relocateNative "org_sqlite", "dh_1sqlite"
|
||||
}
|
||||
|
||||
// ZStd
|
||||
// librariesLocation isn't used because it's too long for replacing paths in native libraries
|
||||
// Allowing strings larger than the original string would require shifting the entire binary's contents
|
||||
relocate "com.github.luben", "dhcomgithubluben"
|
||||
relocate "libzstd-jni", "libzstd-jni_dh"
|
||||
relocate "zstd-jni", "zstd-jni_dh"
|
||||
|
||||
transform(NativeTransformer) {
|
||||
rootDir = project.rootDir
|
||||
|
||||
matchFiles { it.contains("libzstd-jni") && !it.contains("aix/ppc64") }
|
||||
mapPaths { it.replace("libzstd-jni", "libzstd-jni_dh") }
|
||||
|
||||
relocateNative "com/github/luben", "dhcomgithubluben"
|
||||
relocateNative "com_github_luben", "dhcomgithubluben"
|
||||
}
|
||||
|
||||
//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")
|
||||
@@ -478,9 +386,7 @@ subprojects { p ->
|
||||
info_build_source : infoBuildSource,
|
||||
|
||||
fabric_incompatibility_list : fabric_incompatibility_list,
|
||||
fabric_recommend_list : fabric_recommend_list,
|
||||
|
||||
neoforge_version_range : neoforge_version_range,
|
||||
fabric_recommend_list : fabric_recommend_list,
|
||||
]
|
||||
|
||||
// replace any properties in the sub-projects with the values defined here
|
||||
@@ -528,8 +434,7 @@ subprojects { p ->
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -561,9 +466,8 @@ allprojects { p ->
|
||||
apply plugin: "java"
|
||||
apply plugin: "maven-publish"
|
||||
|
||||
// Sets the name of the jar, the version will contain the name of the project if it isn't the root project
|
||||
archivesBaseName = rootProject.mod_name
|
||||
version = (project == rootProject ? "" : project.name + "-") + rootProject.versionStr
|
||||
version = project.name + "-" + rootProject.versionStr
|
||||
group = rootProject.maven_group
|
||||
|
||||
// this is the text that appears at the top of the overview (home) page
|
||||
@@ -585,9 +489,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()
|
||||
|
||||
@@ -595,7 +496,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
|
||||
@@ -717,6 +617,7 @@ allprojects { p ->
|
||||
tasks.withType(JavaCompile) {
|
||||
if (isMinecraftSubProject) {
|
||||
options.release = rootProject.java_version as Integer
|
||||
options.compilerArgs += ["-Xplugin:Manifold"]
|
||||
} else {
|
||||
options.release = 8; // Core & Api should use Java 8 no matter what
|
||||
//options.release = rootProject.java_version as Integer // But if you want to test some stuff, then this can be enabled
|
||||
@@ -728,3 +629,14 @@ allprojects { p ->
|
||||
withSourcesJar()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Delete the merged folder when running clean
|
||||
task cleanMergedJars() {
|
||||
def mergedFolder = file("Merged")
|
||||
if (mergedFolder.exists()) {
|
||||
delete(mergedFolder)
|
||||
}
|
||||
}
|
||||
// add cleanMergedJars to the end of the "clean" task
|
||||
tasks["clean"].finalizedBy(cleanMergedJars)
|
||||
|
||||
@@ -22,7 +22,7 @@ for d in versionProperties/*; do
|
||||
echo "==================== Merging $version ===================="
|
||||
sh gradlew mergeJars -PmcVer=$version
|
||||
if [ $? != 0 ]; then continue; fi
|
||||
|
||||
|
||||
echo "==================== Moving jar ===================="
|
||||
mv build/merged/*.jar buildAllJars/
|
||||
mv Merged/*.jar buildAllJars/
|
||||
done
|
||||
|
||||
+4
-7
@@ -14,16 +14,13 @@ 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
|
||||
|
||||
echo ==================== Building !version! ====================
|
||||
call .\gradlew.bat clean -PmcVer="!version!"
|
||||
echo ==================== Building !version! ====================
|
||||
call .\gradlew.bat build -PmcVer="!version!"
|
||||
|
||||
echo ==================== Merging !version! ====================
|
||||
echo ==================== Merging !version! ====================
|
||||
call .\gradlew.bat mergeJars -PmcVer="!version!"
|
||||
|
||||
echo ==================== Moving jar ====================
|
||||
move build\merged\*.jar buildAllJars\
|
||||
move Merged\*.jar buildAllJars\
|
||||
)
|
||||
|
||||
endlocal
|
||||
|
||||
@@ -1,214 +0,0 @@
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
class NativeRelocator
|
||||
{
|
||||
private final Path rootDirectory;
|
||||
private final Path cacheRoot;
|
||||
|
||||
/**
|
||||
* Initializes the NativeRelocator by preparing the environment if necessary.
|
||||
* Executes the appropriate preparation script based on the OS.
|
||||
*/
|
||||
NativeRelocator(Path rootDirectory)
|
||||
{
|
||||
this.rootDirectory = rootDirectory;
|
||||
this.cacheRoot = this.rootDirectory.resolve("cache");
|
||||
}
|
||||
|
||||
private void prepare() throws Exception
|
||||
{
|
||||
if (this.rootDirectory.resolve(".venv").toFile().exists())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ProcessBuilder processBuilder = new ProcessBuilder();
|
||||
processBuilder.directory(this.rootDirectory.toFile());
|
||||
|
||||
String os = System.getProperty("os.name").toLowerCase();
|
||||
if (os.contains("win"))
|
||||
{
|
||||
processBuilder.command("powershell", "-ExecutionPolicy", "Bypass", "./prepare.ps1");
|
||||
}
|
||||
else if (os.contains("nix")
|
||||
|| os.contains("nux")
|
||||
|| os.contains("mac")
|
||||
|| os.contains("freebsd"))
|
||||
{
|
||||
processBuilder.command("./prepare.sh");
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new IllegalStateException("Unsupported operating system: " + os);
|
||||
}
|
||||
|
||||
Process process = processBuilder.start();
|
||||
CompletableFuture<Void> outputFuture = readOutputStreams(process);
|
||||
|
||||
int exitCode = process.waitFor();
|
||||
outputFuture.get();
|
||||
|
||||
if (exitCode != 0)
|
||||
{
|
||||
throw new Exception("Prepare failed: " + exitCode);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads and prints the output and error streams of a process asynchronously.
|
||||
*
|
||||
* @param process The process whose streams should be read.
|
||||
* @return A CompletableFuture that completes once all output has been processed.
|
||||
*/
|
||||
private static CompletableFuture<Void> readOutputStreams(Process process)
|
||||
{
|
||||
return CompletableFuture.runAsync(() -> {
|
||||
try
|
||||
{
|
||||
while (process.isAlive() || process.getInputStream().available() > 0 || process.getErrorStream().available() > 0)
|
||||
{
|
||||
if (process.getInputStream().available() > 0)
|
||||
{
|
||||
byte[] data = new byte[process.getInputStream().available()];
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
process.getInputStream().read(data);
|
||||
System.out.write(data);
|
||||
}
|
||||
if (process.getErrorStream().available() > 0)
|
||||
{
|
||||
byte[] data = new byte[process.getErrorStream().available()];
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
process.getErrorStream().read(data);
|
||||
System.err.write(data);
|
||||
}
|
||||
|
||||
//noinspection BusyWait
|
||||
Thread.sleep(100);
|
||||
}
|
||||
}
|
||||
catch (Throwable ignored)
|
||||
{
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces occurrences of a target string in a byte array, ensuring null termination.
|
||||
*
|
||||
* @param byteArray The byte array where replacements should occur.
|
||||
* @param target The string to replace.
|
||||
* @param replacement The replacement string (must not be longer than the target).
|
||||
* @throws IllegalArgumentException if the replacement is longer than the target.
|
||||
*/
|
||||
private void replaceInNullTerminatedStrings(byte[] byteArray, String target, String replacement)
|
||||
{
|
||||
if (target.length() < replacement.length())
|
||||
{
|
||||
throw new IllegalArgumentException("Replacement must be the same length or shorter than the target.");
|
||||
}
|
||||
|
||||
byte[] targetBytes = target.getBytes(StandardCharsets.US_ASCII);
|
||||
byte[] replacementBytes = replacement.getBytes(StandardCharsets.US_ASCII);
|
||||
|
||||
byte nullByte = 0;
|
||||
|
||||
for (int endPos = 0; endPos < byteArray.length - targetBytes.length - 1; endPos++)
|
||||
{
|
||||
int startPos = endPos;
|
||||
int targetPos = 0;
|
||||
while (targetPos < targetBytes.length && byteArray[endPos] == targetBytes[targetPos])
|
||||
{
|
||||
targetPos++;
|
||||
endPos++;
|
||||
}
|
||||
|
||||
if (targetPos == targetBytes.length)
|
||||
{
|
||||
System.arraycopy(replacementBytes, 0, byteArray, startPos, replacementBytes.length);
|
||||
|
||||
startPos = startPos + replacementBytes.length;
|
||||
while (byteArray[endPos] != nullByte)
|
||||
{
|
||||
byteArray[startPos] = byteArray[endPos];
|
||||
endPos++;
|
||||
startPos++;
|
||||
}
|
||||
byteArray[startPos] = nullByte;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs an external script to fix a modified binary and returns the processed content.
|
||||
*
|
||||
* @param outputFilePath Path to store the processed binary.
|
||||
* @param content The original binary content.
|
||||
* @return The modified binary content.
|
||||
* @throws Exception if the process execution fails.
|
||||
*/
|
||||
public byte[] fixModifiedBinary(Path outputFilePath, byte[] content) throws Exception
|
||||
{
|
||||
ProcessBuilder processBuilder = new ProcessBuilder();
|
||||
processBuilder.directory(this.rootDirectory.toFile());
|
||||
|
||||
processBuilder.command(
|
||||
this.rootDirectory.resolve(".venv/Scripts").toFile().exists()
|
||||
? this.rootDirectory.resolve(".venv/Scripts/python.exe").toString()
|
||||
: this.rootDirectory.resolve(".venv/bin/python").toString(),
|
||||
"./fix_modified_binary.py",
|
||||
outputFilePath.toString()
|
||||
);
|
||||
|
||||
Process process = processBuilder.start();
|
||||
CompletableFuture<Void> outputFuture = readOutputStreams(process);
|
||||
|
||||
process.getOutputStream().write(content);
|
||||
process.getOutputStream().close();
|
||||
|
||||
int exitCode = process.waitFor();
|
||||
outputFuture.get();
|
||||
|
||||
if (exitCode != 0)
|
||||
{
|
||||
throw new Exception("Process failed: " + exitCode);
|
||||
}
|
||||
|
||||
return Files.readAllBytes(outputFilePath);
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes a binary file, applying string replacements and fixing modifications.
|
||||
*
|
||||
* @param outputPath The output file path relative to the cache directory.
|
||||
* @param content The binary content to process.
|
||||
* @param replacements A map of string replacements to apply.
|
||||
* @return The modified binary content.
|
||||
* @throws Exception if processing fails.
|
||||
*/
|
||||
public byte[] processBinary(String outputPath, byte[] content, Map<String, String> replacements) throws Exception
|
||||
{
|
||||
Path outputFilePath = this.cacheRoot.resolve(outputPath);
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
outputFilePath.getParent().toFile().mkdirs();
|
||||
|
||||
if (outputFilePath.toFile().exists())
|
||||
{
|
||||
return Files.readAllBytes(outputFilePath);
|
||||
}
|
||||
|
||||
System.out.println("Relocating to " + outputPath + "...");
|
||||
this.prepare();
|
||||
|
||||
for (Map.Entry<String, String> replacement : replacements.entrySet())
|
||||
{
|
||||
this.replaceInNullTerminatedStrings(content, replacement.getKey(), replacement.getValue());
|
||||
}
|
||||
|
||||
return this.fixModifiedBinary(outputFilePath, content);
|
||||
}
|
||||
|
||||
}
|
||||
+8
-2
@@ -1,11 +1,17 @@
|
||||
// TODO can this be removed?
|
||||
//buildscript {
|
||||
// configurations.configureEach {
|
||||
// resolutionStrategy {
|
||||
// force 'org.spongepowered:vanillagradle:0.2.1-20240507.024226-82'
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
|
||||
// temporary fix for broken spongepowered version
|
||||
buildscript {
|
||||
configurations.configureEach {
|
||||
resolutionStrategy {
|
||||
force 'org.spongepowered:vanillagradle:0.2.1-20240507.024226-82'
|
||||
// newer versions can be found by going to the link:
|
||||
// https://repo.spongepowered.org/#browse/browse:maven-public:org%2Fspongepowered%2Fvanillagradle%2F0.2.1-SNAPSHOT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+161
-138
@@ -1,23 +1,28 @@
|
||||
package com.seibel.distanthorizons.common;
|
||||
|
||||
import com.mojang.brigadier.Command;
|
||||
import com.mojang.brigadier.CommandDispatcher;
|
||||
import com.mojang.brigadier.arguments.*;
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
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.gui.DhDebugScreenEntry;
|
||||
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftServerWrapper;
|
||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
|
||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.config.ConfigHandler;
|
||||
import com.seibel.distanthorizons.core.config.ConfigBase;
|
||||
import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler;
|
||||
import com.seibel.distanthorizons.core.config.types.AbstractConfigType;
|
||||
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.enums.EMinecraftColor;
|
||||
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.network.messages.base.CodecCrashMessage;
|
||||
import com.seibel.distanthorizons.core.util.objects.Pair;
|
||||
import com.seibel.distanthorizons.core.world.DhServerWorld;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModAccessor;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
|
||||
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
|
||||
@@ -25,20 +30,37 @@ import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.HashMap;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static com.mojang.brigadier.arguments.DoubleArgumentType.doubleArg;
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||
import static com.seibel.distanthorizons.core.network.messages.MessageRegistry.DEBUG_CODEC_CRASH_MESSAGE;
|
||||
import static net.minecraft.commands.Commands.argument;
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
#if MC_VER >= MC_1_19_2
|
||||
import net.minecraft.network.chat.Component;
|
||||
#else // < 1.19.2
|
||||
import net.minecraft.network.chat.TranslatableComponent;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Base for all mod loader initializers
|
||||
* and handles most setup.
|
||||
*/
|
||||
public abstract class AbstractModInitializer
|
||||
{
|
||||
protected static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
protected static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
|
||||
|
||||
private CommandInitializer commandInitializer;
|
||||
private CommandDispatcher<CommandSourceStack> commandDispatcher;
|
||||
|
||||
|
||||
|
||||
@@ -46,8 +68,7 @@ public abstract class AbstractModInitializer
|
||||
// abstract methods //
|
||||
//==================//
|
||||
|
||||
protected abstract void createInitialSharedBindings();
|
||||
protected abstract void createInitialClientBindings();
|
||||
protected abstract void createInitialBindings();
|
||||
protected abstract IEventProxy createClientProxy();
|
||||
protected abstract IEventProxy createServerProxy(boolean isDedicated);
|
||||
protected abstract void initializeModCompat();
|
||||
@@ -67,9 +88,8 @@ public abstract class AbstractModInitializer
|
||||
public void onInitializeClient()
|
||||
{
|
||||
DependencySetup.createClientBindings();
|
||||
this.createInitialClientBindings();
|
||||
|
||||
LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " client, firing DhApiBeforeDhInitEvent...");
|
||||
LOGGER.info("Initializing " + ModInfo.READABLE_NAME);
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
|
||||
|
||||
this.startup();
|
||||
@@ -80,17 +100,11 @@ public abstract class AbstractModInitializer
|
||||
|
||||
this.initializeModCompat();
|
||||
|
||||
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
|
||||
|
||||
LOGGER.info(ModInfo.READABLE_NAME + " client Initialized.");
|
||||
|
||||
#if MC_VER < MC_1_21_9
|
||||
// debug screen rendering handled via a mixin
|
||||
#else
|
||||
DhDebugScreenEntry.register();
|
||||
#endif
|
||||
|
||||
this.subscribeClientStartedEvent(this::postInit);
|
||||
}
|
||||
@@ -99,7 +113,7 @@ public abstract class AbstractModInitializer
|
||||
{
|
||||
DependencySetup.createServerBindings();
|
||||
|
||||
LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " server, firing DhApiBeforeDhInitEvent event...");
|
||||
LOGGER.info("Initializing " + ModInfo.READABLE_NAME);
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
|
||||
|
||||
this.startup();
|
||||
@@ -114,9 +128,10 @@ public abstract class AbstractModInitializer
|
||||
|
||||
this.initializeModCompat();
|
||||
|
||||
LOGGER.info(ModInfo.READABLE_NAME + " server Initialized, adding event subscribers...");
|
||||
this.commandInitializer = new CommandInitializer();
|
||||
this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandInitializer.initCommands(dispatcher); });
|
||||
LOGGER.info(ModInfo.READABLE_NAME + " Initialized");
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
|
||||
|
||||
this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandDispatcher = dispatcher; });
|
||||
|
||||
this.subscribeServerStartingEvent(server ->
|
||||
{
|
||||
@@ -124,11 +139,9 @@ public abstract class AbstractModInitializer
|
||||
|
||||
this.initConfig();
|
||||
this.postInit();
|
||||
this.commandInitializer.onServerReady();
|
||||
this.initCommands();
|
||||
|
||||
this.checkForUpdates();
|
||||
|
||||
LOGGER.info(ModInfo.READABLE_NAME + " server Initialized at " + server.getServerDirectory());
|
||||
LOGGER.info("Dedicated server initialized at " + server.getServerDirectory());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -142,7 +155,7 @@ public abstract class AbstractModInitializer
|
||||
{
|
||||
DependencySetup.createSharedBindings();
|
||||
SharedApi.init();
|
||||
this.createInitialSharedBindings();
|
||||
this.createInitialBindings();
|
||||
}
|
||||
|
||||
private void logBuildInfo()
|
||||
@@ -166,140 +179,150 @@ public abstract class AbstractModInitializer
|
||||
//noinspection unchecked
|
||||
ModAccessorInjector.INSTANCE.bind((Class<? extends IModAccessor>) accessorClass, accessorConstructor.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER.debug("Skipping mod compatibility accessor for: ["+modId+"]");
|
||||
}
|
||||
}
|
||||
|
||||
private void initConfig()
|
||||
{
|
||||
ConfigHandler.tryRunFirstTimeSetup();
|
||||
ConfigBase.INSTANCE = new ConfigBase(ModInfo.ID, ModInfo.NAME, Config.class, 2);
|
||||
Config.completeDelayedSetup();
|
||||
DhLogger.runDelayedConfigSetup();
|
||||
}
|
||||
|
||||
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("Running Delayed setup...");
|
||||
LOGGER.info("Post-Initializing Mod");
|
||||
this.runDelayedSetup();
|
||||
|
||||
if (ConfigHandler.INSTANCE == null)
|
||||
{
|
||||
throw new IllegalStateException("Config was not initialized. Make sure to call LodCommonMain.initConfig() before calling this method.");
|
||||
}
|
||||
|
||||
LOGGER.info("Delayed setup complete, firing DhApiAfterDhInitEvent event...");
|
||||
|
||||
// should be fired after all delayed setup so singletons and config can be accessed
|
||||
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
|
||||
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()
|
||||
@SuppressWarnings({"rawtypes", "unchecked"})
|
||||
private void initCommands()
|
||||
{
|
||||
boolean showChatWarnings = Config.Common.Logging.Warning.showModCompatibilityWarningsOnStartup.get();
|
||||
IModChecker modChecker = SingletonInjector.INSTANCE.get(IModChecker.class);
|
||||
LiteralArgumentBuilder<CommandSourceStack> builder = literal("dhconfig")
|
||||
.requires(source -> source.hasPermission(4));
|
||||
|
||||
String startingString = "Partially Incompatible Distant Horizons mod detected: ";
|
||||
|
||||
|
||||
|
||||
// Alex's caves
|
||||
if (modChecker.isModLoaded("alexscaves"))
|
||||
for (AbstractConfigType<?, ?> type : ConfigBase.INSTANCE.entries)
|
||||
{
|
||||
// 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)
|
||||
if (!(type instanceof ConfigEntry))
|
||||
{
|
||||
String message =
|
||||
EMinecraftColor.ORANGE + "Distant Horizons: Alex's Cave detected." + EMinecraftColor.CLEAR_FORMATTING +
|
||||
"You may have to change Alex's config for DH to render. ";
|
||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
||||
continue;
|
||||
}
|
||||
//noinspection PatternVariableCanBeUsed
|
||||
ConfigEntry configEntry = (ConfigEntry) type;
|
||||
if (configEntry.getServersideShortName() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
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.
|
||||
Function<
|
||||
Function<CommandContext<CommandSourceStack>, Object>,
|
||||
Command<CommandSourceStack>
|
||||
> makeConfigUpdater = (getter) -> (commandContext) -> {
|
||||
Object value = getter.apply(commandContext);
|
||||
|
||||
commandContext.getSource().sendSuccess(
|
||||
#if MC_VER >= MC_1_20_1
|
||||
() -> Component.literal("Changed the value of "+configEntry.getServersideShortName()+" to "+value),
|
||||
#elif MC_VER >= MC_1_19_2
|
||||
Component.literal("Changed the value of "+configEntry.getServersideShortName()+" to "+value),
|
||||
#else
|
||||
new TranslatableComponent("Changed the value of "+configEntry.getServersideShortName()+" to "+value),
|
||||
#endif
|
||||
true);
|
||||
configEntry.set(value);
|
||||
return 1;
|
||||
};
|
||||
|
||||
String wwooWarning = "LODs generated by DH may have grid lines between sections. Disabling either WWOO or DH's distant generator will fix the problem.";
|
||||
LiteralArgumentBuilder<CommandSourceStack> subcommand = literal(configEntry.getServersideShortName())
|
||||
.executes((commandContext) -> {
|
||||
#if MC_VER >= MC_1_20_1
|
||||
commandContext.getSource().sendSuccess(() -> Component.literal("Current value of "+configEntry.getServersideShortName()+" is "+configEntry.get()), true);
|
||||
#elif MC_VER >= MC_1_19_2
|
||||
commandContext.getSource().sendSuccess(Component.literal("Current value of "+configEntry.getServersideShortName()+" is "+configEntry.get()), true);
|
||||
#else // < 1.19.2
|
||||
commandContext.getSource().sendSuccess(new TranslatableComponent("Current value of "+configEntry.getServersideShortName()+" is "+configEntry.get()), true);
|
||||
#endif
|
||||
return 1;
|
||||
});
|
||||
|
||||
if (showChatWarnings)
|
||||
if (Enum.class.isAssignableFrom(configEntry.getType()))
|
||||
{
|
||||
String message =
|
||||
EMinecraftColor.ORANGE + "Distant Horizons: WWOO detected." + EMinecraftColor.CLEAR_FORMATTING + "\n" +
|
||||
wwooWarning;
|
||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
||||
for (Object choice : configEntry.getType().getEnumConstants())
|
||||
{
|
||||
subcommand.then(
|
||||
literal(choice.toString())
|
||||
.executes(makeConfigUpdater.apply(c -> choice))
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
boolean setterAdded = false;
|
||||
|
||||
for (java.util.Map.Entry<Class<?>, Pair<Supplier<ArgumentType<?>>, BiFunction<CommandContext<?>, String, ?>>> pair : new HashMap<
|
||||
Class<?>,
|
||||
Pair<
|
||||
Supplier<ArgumentType<?>>,
|
||||
BiFunction<CommandContext<?>, String, ?>>
|
||||
>() {{
|
||||
this.put(Integer.class, new Pair<>(() -> integer((int) configEntry.getMin(), (int) configEntry.getMax()), IntegerArgumentType::getInteger));
|
||||
this.put(Double.class, new Pair<>(() -> doubleArg((double) configEntry.getMin(), (double) configEntry.getMax()), DoubleArgumentType::getDouble));
|
||||
this.put(Boolean.class, new Pair<>(BoolArgumentType::bool, BoolArgumentType::getBool));
|
||||
this.put(String.class, new Pair<>(StringArgumentType::string, StringArgumentType::getString));
|
||||
}}.entrySet())
|
||||
{
|
||||
if (!pair.getKey().isAssignableFrom(configEntry.getType()))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
subcommand.then(argument("value", pair.getValue().first.get())
|
||||
.executes(makeConfigUpdater.apply(c -> pair.getValue().second.apply(c, "value"))));
|
||||
|
||||
setterAdded = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!setterAdded)
|
||||
{
|
||||
throw new RuntimeException("Config type of "+type.getName()+" is not supported: "+configEntry.getType().getSimpleName());
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.warn(startingString + "[WWOO] "+ wwooWarning);
|
||||
builder.then(subcommand);
|
||||
}
|
||||
|
||||
// Chunky
|
||||
boolean chunkyPresent = false;
|
||||
try
|
||||
this.commandDispatcher.register(builder);
|
||||
|
||||
if (DEBUG_CODEC_CRASH_MESSAGE)
|
||||
{
|
||||
Class.forName("org.popcraft.chunky.api.ChunkyAPI");
|
||||
chunkyPresent = true;
|
||||
LiteralArgumentBuilder<CommandSourceStack> dhcrash = literal("dhcrash")
|
||||
.requires(source -> source.hasPermission(4))
|
||||
.then(literal("encode")
|
||||
.executes(c -> {
|
||||
assert SharedApi.getIDhServerWorld() != null;
|
||||
((DhServerWorld) SharedApi.getIDhServerWorld()).remotePlayerConnectionHandler
|
||||
#if MC_VER >= MC_1_19_2
|
||||
.getConnectedPlayer(ServerPlayerWrapper.getWrapper(Objects.requireNonNull(c.getSource().getPlayer())))
|
||||
#else
|
||||
.getConnectedPlayer(ServerPlayerWrapper.getWrapper(Objects.requireNonNull(c.getSource().getPlayerOrException())))
|
||||
#endif
|
||||
.networkSession.sendMessage(new CodecCrashMessage(CodecCrashMessage.ECrashPhase.ENCODE));
|
||||
return 1;
|
||||
}))
|
||||
.then(literal("decode")
|
||||
.executes(c -> {
|
||||
assert SharedApi.getIDhServerWorld() != null;
|
||||
((DhServerWorld) SharedApi.getIDhServerWorld()).remotePlayerConnectionHandler
|
||||
#if MC_VER >= MC_1_19_2
|
||||
.getConnectedPlayer(ServerPlayerWrapper.getWrapper(Objects.requireNonNull(c.getSource().getPlayer())))
|
||||
#else
|
||||
.getConnectedPlayer(ServerPlayerWrapper.getWrapper(Objects.requireNonNull(c.getSource().getPlayerOrException())))
|
||||
#endif
|
||||
.networkSession.sendMessage(new CodecCrashMessage(CodecCrashMessage.ECrashPhase.DECODE));
|
||||
return 1;
|
||||
}));
|
||||
this.commandDispatcher.register(dhcrash);
|
||||
}
|
||||
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 =
|
||||
EMinecraftColor.ORANGE + "Distant Horizons: Chunky detected." + EMinecraftColor.CLEAR_FORMATTING + "\n" +
|
||||
chunkyWarning;
|
||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
||||
}
|
||||
|
||||
LOGGER.warn(startingString + "[Chunky] "+ chunkyWarning);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
+12
-37
@@ -1,8 +1,7 @@
|
||||
package com.seibel.distanthorizons.common;
|
||||
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
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;
|
||||
@@ -13,41 +12,25 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapp
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import io.netty.buffer.ByteBufUtil;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
|
||||
#if MC_VER <= MC_1_21_10
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
#else
|
||||
import net.minecraft.resources.Identifier;
|
||||
#endif
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Objects;
|
||||
|
||||
public abstract class AbstractPluginPacketSender implements IPluginPacketSender
|
||||
{
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder()
|
||||
.fileLevelConfig(Config.Common.Logging.logNetworkEventToFile)
|
||||
.build();
|
||||
private static final ConfigBasedLogger LOGGER = new ConfigBasedLogger(LogManager.getLogger(),
|
||||
() -> Config.Client.Advanced.Logging.logNetworkEvent.get());
|
||||
|
||||
#if MC_VER <= MC_1_20_6
|
||||
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
|
||||
#elif MC_VER <= MC_1_21_10
|
||||
#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 Identifier WRAPPER_PACKET_RESOURCE = Identifier.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
|
||||
public static final ResourceLocation WRAPPER_PACKET_RESOURCE = new ResourceLocation(ModInfo.RESOURCE_NAMESPACE, ModInfo.WRAPPER_PACKET_PATH);
|
||||
#endif
|
||||
|
||||
// "Forge byte" is an unused packet ID. We have our own system which works with all mod loaders,
|
||||
// so we're just accounting for it by reading the protocol version as a byte instead of a short in Forge, to keep cross-loader compatibility
|
||||
private final boolean forgeByteInProtocolVersion;
|
||||
|
||||
|
||||
public AbstractPluginPacketSender() { this(false); }
|
||||
public AbstractPluginPacketSender(boolean forgeByteInProtocolVersion)
|
||||
{
|
||||
this.forgeByteInProtocolVersion = forgeByteInProtocolVersion;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void sendToClient(IServerPlayerWrapper serverPlayer, AbstractNetworkMessage message)
|
||||
@@ -59,7 +42,7 @@ public abstract class AbstractPluginPacketSender implements IPluginPacketSender
|
||||
@Override
|
||||
public abstract void sendToServer(AbstractNetworkMessage message);
|
||||
|
||||
public AbstractNetworkMessage decodeMessage(FriendlyByteBuf in)
|
||||
public static AbstractNetworkMessage decodeMessage(FriendlyByteBuf in)
|
||||
{
|
||||
AbstractNetworkMessage message = null;
|
||||
|
||||
@@ -67,7 +50,7 @@ public abstract class AbstractPluginPacketSender implements IPluginPacketSender
|
||||
{
|
||||
in.markReaderIndex();
|
||||
|
||||
int protocolVersion = this.forgeByteInProtocolVersion ? in.readByte() : in.readShort();
|
||||
int protocolVersion = in.readShort();
|
||||
if (protocolVersion != ModInfo.PROTOCOL_VERSION)
|
||||
{
|
||||
return new IncompatibleMessageInternalEvent(protocolVersion);
|
||||
@@ -100,19 +83,11 @@ public abstract class AbstractPluginPacketSender implements IPluginPacketSender
|
||||
}
|
||||
}
|
||||
|
||||
public void encodeMessage(FriendlyByteBuf out, AbstractNetworkMessage message)
|
||||
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);
|
||||
|
||||
if (this.forgeByteInProtocolVersion)
|
||||
{
|
||||
out.writeByte(ModInfo.PROTOCOL_VERSION);
|
||||
}
|
||||
else
|
||||
{
|
||||
out.writeShort(ModInfo.PROTOCOL_VERSION);
|
||||
}
|
||||
out.writeShort(ModInfo.PROTOCOL_VERSION);
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
@@ -2,9 +2,7 @@ package com.seibel.distanthorizons.common;
|
||||
|
||||
#if MC_VER >= MC_1_20_6
|
||||
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.codec.StreamCodec;
|
||||
import net.minecraft.network.protocol.common.custom.CustomPacketPayload;
|
||||
@@ -14,7 +12,6 @@ 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);
|
||||
private static final AbstractPluginPacketSender PACKET_SENDER = (AbstractPluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class);
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
@@ -26,11 +23,11 @@ public record CommonPacketPayload(@Nullable AbstractNetworkMessage message) impl
|
||||
@NotNull
|
||||
@Override
|
||||
public CommonPacketPayload decode(@NotNull FriendlyByteBuf in)
|
||||
{ return new CommonPacketPayload(PACKET_SENDER.decodeMessage(in)); }
|
||||
{ return new CommonPacketPayload(AbstractPluginPacketSender.decodeMessage(in)); }
|
||||
|
||||
@Override
|
||||
public void encode(@NotNull FriendlyByteBuf out, CommonPacketPayload payload)
|
||||
{ PACKET_SENDER.encodeMessage(out, payload.message()); }
|
||||
{ AbstractPluginPacketSender.encodeMessage(out, payload.message()); }
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,102 +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, boolean notifyAdmins)
|
||||
{
|
||||
#if MC_VER >= MC_1_20_1
|
||||
commandContext.getSource().sendSuccess(() -> Component.literal(text), notifyAdmins);
|
||||
#elif MC_VER >= MC_1_19_2
|
||||
commandContext.getSource().sendSuccess(Component.literal(text), notifyAdmins);
|
||||
#else
|
||||
commandContext.getSource().sendSuccess(new TranslatableComponent(text), notifyAdmins);
|
||||
#endif
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a failure 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 failure message.
|
||||
* @return 1, indicating that the command was successful.
|
||||
*/
|
||||
protected int sendFailureResponse(CommandContext<CommandSourceStack> commandContext, String text)
|
||||
{
|
||||
#if MC_VER >= MC_1_20_1
|
||||
commandContext.getSource().sendFailure(Component.literal(text));
|
||||
#elif MC_VER >= MC_1_19_2
|
||||
commandContext.getSource().sendFailure(Component.literal(text));
|
||||
#else
|
||||
commandContext.getSource().sendFailure(new TranslatableComponent(text));
|
||||
#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
|
||||
}
|
||||
|
||||
}
|
||||
-84
@@ -1,84 +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 org.jetbrains.annotations.Nullable;
|
||||
|
||||
import static com.seibel.distanthorizons.core.network.messages.MessageRegistry.DEBUG_CODEC_CRASH_MESSAGE;
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
#if MC_VER <= MC_1_21_10
|
||||
#else
|
||||
import net.minecraft.server.permissions.PermissionCheck;
|
||||
import net.minecraft.server.permissions.Permissions;
|
||||
#endif
|
||||
|
||||
public class CommandInitializer
|
||||
{
|
||||
private boolean serverReady = false;
|
||||
|
||||
#if MC_VER <= MC_1_21_10
|
||||
private static final int REQUIRED_PERMISSION_LEVEL = 4;
|
||||
#else
|
||||
private static final PermissionCheck COMMAND_PERMISSION_CHECK = new PermissionCheck.Require(Permissions.COMMANDS_OWNER);
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* A received command dispatcher, which is held until the server is ready to initialize the commands.
|
||||
*/
|
||||
@Nullable
|
||||
private CommandDispatcher<CommandSourceStack> commandDispatcher;
|
||||
|
||||
/**
|
||||
* Notify the command initializer that the game is ready to accept commands.
|
||||
* If {@link CommandInitializer#initCommands(CommandDispatcher)} has been fired before it was ready, it will also initialize the commands.
|
||||
*/
|
||||
public void onServerReady()
|
||||
{
|
||||
this.serverReady = true;
|
||||
if (this.commandDispatcher != null)
|
||||
{
|
||||
this.initCommands(this.commandDispatcher);
|
||||
this.commandDispatcher = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes all available commands.
|
||||
* If the game is not ready yet, it stores the dispatcher to initialize the commands later.
|
||||
*
|
||||
* @param commandDispatcher The command dispatcher to register commands to.
|
||||
*/
|
||||
public void initCommands(CommandDispatcher<CommandSourceStack> commandDispatcher)
|
||||
{
|
||||
if (!this.serverReady)
|
||||
{
|
||||
this.commandDispatcher = commandDispatcher;
|
||||
return;
|
||||
}
|
||||
|
||||
LiteralArgumentBuilder<CommandSourceStack> builder = literal("dh")
|
||||
.requires((source) ->
|
||||
{
|
||||
#if MC_VER <= MC_1_21_10
|
||||
return source.hasPermission(REQUIRED_PERMISSION_LEVEL);
|
||||
#else
|
||||
return COMMAND_PERMISSION_CHECK.check(source.permissions());
|
||||
#endif
|
||||
});
|
||||
|
||||
builder.then(new ConfigCommand().buildCommand());
|
||||
builder.then(new DebugCommand().buildCommand());
|
||||
builder.then(new PregenCommand().buildCommand());
|
||||
|
||||
if (DEBUG_CODEC_CRASH_MESSAGE)
|
||||
{
|
||||
builder.then(new CrashCommand().buildCommand());
|
||||
}
|
||||
|
||||
commandDispatcher.register(builder);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,154 +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.ConfigHandler;
|
||||
import com.seibel.distanthorizons.core.config.types.AbstractConfigBase;
|
||||
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
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");
|
||||
HashSet<String> addedCommands = new HashSet<>();
|
||||
|
||||
for (AbstractConfigBase<?> type : ConfigHandler.INSTANCE.configBaseList)
|
||||
{
|
||||
// Skip non-config entries
|
||||
if (!(type instanceof ConfigEntry))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
//noinspection PatternVariableCanBeUsed
|
||||
ConfigEntry configEntry = (ConfigEntry) type;
|
||||
if (configEntry.getChatCommandName() == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!addedCommands.add(configEntry.getChatCommandName()))
|
||||
{
|
||||
throw new IllegalStateException("Duplicate command name: " + configEntry.getChatCommandName());
|
||||
}
|
||||
|
||||
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",
|
||||
false
|
||||
));
|
||||
|
||||
ToIntBiFunction<CommandContext<CommandSourceStack>, Object> updateConfigValue = (commandContext, value) -> {
|
||||
configEntry.set(value);
|
||||
return this.sendSuccessResponse(commandContext, "Changed the value of [" + configEntry.getChatCommandName() + "] to [" + value + "]", true);
|
||||
};
|
||||
|
||||
// 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.tryGetDhServerWorld() != null;
|
||||
|
||||
ServerPlayerState serverPlayerState = SharedApi.tryGetDhServerWorld().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.tryGetDhServerWorld() != null;
|
||||
|
||||
ServerPlayerState serverPlayerState = SharedApi.tryGetDhServerWorld().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), false);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
package com.seibel.distanthorizons.common.commands;
|
||||
|
||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||
import com.mojang.brigadier.context.CommandContext;
|
||||
import com.mojang.brigadier.exceptions.CommandSyntaxException;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.generation.PregenManager;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
|
||||
import net.minecraft.commands.CommandSourceStack;
|
||||
import net.minecraft.commands.arguments.DimensionArgument;
|
||||
import net.minecraft.commands.arguments.coordinates.ColumnPosArgument;
|
||||
import net.minecraft.server.level.ColumnPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
|
||||
import java.util.concurrent.CancellationException;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.getInteger;
|
||||
import static com.mojang.brigadier.arguments.IntegerArgumentType.integer;
|
||||
import static net.minecraft.commands.Commands.argument;
|
||||
import static net.minecraft.commands.Commands.literal;
|
||||
|
||||
public class PregenCommand extends AbstractCommand
|
||||
{
|
||||
private final PregenManager pregenManager = new PregenManager();
|
||||
|
||||
@Override
|
||||
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
|
||||
{
|
||||
LiteralArgumentBuilder<CommandSourceStack> statusCommand = literal("status")
|
||||
.executes(this::pregenStatus);
|
||||
|
||||
LiteralArgumentBuilder<CommandSourceStack> startCommand = literal("start")
|
||||
.then(argument("dimension", DimensionArgument.dimension())
|
||||
.then(argument("origin", ColumnPosArgument.columnPos())
|
||||
.then(argument("chunkRadius", integer(32))
|
||||
.executes(this::pregenStart))));
|
||||
|
||||
LiteralArgumentBuilder<CommandSourceStack> stopCommand = literal("stop")
|
||||
.executes(this::pregenStop);
|
||||
|
||||
return literal("pregen")
|
||||
.then(statusCommand)
|
||||
.then(startCommand)
|
||||
.then(stopCommand);
|
||||
}
|
||||
|
||||
|
||||
private int pregenStatus(CommandContext<CommandSourceStack> c)
|
||||
{
|
||||
String statusString = this.pregenManager.getStatusString();
|
||||
//noinspection ReplaceNullCheck
|
||||
if (statusString != null)
|
||||
{
|
||||
return this.sendSuccessResponse(c, statusString, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.sendSuccessResponse(c, "Pregen is not running", false);
|
||||
}
|
||||
}
|
||||
|
||||
private int pregenStart(CommandContext<CommandSourceStack> c) throws CommandSyntaxException
|
||||
{
|
||||
this.sendSuccessResponse(c, "Starting pregen. Progress will be in the server console.", true);
|
||||
|
||||
ServerLevel level = DimensionArgument.getDimension(c, "dimension");
|
||||
ColumnPos origin = ColumnPosArgument.getColumnPos(c, "origin");
|
||||
int chunkRadius = getInteger(c, "chunkRadius");
|
||||
|
||||
CompletableFuture<Void> future = this.pregenManager.startPregen(
|
||||
ServerLevelWrapper.getWrapper(level),
|
||||
new DhBlockPos2D(#if MC_VER >= MC_1_19_2 origin.x(), origin.z() #else origin.x, origin.z #endif),
|
||||
chunkRadius
|
||||
);
|
||||
|
||||
future.whenComplete((result, throwable) -> {
|
||||
if (throwable instanceof CancellationException)
|
||||
{
|
||||
this.sendSuccessResponse(c, "Pregen is cancelled", true);
|
||||
return;
|
||||
}
|
||||
else if (throwable != null)
|
||||
{
|
||||
this.sendFailureResponse(c, "Pregen failed: " + throwable.getMessage() + "\n Check the logs for more details.");
|
||||
return;
|
||||
}
|
||||
|
||||
this.sendSuccessResponse(c, "Pregen is complete", true);
|
||||
});
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
private int pregenStop(CommandContext<CommandSourceStack> c)
|
||||
{
|
||||
CompletableFuture<Void> runningPregen = this.pregenManager.getRunningPregen();
|
||||
if (runningPregen == null)
|
||||
{
|
||||
return this.sendFailureResponse(c, "Pregen is not running");
|
||||
}
|
||||
|
||||
runningPregen.cancel(true);
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -77,7 +77,7 @@ public class MixinChunkMapCommon
|
||||
|
||||
// submit the update event
|
||||
ServerApi.INSTANCE.serverChunkSaveEvent(
|
||||
new ChunkWrapper(chunk, ServerLevelWrapper.getWrapper(level)),
|
||||
new ChunkWrapper(chunk, level, ServerLevelWrapper.getWrapper(level)),
|
||||
ServerLevelWrapper.getWrapper(level)
|
||||
);
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
|
||||
+7
-5
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -22,7 +22,6 @@ 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.core.level.IKeyedClientLevelManager;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.config.IConfigGui;
|
||||
@@ -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;
|
||||
|
||||
@@ -56,17 +54,21 @@ public class DependencySetup
|
||||
SingletonInjector.INSTANCE.bind(IVersionConstants.class, VersionConstants.INSTANCE);
|
||||
SingletonInjector.INSTANCE.bind(IWrapperFactory.class, WrapperFactory.INSTANCE);
|
||||
SingletonInjector.INSTANCE.bind(IKeyedClientLevelManager.class, KeyedClientLevelManager.INSTANCE);
|
||||
DependencySetupDoneCheck.isDone = true;
|
||||
}
|
||||
|
||||
//@Environment(EnvType.SERVER)
|
||||
public static void createServerBindings()
|
||||
{ SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftServerWrapper.INSTANCE); }
|
||||
{
|
||||
SingletonInjector.INSTANCE.bind(IMinecraftSharedWrapper.class, MinecraftServerWrapper.INSTANCE);
|
||||
}
|
||||
|
||||
//@Environment(EnvType.CLIENT)
|
||||
public static void createClientBindings()
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
+8
-9
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -17,16 +17,15 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.fabric.wrappers.modAccessor;
|
||||
package com.seibel.distanthorizons.common.wrappers;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IC2meAccessor;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class C2meAccessor implements IC2meAccessor
|
||||
public class DependencySetupDoneCheck
|
||||
{
|
||||
@Override
|
||||
public String getModName()
|
||||
{
|
||||
return "c2me";
|
||||
}
|
||||
// TODO move to DependencySetup
|
||||
public static boolean isDone = false;
|
||||
// TODO why is this here and what is its purpose?
|
||||
public static Supplier<Boolean> getIsCurrentThreadDistantGeneratorThread = (() -> { return false; });
|
||||
|
||||
}
|
||||
+3
-7
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -49,9 +49,7 @@ public class McObjectConverter
|
||||
@Deprecated
|
||||
public static Mat4f Convert(
|
||||
#if MC_VER < MC_1_19_4 com.mojang.math.Matrix4f
|
||||
#elif MC_VER < MC_1_21_6 org.joml.Matrix4f
|
||||
#else org.joml.Matrix4fc
|
||||
#endif
|
||||
#else org.joml.Matrix4f #endif
|
||||
mcMatrix)
|
||||
{
|
||||
FloatBuffer buffer = FloatBuffer.allocate(16);
|
||||
@@ -65,9 +63,7 @@ public class McObjectConverter
|
||||
/** Taken from Minecraft's com.mojang.math.Matrix4f class from 1.18.2 */
|
||||
private static void storeMatrix(
|
||||
#if MC_VER < MC_1_19_4 com.mojang.math.Matrix4f
|
||||
#elif MC_VER < MC_1_21_6 org.joml.Matrix4f
|
||||
#else org.joml.Matrix4fc
|
||||
#endif
|
||||
#else org.joml.Matrix4f #endif
|
||||
matrix,
|
||||
FloatBuffer buffer)
|
||||
{
|
||||
|
||||
+24
-49
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -20,6 +20,8 @@
|
||||
package com.seibel.distanthorizons.common.wrappers;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
|
||||
import net.minecraft.SharedConstants;
|
||||
import net.minecraft.client.Minecraft;
|
||||
|
||||
/**
|
||||
* @author James Seibel
|
||||
@@ -36,59 +38,32 @@ public class VersionConstants implements IVersionConstants
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getMinimumWorldHeight()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getWorldGenerationCountPerThread()
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVanillaRenderedChunkSquare()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMinecraftVersion()
|
||||
{
|
||||
// these values are hard-coded to prevent an issue with Forge (specifically 1.18.2) where
|
||||
// it can't load client classes when running as a dedicated server,
|
||||
// which was how we were dynamically accessing the MC version string
|
||||
|
||||
#if MC_VER == MC_1_16_5
|
||||
return "1.16.5";
|
||||
|
||||
#elif MC_VER == MC_1_17_1
|
||||
return "1.17.1";
|
||||
|
||||
#elif MC_VER == MC_1_18_2
|
||||
return "1.18.2";
|
||||
|
||||
#elif MC_VER == MC_1_19_2
|
||||
return "1.19.2";
|
||||
#elif MC_VER == MC_1_19_4
|
||||
return "1.19.4";
|
||||
|
||||
#elif MC_VER == MC_1_20_1
|
||||
return "1.20.1";
|
||||
#elif MC_VER == MC_1_20_2
|
||||
return "1.20.2";
|
||||
#elif MC_VER == MC_1_20_4
|
||||
return "1.20.4";
|
||||
#elif MC_VER == MC_1_20_6
|
||||
return "1.20.6";
|
||||
|
||||
#elif MC_VER == MC_1_21_1
|
||||
return "1.21.1";
|
||||
#elif MC_VER == MC_1_21_3
|
||||
return "1.21.3";
|
||||
#elif MC_VER == MC_1_21_4
|
||||
return "1.21.4";
|
||||
#elif MC_VER == MC_1_21_5
|
||||
return "1.21.5";
|
||||
#elif MC_VER == MC_1_21_6
|
||||
return "1.21.6";
|
||||
#elif MC_VER == MC_1_21_8
|
||||
return "1.21.8";
|
||||
|
||||
#elif MC_VER == MC_1_21_9
|
||||
return "1.21.9";
|
||||
#elif MC_VER == MC_1_21_10
|
||||
return "1.21.10";
|
||||
#elif MC_VER == MC_1_21_11
|
||||
return "1.21.11";
|
||||
#if MC_VER < MC_1_19_2
|
||||
return Minecraft.getInstance().getGame().getVersion().getId();
|
||||
#else
|
||||
ERROR MC version constant missing
|
||||
return SharedConstants.getCurrentVersion().getId();
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -38,7 +38,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.IBatchGeneratorEnvironmentWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
#if MC_VER > MC_1_17_1
|
||||
import net.minecraft.core.Holder;
|
||||
@@ -67,7 +67,7 @@ public class WrapperFactory implements IWrapperFactory
|
||||
//==============//
|
||||
|
||||
@Override
|
||||
public IBatchGeneratorEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel)
|
||||
public AbstractBatchGenerationEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel)
|
||||
{
|
||||
if (targetLevel instanceof IDhServerLevel)
|
||||
{
|
||||
@@ -174,6 +174,8 @@ public class WrapperFactory implements IWrapperFactory
|
||||
}
|
||||
// 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
|
||||
@@ -182,7 +184,7 @@ public class WrapperFactory implements IWrapperFactory
|
||||
: ServerLevelWrapper.getWrapper((ServerLevel)level);
|
||||
|
||||
|
||||
return new ChunkWrapper(chunk, levelWrapper);
|
||||
return new ChunkWrapper(chunk, lightSource, levelWrapper);
|
||||
}
|
||||
// incorrect number of parameters from the API
|
||||
else
|
||||
|
||||
-337
@@ -1,337 +0,0 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.block;
|
||||
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dataObjects.BlockBiomeWrapperPair;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhSectionPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPosMutable;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.core.util.FullDataPointUtil;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import it.unimi.dsi.fastutil.longs.LongArrayList;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.ColorResolver;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.core.Holder;
|
||||
#endif
|
||||
|
||||
|
||||
public abstract class AbstractDhTintGetter implements BlockAndTintGetter
|
||||
{
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
private static final ConcurrentHashMap<String, Biome> BIOME_BY_RESOURCE_STRING = new ConcurrentHashMap<>();
|
||||
#else
|
||||
private static final ConcurrentHashMap<String, Holder<Biome>> BIOME_BY_RESOURCE_STRING = new ConcurrentHashMap<>();
|
||||
#endif
|
||||
|
||||
private static final ConcurrentHashMap<BlockBiomeWrapperPair, Integer> COLOR_BY_BLOCK_BIOME_PAIR = new ConcurrentHashMap<>();
|
||||
/** returned if the color cache is incomplete */
|
||||
public static final int INVALID_COLOR = Integer.MIN_VALUE;
|
||||
|
||||
|
||||
protected BiomeWrapper biomeWrapper;
|
||||
protected BlockStateWrapper blockStateWrapper;
|
||||
protected FullDataSourceV2 fullDataSource;
|
||||
protected int smoothingRadiusInBlocks;
|
||||
protected IClientLevelWrapper clientLevelWrapper;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public AbstractDhTintGetter() { }
|
||||
|
||||
/**
|
||||
* Mutates this getter so we can access the necessary
|
||||
* variables for tint getting.
|
||||
*/
|
||||
public void update(BiomeWrapper biomeWrapper, BlockStateWrapper blockStateWrapper, FullDataSourceV2 fullDataSource, IClientLevelWrapper clientLevelWrapper)
|
||||
{
|
||||
this.biomeWrapper = biomeWrapper;
|
||||
this.blockStateWrapper = blockStateWrapper;
|
||||
this.fullDataSource = fullDataSource;
|
||||
this.clientLevelWrapper = clientLevelWrapper;
|
||||
this.smoothingRadiusInBlocks = Config.Client.Advanced.Graphics.Quality.lodBiomeBlending.get();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// shared methods //
|
||||
//================//
|
||||
|
||||
/** Called by MC's tint getter */
|
||||
@Override
|
||||
public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
|
||||
{
|
||||
DhBlockPosMutable mutableBlockPos = new DhBlockPosMutable(blockPos.getX(), blockPos.getY(), blockPos.getZ());
|
||||
return this.tryGetBlockTint(mutableBlockPos, colorResolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Can be called by DH directly, skipping some of MC's logic
|
||||
* to speed up tint getting slightly.
|
||||
*
|
||||
* @return {@link AbstractDhTintGetter#INVALID_COLOR} if any of the biomes needed for this position
|
||||
* were not cached. In that case calling {@link AbstractDhTintGetter#getBlockTint(BlockPos, ColorResolver)}
|
||||
* will need to be called by MC's ColorResolver so we can
|
||||
* populate the color cache.
|
||||
*/
|
||||
public int tryGetBlockTint(DhBlockPosMutable mutableBlockPos)
|
||||
{ return this.tryGetBlockTint(mutableBlockPos, null); }
|
||||
|
||||
private int tryGetBlockTint(DhBlockPosMutable mutableBlockPos, @Nullable ColorResolver colorResolver)
|
||||
{
|
||||
// determine how wide this data source is so we can determine
|
||||
// if blending should be used
|
||||
byte dataSourceDetailLevel = DhSectionPos.getDetailLevel(this.fullDataSource.getPos());
|
||||
// convert from section detail level to absolute detail level
|
||||
dataSourceDetailLevel = (byte)(dataSourceDetailLevel - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
|
||||
int dataSourceLodWidthInBlocks = DhSectionPos.getDetailLevelWidthInBlocks(dataSourceDetailLevel);
|
||||
|
||||
// don't do any smoothing if smoothing is disabled or if the LOD
|
||||
// is to large for block-based smoothing to show up
|
||||
if (this.smoothingRadiusInBlocks == 0
|
||||
|| dataSourceLodWidthInBlocks > this.smoothingRadiusInBlocks)
|
||||
{
|
||||
return this.tryGetClientBiomeColor(colorResolver, this.biomeWrapper);
|
||||
}
|
||||
|
||||
|
||||
// use a rolling average to calculate the color
|
||||
int dataPointCount = 0;
|
||||
int rollingRed = 0;
|
||||
int rollingGreen = 0;
|
||||
int rollingBlue = 0;
|
||||
|
||||
int xMin = mutableBlockPos.getX() - this.smoothingRadiusInBlocks;
|
||||
int xMax = mutableBlockPos.getX() + this.smoothingRadiusInBlocks + 1; // +1 to account for the center block
|
||||
|
||||
int zMin = mutableBlockPos.getZ() - this.smoothingRadiusInBlocks;
|
||||
int zMax = mutableBlockPos.getZ() + this.smoothingRadiusInBlocks + 1;
|
||||
|
||||
int levelMinY = this.clientLevelWrapper.getMinHeight();
|
||||
|
||||
for (int x = xMin; x < xMax; x++)
|
||||
{
|
||||
for (int z = zMin; z < zMax; z++)
|
||||
{
|
||||
mutableBlockPos.setX(x);
|
||||
mutableBlockPos.setZ(z);
|
||||
|
||||
// this can return the same position/datapoint for larger LODs duplicating work,
|
||||
// however for small smoothing ranges that isn't a big deal and for large LODs
|
||||
// we ignore smoothing anyway
|
||||
long dataPoint = this.fullDataSource.getDataPointAtBlockPos(mutableBlockPos.getX(), mutableBlockPos.getY(), mutableBlockPos.getZ(), levelMinY);
|
||||
if (dataPoint == FullDataPointUtil.EMPTY_DATA_POINT)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
// get the color for this nearby position
|
||||
int id = FullDataPointUtil.getId(dataPoint);
|
||||
BiomeWrapper biomeWrapper = (BiomeWrapper) this.fullDataSource.mapping.getBiomeWrapper(id);
|
||||
int color = this.tryGetClientBiomeColor(colorResolver, biomeWrapper);
|
||||
if (color == INVALID_COLOR)
|
||||
{
|
||||
return INVALID_COLOR;
|
||||
}
|
||||
|
||||
|
||||
// rolling average
|
||||
rollingRed += ColorUtil.getRed(color);
|
||||
rollingGreen += ColorUtil.getGreen(color);
|
||||
rollingBlue += ColorUtil.getBlue(color);
|
||||
|
||||
dataPointCount++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if no data was present (rarely possible)
|
||||
// just use the default center's color
|
||||
if (dataPointCount == 0)
|
||||
{
|
||||
return this.tryGetClientBiomeColor(colorResolver, this.biomeWrapper);
|
||||
}
|
||||
|
||||
int colorInt = ColorUtil.argbToInt(
|
||||
255, // blending often ignores alpha, having it always 255 prevents multiplication issues later
|
||||
rollingRed / dataPointCount,
|
||||
rollingGreen / dataPointCount,
|
||||
rollingBlue / dataPointCount);
|
||||
return colorInt;
|
||||
}
|
||||
|
||||
/**
|
||||
* If given a ColorResolver this will always succeed. <Br>
|
||||
* If not it will attempt to use the cached color.
|
||||
*/
|
||||
private int tryGetClientBiomeColor(@Nullable ColorResolver colorResolver, BiomeWrapper biomeWrapper)
|
||||
{
|
||||
BlockBiomeWrapperPair pair = BlockBiomeWrapperPair.get(this.blockStateWrapper, biomeWrapper);
|
||||
|
||||
// use the cached color if possible
|
||||
Integer cachedColor = COLOR_BY_BLOCK_BIOME_PAIR.get(pair); // explicit Integer return here reduces unnecessary allocations
|
||||
if (cachedColor != null)
|
||||
{
|
||||
return cachedColor;
|
||||
}
|
||||
|
||||
if (colorResolver == null)
|
||||
{
|
||||
// no color resolver is present,
|
||||
// the cache needs to be populated before
|
||||
// we can use the fast path
|
||||
return INVALID_COLOR;
|
||||
}
|
||||
|
||||
|
||||
int color = colorResolver.getColor(unwrapClientBiome(biomeWrapper), 0, 0);
|
||||
COLOR_BY_BLOCK_BIOME_PAIR.put(pair, color);
|
||||
return color;
|
||||
}
|
||||
|
||||
protected static Biome unwrapClientBiome(BiomeWrapper biomeWrapper)
|
||||
{
|
||||
String biomeString = biomeWrapper.getSerialString();
|
||||
if (biomeString == null
|
||||
|| biomeString.isEmpty()
|
||||
|| biomeString.equals(BiomeWrapper.EMPTY_BIOME_STRING))
|
||||
{
|
||||
// default to "plains" for empty/invalid biomes
|
||||
biomeString = "minecraft:plains";
|
||||
}
|
||||
|
||||
return unwrapBiome(getClientBiome(biomeString));
|
||||
}
|
||||
|
||||
protected static Biome unwrapBiome(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
|
||||
{
|
||||
#if MC_VER >= MC_1_18_2
|
||||
return biome.value();
|
||||
#else
|
||||
return biome;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>Previously, this class might have immediately unwrapped the Holder like this:</p>
|
||||
* <pre>{@code
|
||||
* // Inside constructor (OLD WAY - PROBLEMATIC):
|
||||
* Holder<Biome> biomeHolder = getTheHolderFromSomewhere();
|
||||
* this.biome = biomeHolder.value(); // <-- PROBLEM HERE
|
||||
* }</pre>
|
||||
*
|
||||
* <p>This approach is problematic because the {@link net.minecraft.core.Holder} system,
|
||||
* particularly {@code Holder.Reference}, is designed for <strong>late binding</strong>. Here's why storing
|
||||
* the Holder itself is now necessary:</p>
|
||||
* <ol>
|
||||
* <li>A {@code Holder.Reference<Biome>} might be created initially just with a
|
||||
* {@link net.minecraft.resources.ResourceKey} (like {@code minecraft:plains}), but its actual
|
||||
* {@link net.minecraft.core.Holder#value() value()} (the {@code Biome} object itself) might be {@code null}
|
||||
* at construction time.</li>
|
||||
* <li>Later, during game loading, registry population, or potentially due to modifications by other mods
|
||||
* (e.g., Polytone), the system calls internal binding methods (like {@code bindValue(Biome)})
|
||||
* on the {@code Holder} instance. This sets or <strong>updates</strong> the internal reference to the
|
||||
* actual {@code Biome} object.</li>
|
||||
* <li>Crucially, the binding process might assign a completely <strong>new</strong> {@code Biome} object
|
||||
* instance to the {@code Holder} reference, replacing any previous one.</li>
|
||||
* </ol>
|
||||
*
|
||||
* <p>If we unwrapped the {@code Holder} using {@code .value()} within the constructor (the old way),
|
||||
* our class's internal {@code biome} field would permanently store a reference to whatever {@code Biome}
|
||||
* object the {@code Holder} pointed to *at that exact moment*. It would have no link back to the
|
||||
* {@code Holder} and would be unaware if the {@code Holder} was later updated to point to a different
|
||||
* (or the initially missing) {@code Biome} object. This would lead to using stale or even {@code null} data.</p>
|
||||
*
|
||||
* <p>By storing the {@code Holder<Biome>} itself, this class can call {@link net.minecraft.core.Holder#value()}
|
||||
* whenever the biome information is needed, ensuring it always retrieves the most current {@code Biome}
|
||||
* instance associated with the holder at that time.</p>
|
||||
*/
|
||||
private static #if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif getClientBiome(String biomeResourceString)
|
||||
{
|
||||
#if MC_VER < MC_1_18_2
|
||||
Biome biome;
|
||||
#else
|
||||
Holder<Biome> biome;
|
||||
#endif
|
||||
|
||||
// calling get instead of compute is slightly faster for already
|
||||
// computed values
|
||||
biome = BIOME_BY_RESOURCE_STRING.get(biomeResourceString);
|
||||
if (biome != null)
|
||||
{
|
||||
return biome;
|
||||
}
|
||||
|
||||
|
||||
// cache the client biomes so we don't have to re-parse the resource location every time
|
||||
return BIOME_BY_RESOURCE_STRING.compute(biomeResourceString,
|
||||
(resourceString, existingBiome) ->
|
||||
{
|
||||
if (existingBiome != null)
|
||||
{
|
||||
return existingBiome;
|
||||
}
|
||||
|
||||
ClientLevel clientLevel = Minecraft.getInstance().level;
|
||||
if (clientLevel == null)
|
||||
{
|
||||
// shouldn't happen, but just in case
|
||||
throw new IllegalStateException("Attempted to get client biome when no client level was loaded.");
|
||||
}
|
||||
|
||||
BiomeWrapper.BiomeDeserializeResult result;
|
||||
try
|
||||
{
|
||||
result = BiomeWrapper.deserializeBiome(resourceString, clientLevel.registryAccess());
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.warn("Unable to deserialize client biome ["+resourceString+"], using fallback...");
|
||||
|
||||
try
|
||||
{
|
||||
result = BiomeWrapper.deserializeBiome(BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING, clientLevel.registryAccess());
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
// should never happen, if it does this log will explode, but just in case
|
||||
LOGGER.error("Unable to deserialize fallback client biome ["+BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING+"], returning NULL.");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (result.success)
|
||||
{
|
||||
existingBiome = result.biome;
|
||||
}
|
||||
|
||||
return existingBiome;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+53
-110
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -22,14 +22,13 @@ 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;
|
||||
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import net.minecraft.world.level.Level;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper;
|
||||
|
||||
@@ -45,14 +44,8 @@ import net.minecraft.core.Holder;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
#endif
|
||||
|
||||
#if MC_VER <= MC_1_21_10
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
#else
|
||||
import net.minecraft.resources.Identifier;
|
||||
#endif
|
||||
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
#endif
|
||||
@@ -62,7 +55,7 @@ import net.minecraft.world.level.biome.Biomes;
|
||||
public class BiomeWrapper implements IBiomeWrapper
|
||||
{
|
||||
// must be defined before AIR, otherwise a null pointer will be thrown
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
private static final Logger LOGGER = LogManager.getLogger();
|
||||
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
@@ -108,7 +101,7 @@ public class BiomeWrapper implements IBiomeWrapper
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public static BiomeWrapper getBiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
|
||||
static public IBiomeWrapper getBiomeWrapper(#if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome, ILevelWrapper levelWrapper)
|
||||
{
|
||||
if (biome == null)
|
||||
{
|
||||
@@ -116,10 +109,9 @@ public class BiomeWrapper implements IBiomeWrapper
|
||||
}
|
||||
|
||||
|
||||
BiomeWrapper biomeWrapper = WRAPPER_BY_BIOME.get(biome);
|
||||
if (biomeWrapper != null)
|
||||
if (WRAPPER_BY_BIOME.containsKey(biome))
|
||||
{
|
||||
return biomeWrapper;
|
||||
return WRAPPER_BY_BIOME.get(biome);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -137,6 +129,14 @@ public class BiomeWrapper implements IBiomeWrapper
|
||||
//LOGGER.trace("Created BiomeWrapper ["+this.serialString+"] for ["+biome+"]");
|
||||
}
|
||||
|
||||
/** should only be used to create {@link BiomeWrapper#EMPTY_WRAPPER} */
|
||||
private BiomeWrapper()
|
||||
{
|
||||
this.biome = null;
|
||||
this.serialString = EMPTY_BIOME_STRING;
|
||||
this.hashCode = Objects.hash(this.serialString);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
@@ -221,20 +221,13 @@ public class BiomeWrapper implements IBiomeWrapper
|
||||
Level level = (Level)levelWrapper.getWrappedMcObject();
|
||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
||||
|
||||
#if MC_VER < MC_1_21_11
|
||||
ResourceLocation resourceLocation;
|
||||
#else
|
||||
Identifier resourceLocation;
|
||||
#endif
|
||||
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
resourceLocation = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).getKey(this.biome);
|
||||
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
|
||||
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)
|
||||
@@ -290,16 +283,50 @@ public class BiomeWrapper implements IBiomeWrapper
|
||||
BiomeWrapper foundWrapper = EMPTY_WRAPPER;
|
||||
try
|
||||
{
|
||||
// parse the resource location
|
||||
int separatorIndex = resourceLocationString.indexOf(":");
|
||||
if (separatorIndex == -1)
|
||||
{
|
||||
throw new IOException("Unable to parse resource location string: [" + resourceLocationString + "].");
|
||||
}
|
||||
|
||||
ResourceLocation resourceLocation;
|
||||
try
|
||||
{
|
||||
#if MC_VER < MC_1_21_1
|
||||
resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
|
||||
#else
|
||||
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new IOException("No Resource Location found for the string: [" + resourceLocationString + "] Error: [" + e.getMessage() + "].");
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
Level level = (Level) levelWrapper.getWrappedMcObject();
|
||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
||||
|
||||
BiomeDeserializeResult deserializeResult = deserializeBiome(resourceLocationString, registryAccess);
|
||||
boolean success;
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
Biome biome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
|
||||
success = (biome != null);
|
||||
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
|
||||
Biome unwrappedBiome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
|
||||
success = (unwrappedBiome != null);
|
||||
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
|
||||
#else
|
||||
Biome unwrappedBiome = registryAccess.registryOrThrow(Registries.BIOME).get(resourceLocation);
|
||||
success = (unwrappedBiome != null);
|
||||
Holder<Biome> biome = new Holder.Direct<>(unwrappedBiome);
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
if (!deserializeResult.success)
|
||||
if (!success)
|
||||
{
|
||||
if (!brokenResourceLocationStrings.contains(resourceLocationString))
|
||||
{
|
||||
@@ -310,7 +337,7 @@ public class BiomeWrapper implements IBiomeWrapper
|
||||
}
|
||||
|
||||
|
||||
foundWrapper = getBiomeWrapper(deserializeResult.biome, levelWrapper);
|
||||
foundWrapper = (BiomeWrapper) getBiomeWrapper(biome, levelWrapper);
|
||||
return foundWrapper;
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -324,88 +351,4 @@ public class BiomeWrapper implements IBiomeWrapper
|
||||
}
|
||||
}
|
||||
|
||||
public static BiomeDeserializeResult deserializeBiome(String resourceLocationString, net.minecraft.core.RegistryAccess registryAccess) throws IOException
|
||||
{
|
||||
// parse the resource location
|
||||
int separatorIndex = resourceLocationString.indexOf(":");
|
||||
if (separatorIndex == -1)
|
||||
{
|
||||
throw new IOException("Unable to parse resource location string: [" + resourceLocationString + "].");
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_21_11
|
||||
ResourceLocation resourceLocation;
|
||||
#else
|
||||
Identifier resourceLocation;
|
||||
#endif
|
||||
try
|
||||
{
|
||||
#if MC_VER <= MC_1_20_6
|
||||
resourceLocation = new ResourceLocation(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
|
||||
#elif MC_VER <= MC_1_21_10
|
||||
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
|
||||
#else
|
||||
resourceLocation = Identifier.fromNamespaceAndPath(resourceLocationString.substring(0, separatorIndex), resourceLocationString.substring(separatorIndex + 1));
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new IOException("No Resource Location found for the string: [" + resourceLocationString + "] Error: [" + e.getMessage() + "].");
|
||||
}
|
||||
|
||||
|
||||
boolean success;
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
Biome biome = registryAccess.registryOrThrow(Registry.BIOME_REGISTRY).get(resourceLocation);
|
||||
success = (biome != null);
|
||||
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
|
||||
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
|
||||
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
|
||||
|
||||
return new BiomeDeserializeResult(success, biome);
|
||||
}
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
public static class BiomeDeserializeResult
|
||||
{
|
||||
public final boolean success;
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
public final Biome biome;
|
||||
#else
|
||||
public final Holder<Biome> biome;
|
||||
#endif
|
||||
|
||||
public BiomeDeserializeResult(boolean success, #if MC_VER < MC_1_18_2 Biome #else Holder<Biome> #endif biome)
|
||||
{
|
||||
this.success = success;
|
||||
this.biome = biome;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
+37
-144
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -28,13 +28,14 @@ import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
|
||||
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 com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.awt.*;
|
||||
import java.io.IOException;
|
||||
@@ -42,8 +43,6 @@ import java.util.*;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.BlockPos;
|
||||
@@ -59,13 +58,6 @@ 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
|
||||
|
||||
#if MC_VER <= MC_1_21_10
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
#else
|
||||
import net.minecraft.resources.Identifier;
|
||||
#endif
|
||||
|
||||
public class BlockStateWrapper implements IBlockStateWrapper
|
||||
@@ -77,7 +69,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
|
||||
|
||||
// must be defined before AIR, otherwise a null pointer will be thrown
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
public static final ConcurrentHashMap<BlockState, BlockStateWrapper> WRAPPER_BY_BLOCK_STATE = new ConcurrentHashMap<>();
|
||||
public static final ConcurrentHashMap<String, BlockStateWrapper> WRAPPER_BY_RESOURCE_LOCATION = new ConcurrentHashMap<>();
|
||||
@@ -86,35 +78,31 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
public static final BlockStateWrapper AIR = new BlockStateWrapper(null, null);
|
||||
|
||||
public static final String DIRT_RESOURCE_LOCATION_STRING = "minecraft:dirt";
|
||||
public static final String WATER_RESOURCE_LOCATION_STRING = "minecraft:water";
|
||||
|
||||
public static HashSet<IBlockStateWrapper> rendererIgnoredBlocks = null;
|
||||
public static HashSet<IBlockStateWrapper> rendererIgnoredCaveBlocks = null;
|
||||
|
||||
/** keep track of broken blocks so we don't log every time */
|
||||
#if MC_VER <= MC_1_21_10
|
||||
private static final HashSet<ResourceLocation> BROKEN_RESOURCE_LOCATIONS = new HashSet<>();
|
||||
#else
|
||||
private static final HashSet<Identifier> BROKEN_RESOURCE_LOCATIONS = new HashSet<>();
|
||||
#endif
|
||||
private static final HashSet<ResourceLocation> BrokenResourceLocations = new HashSet<>();
|
||||
|
||||
|
||||
|
||||
// properties //
|
||||
|
||||
@Nullable
|
||||
public final BlockState blockState;
|
||||
/** technically final, but since it requires a method call to generate it can't be marked as such */
|
||||
private String serialString;
|
||||
private final int hashCode;
|
||||
/** Should be between {@link LodUtil#BLOCK_FULLY_OPAQUE} and {@link LodUtil#BLOCK_FULLY_OPAQUE} */
|
||||
private final int opacity;
|
||||
/**
|
||||
* Cached opacity value, -1 if not populated. <br>
|
||||
* Should be between {@link LodUtil#BLOCK_FULLY_OPAQUE} and {@link LodUtil#BLOCK_FULLY_OPAQUE}
|
||||
*/
|
||||
private int opacity = -1;
|
||||
/** used by the Iris shader mod to determine how each LOD should be rendered */
|
||||
private byte blockMaterialId = 0;
|
||||
|
||||
private final boolean isBeaconBlock;
|
||||
private final boolean isBeaconBaseBlock;
|
||||
private final boolean allowsBeaconBeamPassage;
|
||||
/** null if this block can't tint beacons */
|
||||
private final Color beaconTintColor;
|
||||
private final Color mapColor;
|
||||
@@ -154,10 +142,10 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
BlockState guessBlockState = (guess == null || guess.isAir()) ? null : (BlockState) guess.getWrappedMcObject();
|
||||
BlockState inputBlockState = (blockState == null || blockState.isAir()) ? null : blockState;
|
||||
|
||||
if (guess instanceof BlockStateWrapper
|
||||
&& guessBlockState == inputBlockState)
|
||||
if (guess instanceof BlockStateWrapper guessWrapper
|
||||
&& guessBlockState == inputBlockState)
|
||||
{
|
||||
return (BlockStateWrapper) guess;
|
||||
return guessWrapper;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -171,12 +159,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
this.serialString = this.serialize(levelWrapper);
|
||||
this.hashCode = Objects.hash(this.serialString);
|
||||
this.blockMaterialId = this.calculateEDhApiBlockMaterialId().index;
|
||||
this.opacity = this.calculateOpacity();
|
||||
|
||||
String lowercaseSerial = this.serialString.toLowerCase();
|
||||
|
||||
|
||||
// beacon blocks
|
||||
String lowercaseSerial = this.serialString.toLowerCase();
|
||||
boolean isBeaconBaseBlock = false;
|
||||
for (int i = 0; i < LodUtil.BEACON_BASE_BLOCK_NAME_LIST.size(); i++)
|
||||
{
|
||||
@@ -212,39 +197,6 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
this.beaconTintColor = beaconTintColor;
|
||||
|
||||
|
||||
// allow/deny beacon beam passage
|
||||
boolean allowsBeaconBeamPassage;
|
||||
if (this.blockState != null)
|
||||
{
|
||||
// get block properties (defaults to the values used by air)
|
||||
boolean canOcclude = this.getCanOcclude();
|
||||
boolean propagatesSkyLightDown = this.getPropagatesSkyLightDown();
|
||||
|
||||
if (lowercaseSerial.contains("minecraft:bedrock"))
|
||||
{
|
||||
// bedrock is a special case fully opaque block that does allow beacons through
|
||||
allowsBeaconBeamPassage = true;
|
||||
}
|
||||
else if (propagatesSkyLightDown || !canOcclude)
|
||||
{
|
||||
// stairs, cake, fences, etc.
|
||||
allowsBeaconBeamPassage = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// non-opaque blocks (glass, mob spawners, etc.)
|
||||
// all allow beacons through
|
||||
allowsBeaconBeamPassage = (this.opacity != LodUtil.BLOCK_FULLY_OPAQUE);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// air allows beacons through
|
||||
allowsBeaconBeamPassage = true;
|
||||
}
|
||||
this.allowsBeaconBeamPassage = allowsBeaconBeamPassage;
|
||||
|
||||
|
||||
int mcColor = 0;
|
||||
if (this.blockState != null)
|
||||
{
|
||||
@@ -283,7 +235,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
|
||||
HashSet<String> baseIgnoredBlock = new HashSet<>();
|
||||
baseIgnoredBlock.add(AIR_STRING);
|
||||
rendererIgnoredBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderBlockCsv, baseIgnoredBlock, levelWrapper);
|
||||
rendererIgnoredBlocks = getBlockWrappers(Config.Client.Advanced.LodBuilding.ignoredRenderBlockCsv, baseIgnoredBlock, levelWrapper);
|
||||
return rendererIgnoredBlocks;
|
||||
}
|
||||
/**
|
||||
@@ -300,7 +252,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
|
||||
HashSet<String> baseIgnoredBlock = new HashSet<>();
|
||||
baseIgnoredBlock.add(AIR_STRING);
|
||||
rendererIgnoredCaveBlocks = getAllBlockWrappers(Config.Client.Advanced.Graphics.Culling.ignoredRenderCaveBlockCsv, baseIgnoredBlock, levelWrapper);
|
||||
rendererIgnoredCaveBlocks = getBlockWrappers(Config.Client.Advanced.LodBuilding.ignoredRenderCaveBlockCsv, baseIgnoredBlock, levelWrapper);
|
||||
return rendererIgnoredCaveBlocks;
|
||||
}
|
||||
|
||||
@@ -311,7 +263,7 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
|
||||
// lod builder helpers //
|
||||
|
||||
private static HashSet<IBlockStateWrapper> getAllBlockWrappers(ConfigEntry<String> config, HashSet<String> baseResourceLocations, ILevelWrapper levelWrapper)
|
||||
private static HashSet<IBlockStateWrapper> getBlockWrappers(ConfigEntry<String> config, HashSet<String> baseResourceLocations, ILevelWrapper levelWrapper)
|
||||
{
|
||||
// get the base blocks
|
||||
HashSet<String> blockStringList = new HashSet<>();
|
||||
@@ -327,9 +279,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
blockStringList.addAll(Arrays.asList(ignoreBlockCsv.split(",")));
|
||||
}
|
||||
|
||||
return getAllBlockWrappers(blockStringList, levelWrapper);
|
||||
return getBlockWrappers(blockStringList, levelWrapper);
|
||||
}
|
||||
private static HashSet<IBlockStateWrapper> getAllBlockWrappers(HashSet<String> blockResourceLocationSet, ILevelWrapper levelWrapper)
|
||||
private static HashSet<IBlockStateWrapper> getBlockWrappers(HashSet<String> blockResourceLocationSet, ILevelWrapper levelWrapper)
|
||||
{
|
||||
// deserialize each of the given resource locations
|
||||
HashSet<IBlockStateWrapper> blockStateWrappers = new HashSet<>();
|
||||
@@ -388,13 +340,13 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
//=================//
|
||||
|
||||
@Override
|
||||
public int getOpacity() { return this.opacity; }
|
||||
private int calculateOpacity()
|
||||
public int getOpacity()
|
||||
{
|
||||
// get block properties (defaults to the values used by air)
|
||||
boolean canOcclude = this.getCanOcclude();
|
||||
boolean propagatesSkyLightDown = this.getPropagatesSkyLightDown();
|
||||
|
||||
// use the cached opacity value if possible
|
||||
if (this.opacity != -1)
|
||||
{
|
||||
return this.opacity;
|
||||
}
|
||||
|
||||
|
||||
// this method isn't perfect, but works well enough for our use case
|
||||
@@ -403,20 +355,15 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
{
|
||||
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
|
||||
}
|
||||
else if (this.isLiquid() && !canOcclude)
|
||||
else if (this.isLiquid() && !this.blockState.canOcclude())
|
||||
{
|
||||
// probably not a waterlogged block (which should block light entirely)
|
||||
|
||||
// +1 to indicate that the block is translucent (in between transparent and opaque)
|
||||
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT + 1;
|
||||
}
|
||||
else if (propagatesSkyLightDown && !canOcclude)
|
||||
else if (this.blockState.propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO))
|
||||
{
|
||||
// probably glass or some other fully transparent block
|
||||
|
||||
// !canOcclude is required to ignore stairs and slabs since
|
||||
// propagateSkyLightDown is true for them, but they're solid and don't actually let light through
|
||||
|
||||
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
|
||||
}
|
||||
else
|
||||
@@ -426,37 +373,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
}
|
||||
|
||||
|
||||
return opacity;
|
||||
this.opacity = opacity;
|
||||
return this.opacity;
|
||||
}
|
||||
private boolean getCanOcclude()
|
||||
{
|
||||
// defaults to the value used by air
|
||||
boolean canOcclude = false;
|
||||
if (this.blockState != null)
|
||||
{
|
||||
canOcclude = this.blockState.canOcclude();
|
||||
}
|
||||
|
||||
return canOcclude;
|
||||
}
|
||||
private boolean getPropagatesSkyLightDown()
|
||||
{
|
||||
// defaults to the value used by air
|
||||
boolean propagatesSkyLightDown = true;
|
||||
if (this.blockState != null)
|
||||
{
|
||||
#if MC_VER < MC_1_21_3
|
||||
propagatesSkyLightDown = this.blockState.propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
||||
#else
|
||||
propagatesSkyLightDown = this.blockState.propagatesSkylightDown();
|
||||
#endif
|
||||
}
|
||||
|
||||
return propagatesSkyLightDown;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public int getLightEmission() { return (this.blockState != null) ? this.blockState.getLightEmission() : 0; }
|
||||
@@ -496,11 +415,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
|
||||
@@ -529,8 +443,6 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
public boolean isBeaconBaseBlock() { return this.isBeaconBaseBlock; }
|
||||
@Override
|
||||
public boolean isBeaconTintBlock() { return this.beaconTintColor != null; }
|
||||
@Override
|
||||
public boolean allowsBeaconBeamPassage() { return this.allowsBeaconBeamPassage; }
|
||||
|
||||
@Override
|
||||
public Color getMapColor() { return this.mapColor; }
|
||||
@@ -564,20 +476,13 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
net.minecraft.core.RegistryAccess registryAccess = level.registryAccess();
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_21_11
|
||||
ResourceLocation resourceLocation;
|
||||
#else
|
||||
Identifier resourceLocation;
|
||||
#endif
|
||||
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
resourceLocation = Registry.BLOCK.getKey(this.blockState.getBlock());
|
||||
#elif MC_VER == MC_1_18_2 || MC_VER == MC_1_19_2
|
||||
resourceLocation = registryAccess.registryOrThrow(Registry.BLOCK_REGISTRY).getKey(this.blockState.getBlock());
|
||||
#elif MC_VER < MC_1_21_3
|
||||
resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
|
||||
#else
|
||||
resourceLocation = registryAccess.lookupOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
|
||||
resourceLocation = registryAccess.registryOrThrow(Registries.BLOCK).getKey(this.blockState.getBlock());
|
||||
#endif
|
||||
|
||||
|
||||
@@ -601,8 +506,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.equals("")) // the empty string shouldn't normally happen, but just in case
|
||||
{
|
||||
return AIR;
|
||||
}
|
||||
@@ -636,20 +540,13 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
throw new IOException("Unable to parse Resource Location out of string: [" + resourceStateString + "].");
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_21_11
|
||||
ResourceLocation resourceLocation;
|
||||
#else
|
||||
Identifier resourceLocation;
|
||||
#endif
|
||||
|
||||
try
|
||||
{
|
||||
#if MC_VER < MC_1_21_1
|
||||
resourceLocation = new ResourceLocation(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
|
||||
#elif MC_VER <= MC_1_21_10
|
||||
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
|
||||
#else
|
||||
resourceLocation = Identifier.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
|
||||
resourceLocation = ResourceLocation.fromNamespaceAndPath(resourceStateString.substring(0, separatorIndex), resourceStateString.substring(separatorIndex + 1));
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -674,22 +571,18 @@ 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
|
||||
|
||||
|
||||
if (block == null)
|
||||
{
|
||||
// shouldn't normally happen, but here to make the compiler happy
|
||||
if (!BROKEN_RESOURCE_LOCATIONS.contains(resourceLocation))
|
||||
if (!BrokenResourceLocations.contains(resourceLocation))
|
||||
{
|
||||
BROKEN_RESOURCE_LOCATIONS.add(resourceLocation);
|
||||
BrokenResourceLocations.add(resourceLocation);
|
||||
LOGGER.warn("Unable to find BlockState with the resourceLocation [" + resourceLocation + "] and properties: [" + blockStatePropertiesString + "]. Air will be used instead, some data may be lost.");
|
||||
}
|
||||
|
||||
@@ -719,9 +612,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
||||
if (blockStatePropertiesString != null)
|
||||
{
|
||||
// we should have found a blockstate, but didn't
|
||||
if (!BROKEN_RESOURCE_LOCATIONS.contains(resourceLocation))
|
||||
if (!BrokenResourceLocations.contains(resourceLocation))
|
||||
{
|
||||
BROKEN_RESOURCE_LOCATIONS.add(resourceLocation);
|
||||
BrokenResourceLocations.add(resourceLocation);
|
||||
LOGGER.warn("Unable to find BlockState for Block [" + resourceLocation + "] with properties: [" + blockStatePropertiesString + "]. Using the default block state.");
|
||||
}
|
||||
}
|
||||
|
||||
+54
-140
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -20,16 +20,15 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.block;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPosMutable;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.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;
|
||||
@@ -40,19 +39,12 @@ import net.minecraft.util.RandomSource;
|
||||
import java.util.Random;
|
||||
#endif
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
#if MC_VER < MC_1_21_5
|
||||
#else
|
||||
import net.minecraft.client.renderer.block.model.BlockModelPart;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* This stores and calculates the colors
|
||||
* the given {@link BlockState} should have based
|
||||
@@ -62,7 +54,7 @@ import net.minecraft.client.renderer.block.model.BlockModelPart;
|
||||
*/
|
||||
public class ClientBlockStateColorCache
|
||||
{
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
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<>();
|
||||
@@ -92,12 +84,13 @@ public class ClientBlockStateColorCache
|
||||
private static final RandomSource RANDOM = RandomSource.create();
|
||||
#endif
|
||||
|
||||
private final IClientLevelWrapper clientLevelWrapper;
|
||||
private final IClientLevelWrapper levelWrapper;
|
||||
private final BlockState blockState;
|
||||
private final BlockStateWrapper blockStateWrapper;
|
||||
private final LevelReader level;
|
||||
|
||||
private boolean isColorResolved = false;
|
||||
private int baseColor = 0;
|
||||
private boolean needShade = true;
|
||||
private boolean needPostTinting = false;
|
||||
private int tintIndex = 0;
|
||||
|
||||
@@ -165,21 +158,17 @@ public class ClientBlockStateColorCache
|
||||
0.93011117f, 0.9386859f, 0.9473069f, 0.9559735f, 0.9646866f, 0.9734455f, 0.98225087f, 0.9911022f, 1.0f
|
||||
};
|
||||
|
||||
private static final ThreadLocal<TintWithoutLevelOverrider> TintWithoutLevelOverrideGetter = ThreadLocal.withInitial(() -> new TintWithoutLevelOverrider());
|
||||
private static final ThreadLocal<TintGetterOverride> TintOverrideGetter = ThreadLocal.withInitial(() -> new TintGetterOverride());
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper clientLevelWrapper)
|
||||
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper samplingLevel)
|
||||
{
|
||||
this.blockState = blockState;
|
||||
this.blockStateWrapper = BlockStateWrapper.fromBlockState(blockState, clientLevelWrapper);
|
||||
this.clientLevelWrapper = clientLevelWrapper;
|
||||
|
||||
this.levelWrapper = samplingLevel;
|
||||
this.level = (LevelReader) samplingLevel.getWrappedMcObject();
|
||||
this.resolveColors();
|
||||
}
|
||||
|
||||
@@ -207,7 +196,9 @@ public class ClientBlockStateColorCache
|
||||
List<BakedQuad> quads = null;
|
||||
for (Direction direction : COLOR_RESOLUTION_DIRECTION_ORDER)
|
||||
{
|
||||
quads = this.getQuadsForDirection(direction);
|
||||
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
|
||||
getBlockModel(this.blockState).getQuads(this.blockState, direction, RANDOM);
|
||||
|
||||
if (quads != null && !quads.isEmpty()
|
||||
&& !(
|
||||
this.blockState.getBlock() instanceof RotatedPillarBlock
|
||||
@@ -221,53 +212,40 @@ public class ClientBlockStateColorCache
|
||||
|
||||
if (quads == null || quads.isEmpty())
|
||||
{
|
||||
quads = this.getUnculledQuads();
|
||||
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
|
||||
getBlockModel(this.blockState).getQuads(this.blockState, null, RANDOM);
|
||||
}
|
||||
|
||||
if (quads != null
|
||||
&& !quads.isEmpty()
|
||||
&& quads.get(0) != null)
|
||||
if (quads != null && !quads.isEmpty())
|
||||
{
|
||||
BakedQuad firstQuad = quads.get(0);
|
||||
|
||||
this.needPostTinting = firstQuad.isTinted();
|
||||
#if MC_VER <= MC_1_21_4
|
||||
this.tintIndex = firstQuad.getTintIndex();
|
||||
#else
|
||||
this.tintIndex = firstQuad.tintIndex();
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_17_1
|
||||
this.needPostTinting = quads.get(0).isTinted();
|
||||
this.needShade = quads.get(0).isShade();
|
||||
this.tintIndex = quads.get(0).getTintIndex();
|
||||
this.baseColor = calculateColorFromTexture(
|
||||
firstQuad.sprite,
|
||||
EColorMode.getColorMode(this.blockState.getBlock()));
|
||||
#elif MC_VER < MC_1_21_5
|
||||
this.baseColor = calculateColorFromTexture(
|
||||
firstQuad.getSprite(),
|
||||
EColorMode.getColorMode(this.blockState.getBlock()));
|
||||
#else
|
||||
this.baseColor = calculateColorFromTexture(
|
||||
firstQuad.sprite(),
|
||||
EColorMode.getColorMode(this.blockState.getBlock()));
|
||||
#endif
|
||||
#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 = this.getParticleIconColor();
|
||||
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 = this.getParticleIconColor();
|
||||
this.baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
|
||||
ColorMode.getColorMode(this.blockState.getBlock()));
|
||||
}
|
||||
|
||||
|
||||
this.isColorResolved = true;
|
||||
}
|
||||
finally
|
||||
@@ -275,37 +253,8 @@ public class ClientBlockStateColorCache
|
||||
RESOLVE_LOCK.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private List<BakedQuad> getUnculledQuads() { return this.getQuadsForDirection(null); }
|
||||
@Nullable
|
||||
private List<BakedQuad> getQuadsForDirection(@Nullable Direction direction)
|
||||
{
|
||||
List<BakedQuad> quads = null;
|
||||
|
||||
#if MC_VER < MC_1_21_5
|
||||
quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
|
||||
getBlockModel(this.blockState).getQuads(this.blockState, direction, RANDOM);
|
||||
#else
|
||||
List<BlockModelPart> blockModelPartList = Minecraft.getInstance().getModelManager().getBlockModelShaper().
|
||||
getBlockModel(this.blockState).collectParts(RANDOM);
|
||||
|
||||
quads = new ArrayList<>();
|
||||
if (blockModelPartList != null)
|
||||
{
|
||||
for (int i = 0; i < blockModelPartList.size(); i++)
|
||||
{
|
||||
// if direction is null this will return the unculled quads
|
||||
quads.addAll(blockModelPartList.get(i).getQuads(direction));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
return quads;
|
||||
}
|
||||
|
||||
//TODO: Perhaps make this not just use the first frame?
|
||||
private static int calculateColorFromTexture(TextureAtlasSprite texture, EColorMode colorMode)
|
||||
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode)
|
||||
{
|
||||
int count = 0;
|
||||
int alpha = 0;
|
||||
@@ -315,8 +264,8 @@ public class ClientBlockStateColorCache
|
||||
int tempColor;
|
||||
|
||||
// don't render Chiseled blocks.
|
||||
// Since EColorMode is set per block, you only need to check this once.
|
||||
if (colorMode != EColorMode.Chisel)
|
||||
// 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++)
|
||||
@@ -334,7 +283,7 @@ public class ClientBlockStateColorCache
|
||||
int b = (tempColor & 0x00FF0000) >>> 16;
|
||||
int a = (tempColor & 0xFF000000) >>> 24;
|
||||
int scale = 1;
|
||||
if (colorMode == EColorMode.Leaves)
|
||||
if (colorMode == ColorMode.Leaves)
|
||||
{
|
||||
//switch (//FIXME add config option)
|
||||
// case BLACK:
|
||||
@@ -353,11 +302,11 @@ public class ClientBlockStateColorCache
|
||||
// break; //do nothing, let it count towards transparency
|
||||
|
||||
}
|
||||
else if (a == 0 && colorMode != EColorMode.Glass)
|
||||
else if (a == 0 && colorMode != ColorMode.Glass)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if (colorMode == EColorMode.Flower && (g + 25 < b || g + 25 < r))
|
||||
else if (colorMode == ColorMode.Flower && (g + 25 < b || g + 25 < r))
|
||||
{
|
||||
scale = FLOWER_COLOR_SCALE;
|
||||
}
|
||||
@@ -375,12 +324,12 @@ public class ClientBlockStateColorCache
|
||||
if (count == 0)
|
||||
{
|
||||
// this block is entirely transparent
|
||||
tempColor = ColorUtil.argbToInt(0, 255, 255, 255);
|
||||
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
|
||||
}
|
||||
else
|
||||
{
|
||||
// determine the average color
|
||||
tempColor = ColorUtil.argbToInt(
|
||||
tempColor = ColorUtil.rgbToInt(
|
||||
alpha / count,
|
||||
linearToSrgb((float) (red / (double) alpha)),
|
||||
linearToSrgb((float) (green / (double) alpha)),
|
||||
@@ -388,10 +337,10 @@ public class ClientBlockStateColorCache
|
||||
}
|
||||
|
||||
//check if not missing texture
|
||||
if (tempColor == ColorUtil.argbToInt(255, 182, 0, 182))
|
||||
if (tempColor == ColorUtil.rgbToInt(255, 182, 0, 182))
|
||||
{
|
||||
//make it not render at all
|
||||
tempColor = ColorUtil.argbToInt(0, 255, 255, 255);
|
||||
tempColor = ColorUtil.rgbToInt(0, 255, 255, 255);
|
||||
}
|
||||
return tempColor;
|
||||
}
|
||||
@@ -415,18 +364,16 @@ public class ClientBlockStateColorCache
|
||||
* 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 color)
|
||||
private static int linearToSrgb(float c)
|
||||
{
|
||||
if (!(color > MIN_SRGB_BOUND))
|
||||
{
|
||||
color = MIN_SRGB_BOUND;
|
||||
if (!(c > MIN_SRGB_BOUND)) {
|
||||
c = MIN_SRGB_BOUND;
|
||||
}
|
||||
|
||||
if (color > MAX_SRGB_BOUND)
|
||||
{
|
||||
color = MAX_SRGB_BOUND;
|
||||
if (c > MAX_SRGB_BOUND) {
|
||||
c = MAX_SRGB_BOUND;
|
||||
}
|
||||
int inputBits = Float.floatToRawIntBits(color);
|
||||
int inputBits = Float.floatToRawIntBits(c);
|
||||
int entry = linearToSrgbTable[((inputBits - MIN_SRGB_BITS) >> 20)];
|
||||
|
||||
int bias = (entry >>> 16) << 9;
|
||||
@@ -436,20 +383,13 @@ public class ClientBlockStateColorCache
|
||||
return (bias + (scale * t)) >>> 16;
|
||||
}
|
||||
|
||||
private int getParticleIconColor()
|
||||
{
|
||||
return calculateColorFromTexture(
|
||||
Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
|
||||
EColorMode.getColorMode(this.blockState.getBlock()));
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===============//
|
||||
// public getter //
|
||||
//===============//
|
||||
|
||||
public int getColor(BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource, DhBlockPos blockPos)
|
||||
public int getColor(BiomeWrapper biome, DhBlockPos pos)
|
||||
{
|
||||
// only get the tint if the block needs to be tinted
|
||||
if (!this.needPostTinting)
|
||||
@@ -473,27 +413,13 @@ public class ClientBlockStateColorCache
|
||||
{
|
||||
try
|
||||
{
|
||||
TintWithoutLevelOverrider tintOverride = TintWithoutLevelOverrideGetter.get();
|
||||
tintOverride.update(biomeWrapper, this.blockStateWrapper, fullDataSource, this.clientLevelWrapper);
|
||||
|
||||
// try using DH's cached tint values first if possible
|
||||
tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos));
|
||||
if (tintColor == AbstractDhTintGetter.INVALID_COLOR)
|
||||
{
|
||||
// one or more tint values weren't calculated,
|
||||
// we need MC's color resolver
|
||||
tintColor = Minecraft.getInstance()
|
||||
.getBlockColors()
|
||||
.getColor(this.blockState,
|
||||
tintOverride,
|
||||
McObjectConverter.Convert(blockPos),
|
||||
this.tintIndex);
|
||||
}
|
||||
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: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: [" + e.getMessage() + "]. Attempting to use backup method...", e);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -501,22 +427,10 @@ public class ClientBlockStateColorCache
|
||||
// use the level logic only if requested
|
||||
if (BLOCK_STATES_THAT_NEED_LEVEL.contains(this.blockState))
|
||||
{
|
||||
// the level shouldn't be used all the time due to it breaking some blocks tinting
|
||||
// this logic can't be used all the time due to it breaking some blocks tinting
|
||||
// specifically oceans don't render correctly
|
||||
|
||||
TintGetterOverride tintOverride = TintOverrideGetter.get();
|
||||
tintOverride.update(biomeWrapper, this.blockStateWrapper, fullDataSource, this.clientLevelWrapper);
|
||||
|
||||
tintColor = tintOverride.tryGetBlockTint(new DhBlockPosMutable(blockPos));
|
||||
if (tintColor == AbstractDhTintGetter.INVALID_COLOR)
|
||||
{
|
||||
tintColor = Minecraft.getInstance()
|
||||
.getBlockColors()
|
||||
.getColor(this.blockState,
|
||||
tintOverride,
|
||||
McObjectConverter.Convert(blockPos),
|
||||
this.tintIndex);
|
||||
}
|
||||
tintColor = Minecraft.getInstance().getBlockColors()
|
||||
.getColor(this.blockState, new TintGetterOverrideFast(this.level), McObjectConverter.Convert(pos), this.tintIndex);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -524,7 +438,7 @@ public class ClientBlockStateColorCache
|
||||
// only display the error once per block/biome type to reduce log spam
|
||||
if (!BROKEN_BLOCK_STATES.contains(this.blockState))
|
||||
{
|
||||
LOGGER.warn("Failed to get block color for block: [" + this.blockState + "] and biome: [" + biomeWrapper + "] at pos: " + blockPos + ". Error: ["+e.getMessage() + "]. Note: future errors for this block/biome will be ignored.", e);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -548,7 +462,7 @@ public class ClientBlockStateColorCache
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
private enum EColorMode
|
||||
enum ColorMode
|
||||
{
|
||||
Default,
|
||||
Flower,
|
||||
@@ -556,7 +470,7 @@ public class ClientBlockStateColorCache
|
||||
Chisel,
|
||||
Glass;
|
||||
|
||||
static EColorMode getColorMode(Block block)
|
||||
static ColorMode getColorMode(Block block)
|
||||
{
|
||||
if (block instanceof LeavesBlock) return Leaves;
|
||||
if (block instanceof FlowerBlock) return Flower;
|
||||
|
||||
+12
-25
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
+231
@@ -0,0 +1,231 @@
|
||||
/*
|
||||
* 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 net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import net.minecraft.world.phys.AABB;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import net.minecraft.world.phys.shapes.VoxelShape;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class TintGetterOverrideFast implements BlockAndTintGetter
|
||||
{
|
||||
LevelReader parent;
|
||||
|
||||
public TintGetterOverrideFast(LevelReader parent)
|
||||
{
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
private Biome _getBiome(BlockPos pos)
|
||||
{
|
||||
#if MC_VER >= MC_1_18_2
|
||||
return parent.getBiome(pos).value();
|
||||
#else
|
||||
return parent.getBiome(pos);
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) { return colorResolver.getColor(this._getBiome(blockPos), blockPos.getX(), blockPos.getZ()); }
|
||||
|
||||
@Override
|
||||
public float getShade(Direction direction, boolean bl) { return this.parent.getShade(direction, bl); }
|
||||
|
||||
@Override
|
||||
public LevelLightEngine getLightEngine()
|
||||
{
|
||||
return parent.getLightEngine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getBrightness(LightLayer lightLayer, BlockPos blockPos)
|
||||
{
|
||||
return parent.getBrightness(lightLayer, blockPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRawBrightness(BlockPos blockPos, int i)
|
||||
{
|
||||
return parent.getRawBrightness(blockPos, i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canSeeSky(BlockPos blockPos)
|
||||
{
|
||||
return parent.canSeeSky(blockPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BlockEntity getBlockEntity(BlockPos blockPos)
|
||||
{
|
||||
return parent.getBlockEntity(blockPos);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos blockPos)
|
||||
{
|
||||
return parent.getBlockState(blockPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public FluidState getFluidState(BlockPos blockPos)
|
||||
{
|
||||
return parent.getFluidState(blockPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getLightEmission(BlockPos blockPos)
|
||||
{
|
||||
return parent.getLightEmission(blockPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxLightLevel()
|
||||
{
|
||||
return parent.getMaxLightLevel();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<BlockState> getBlockStates(AABB aABB)
|
||||
{
|
||||
return parent.getBlockStates(aABB);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockHitResult clip(ClipContext clipContext)
|
||||
{
|
||||
return parent.clip(clipContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState)
|
||||
{
|
||||
return parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier)
|
||||
{
|
||||
return parent.getBlockFloorHeight(voxelShape, supplier);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getBlockFloorHeight(BlockPos blockPos)
|
||||
{
|
||||
return parent.getBlockFloorHeight(blockPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
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 parent.getBlockEntity(blockPos, blockEntityType);
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext)
|
||||
{
|
||||
return parent.isBlockInLine(clipBlockStateContext);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getHeight()
|
||||
{
|
||||
return parent.getHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinBuildHeight()
|
||||
{
|
||||
return parent.getMinBuildHeight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSectionsCount()
|
||||
{
|
||||
return parent.getSectionsCount();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMinSection()
|
||||
{
|
||||
return parent.getMinSection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMaxSection()
|
||||
{
|
||||
return parent.getMaxSection();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOutsideBuildHeight(BlockPos blockPos)
|
||||
{
|
||||
return parent.isOutsideBuildHeight(blockPos);
|
||||
}
|
||||
|
||||
@Override
|
||||
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
|
||||
}
|
||||
+40
-44
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -19,11 +19,11 @@
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Cursor3D;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.BlockEntityType;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
@@ -39,29 +39,51 @@ import java.util.Optional;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
public class TintGetterOverride extends AbstractDhTintGetter
|
||||
public class TintGetterOverrideSmooth implements BlockAndTintGetter
|
||||
{
|
||||
private LevelReader parent;
|
||||
LevelReader parent;
|
||||
public int smoothingRange;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public TintGetterOverride() { }
|
||||
|
||||
public void update(LevelReader parent, BiomeWrapper biomeWrapper, BlockStateWrapper blockStateWrapper, FullDataSourceV2 fullDataSource, IClientLevelWrapper clientLevelWrapper)
|
||||
public TintGetterOverrideSmooth(LevelReader parent, int smoothingRange)
|
||||
{
|
||||
super.update(biomeWrapper, blockStateWrapper, fullDataSource, clientLevelWrapper);
|
||||
this.parent = parent;
|
||||
this.smoothingRange = smoothingRange;
|
||||
}
|
||||
|
||||
private Biome _getBiome(BlockPos pos)
|
||||
{
|
||||
#if MC_VER >= MC_1_18_2
|
||||
return parent.getBiome(pos).value();
|
||||
#else
|
||||
return parent.getBiome(pos);
|
||||
#endif
|
||||
}
|
||||
|
||||
public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver)
|
||||
{
|
||||
int i = smoothingRange;
|
||||
if (i == 0)
|
||||
return colorResolver.getColor(_getBiome(blockPos), blockPos.getX(), blockPos.getZ());
|
||||
int j = (i * 2 + 1) * (i * 2 + 1);
|
||||
int k = 0;
|
||||
int l = 0;
|
||||
int m = 0;
|
||||
Cursor3D cursor3D = new Cursor3D(blockPos.getX() - i, blockPos.getY(), blockPos.getZ() - i, blockPos.getX() + i, blockPos.getY(), blockPos.getZ() + i);
|
||||
BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
||||
while (cursor3D.advance())
|
||||
{
|
||||
mutableBlockPos.set(cursor3D.nextX(), cursor3D.nextY(), cursor3D.nextZ());
|
||||
int n = colorResolver.getColor(this._getBiome(mutableBlockPos), mutableBlockPos.getX(), mutableBlockPos.getZ());
|
||||
|
||||
k += (n & 0xFF0000) >> 16;
|
||||
l += (n & 0xFF00) >> 8;
|
||||
m += n & 0xFF;
|
||||
}
|
||||
return (k / j & 0xFF) << 16 | (l / j & 0xFF) << 8 | m / j & 0xFF;
|
||||
}
|
||||
|
||||
//=========//
|
||||
// methods //
|
||||
//=========//
|
||||
@Override
|
||||
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver) { return this.calculateBlockTint(blockPos, colorResolver); }
|
||||
|
||||
@Override
|
||||
public float getShade(Direction direction, boolean bl) { return this.parent.getShade(direction, bl); }
|
||||
@@ -91,11 +113,8 @@ public class TintGetterOverride extends AbstractDhTintGetter
|
||||
@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); }
|
||||
@@ -116,13 +135,8 @@ public class TintGetterOverride extends AbstractDhTintGetter
|
||||
@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
|
||||
@@ -134,32 +148,17 @@ public class TintGetterOverride extends AbstractDhTintGetter
|
||||
@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 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); }
|
||||
@@ -176,7 +175,4 @@ public class TintGetterOverride extends AbstractDhTintGetter
|
||||
@Override
|
||||
public int getSectionYFromSectionIndex(int i) { return this.parent.getSectionYFromSectionIndex(i); }
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
}
|
||||
+98
-40
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -19,72 +19,130 @@
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block;
|
||||
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.LevelReader;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
public class TintWithoutLevelOverrider extends AbstractDhTintGetter
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.core.Holder;
|
||||
#endif
|
||||
|
||||
public class TintWithoutLevelOverrider implements BlockAndTintGetter
|
||||
{
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public TintWithoutLevelOverrider()
|
||||
{ }
|
||||
/**
|
||||
* This will only ever be null if there was an issue with {@link IClientLevelWrapper#getPlainsBiomeWrapper()}
|
||||
* but {@link Nullable} is there just in case.
|
||||
*/
|
||||
@Nullable
|
||||
private final Biome biome;
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// methods //
|
||||
//=========//
|
||||
public TintWithoutLevelOverrider(BiomeWrapper biomeWrapper, IClientLevelWrapper clientLevelWrapper)
|
||||
{
|
||||
// try to get the wrapped biome
|
||||
Biome unwrappedBiome = null;
|
||||
if (biomeWrapper.biome != null)
|
||||
{
|
||||
unwrappedBiome = unwrap(biomeWrapper.biome);
|
||||
}
|
||||
|
||||
if(unwrappedBiome == null)
|
||||
{
|
||||
// we are looking at the empty biome wrapper, try using plains as a backup
|
||||
BiomeWrapper plainsBiomeWrapper = ((BiomeWrapper) clientLevelWrapper.getPlainsBiomeWrapper());
|
||||
if (plainsBiomeWrapper != null)
|
||||
{
|
||||
unwrappedBiome = unwrap(plainsBiomeWrapper.biome);
|
||||
}
|
||||
}
|
||||
|
||||
this.biome = unwrappedBiome;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public float getShade(Direction direction, boolean shade)
|
||||
{ throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only."); }
|
||||
public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
|
||||
{
|
||||
if (this.biome != null)
|
||||
{
|
||||
return colorResolver.getColor(this.biome, blockPos.getX(), blockPos.getZ());
|
||||
}
|
||||
else
|
||||
{
|
||||
// hopefully unneeded debug color
|
||||
return ColorUtil.CYAN;
|
||||
}
|
||||
}
|
||||
|
||||
private static Biome unwrap(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
|
||||
{
|
||||
#if MC_VER >= MC_1_18_2
|
||||
return biome.value();
|
||||
#else
|
||||
return biome;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// unused methods //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public LevelLightEngine getLightEngine()
|
||||
{ throw new UnsupportedOperationException("ERROR: getLightEngine() called on TintWithoutLevelOverrider. Object is for tinting only."); }
|
||||
public float getShade(@NotNull Direction direction, boolean shade)
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
@Override
|
||||
public @NotNull LevelLightEngine getLightEngine()
|
||||
{
|
||||
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 TintWithoutLevelOverrider. Object is for tinting only."); }
|
||||
public BlockEntity getBlockEntity(@NotNull BlockPos pos)
|
||||
{
|
||||
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 TintWithoutLevelOverrider. Object is for tinting only."); }
|
||||
public @NotNull BlockState getBlockState(@NotNull BlockPos pos)
|
||||
{
|
||||
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 TintWithoutLevelOverrider. Object is for tinting only."); }
|
||||
public @NotNull FluidState getFluidState(@NotNull BlockPos pos)
|
||||
{
|
||||
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 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 TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
|
||||
#else
|
||||
@Override
|
||||
public int getMinY()
|
||||
{ throw new UnsupportedOperationException("ERROR: getMinY() called on TintWithoutLevelOverrider. Object is for tinting only."); }
|
||||
#endif
|
||||
|
||||
public int getMinBuildHeight()
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
/*
|
||||
* 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 net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.BlockAndTintGetter;
|
||||
import net.minecraft.world.level.ColorResolver;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.core.Holder;
|
||||
#endif
|
||||
|
||||
public class TintWithoutLevelSmoothOverrider implements BlockAndTintGetter
|
||||
{
|
||||
final BiomeWrapper biome;
|
||||
public int smoothingRange;
|
||||
|
||||
public TintWithoutLevelSmoothOverrider(BiomeWrapper biome, int smoothingRange)
|
||||
{
|
||||
this.biome = biome;
|
||||
this.smoothingRange = smoothingRange;
|
||||
}
|
||||
@Override
|
||||
public int getBlockTint(BlockPos blockPos, ColorResolver colorResolver)
|
||||
{
|
||||
return colorResolver.getColor(_unwrap(biome.biome), blockPos.getX(), blockPos.getZ());
|
||||
}
|
||||
private Biome _unwrap(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
|
||||
{
|
||||
#if MC_VER >= MC_1_18_2
|
||||
return biome.value();
|
||||
#else
|
||||
return biome;
|
||||
#endif
|
||||
}
|
||||
//
|
||||
// public int calculateBlockTint(BlockPos blockPos, ColorResolver colorResolver)
|
||||
// {
|
||||
// int i = smoothingRange;
|
||||
// if (i == 0)
|
||||
// return colorResolver.getColor(_getBiome(blockPos), blockPos.getX(), blockPos.getZ());
|
||||
// int j = (i * 2 + 1) * (i * 2 + 1);
|
||||
// int k = 0;
|
||||
// int l = 0;
|
||||
// int m = 0;
|
||||
// Cursor3D cursor3D = new Cursor3D(blockPos.getX() - i, blockPos.getY(), blockPos.getZ() - i, blockPos.getX() + i, blockPos.getY(), blockPos.getZ() + i);
|
||||
// BlockPos.MutableBlockPos mutableBlockPos = new BlockPos.MutableBlockPos();
|
||||
// while (cursor3D.advance())
|
||||
// {
|
||||
// mutableBlockPos.set(cursor3D.nextX(), cursor3D.nextY(), cursor3D.nextZ());
|
||||
// int n;
|
||||
// if (LodCommonMain.forgeMethodCaller != null) {
|
||||
// n = LodCommonMain.forgeMethodCaller.colorResolverGetColor(colorResolver, _getBiome(mutableBlockPos),
|
||||
// mutableBlockPos.getX(), mutableBlockPos.getZ());
|
||||
// } else {
|
||||
// n = colorResolver.getColor(_getBiome(mutableBlockPos), mutableBlockPos.getX(), mutableBlockPos.getZ());
|
||||
// }
|
||||
//
|
||||
// k += (n & 0xFF0000) >> 16;
|
||||
// l += (n & 0xFF00) >> 8;
|
||||
// m += n & 0xFF;
|
||||
// }
|
||||
// return (k / j & 0xFF) << 16 | (l / j & 0xFF) << 8 | m / j & 0xFF;
|
||||
// }
|
||||
|
||||
@Override
|
||||
public float getShade(Direction direction, boolean shade)
|
||||
{
|
||||
throw new UnsupportedOperationException("ERROR: getShade() called on TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
@Override
|
||||
public LevelLightEngine getLightEngine()
|
||||
{
|
||||
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 TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockState getBlockState(BlockPos pos)
|
||||
{
|
||||
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 TintWithoutLevelOverrider. Object is for tinting only.");
|
||||
}
|
||||
|
||||
|
||||
#if MC_VER >= MC_1_17_1
|
||||
@Override
|
||||
public int getHeight()
|
||||
{
|
||||
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.");
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
+67
-227
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -16,16 +16,19 @@
|
||||
* 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.chunk;
|
||||
|
||||
import com.seibel.distanthorizons.api.DhApi;
|
||||
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.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
|
||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.world.EWorldEnvironment;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
@@ -33,14 +36,19 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IMutableBlockPosWr
|
||||
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 com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
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;
|
||||
@@ -64,6 +72,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
|
||||
@@ -75,21 +85,22 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
|
||||
public class ChunkWrapper implements IChunkWrapper
|
||||
{
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
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 boolean heightmapThreadWarningLogged = false;
|
||||
|
||||
|
||||
private final ChunkAccess chunk;
|
||||
private final DhChunkPos chunkPos;
|
||||
private final LevelReader lightSource;
|
||||
private final ILevelWrapper wrappedLevel;
|
||||
|
||||
private boolean isDhBlockLightCorrect = false;
|
||||
private boolean isDhSkyLightCorrect = false;
|
||||
/** only used when connected to a dedicated server */
|
||||
private boolean isMcClientLightingCorrect = false;
|
||||
|
||||
private ChunkLightStorage blockLightStorage;
|
||||
private ChunkLightStorage skyLightStorage;
|
||||
@@ -99,32 +110,21 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
private int minNonEmptyHeight = Integer.MIN_VALUE;
|
||||
private int maxNonEmptyHeight = Integer.MAX_VALUE;
|
||||
|
||||
/** will be null if we are using MC heightmaps */
|
||||
private int[][] solidHeightMap = null;
|
||||
/** will be null if we are using MC heightmaps */
|
||||
private int[][] lightBlockingHeightMap = null;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
/**
|
||||
* Note: this constructor should be very
|
||||
* fast since it will be called frequently on the MC
|
||||
* server thread and a slow method will cause server lag.
|
||||
*/
|
||||
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);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public ChunkWrapper copy() { return new ChunkWrapper(this.chunk, this.wrappedLevel); }
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
@@ -143,29 +143,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()
|
||||
@@ -177,7 +167,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();
|
||||
@@ -209,7 +199,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();
|
||||
@@ -242,99 +232,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(); }
|
||||
|
||||
|
||||
@Override
|
||||
public void createDhHeightMaps()
|
||||
{
|
||||
if (heightmapThreadWarningLogged
|
||||
&& !DhApi.isDhThread())
|
||||
{
|
||||
LOGGER.warn("ChunkWrapper Height maps created on non-DH thread ["+Thread.currentThread().getName()+"]. This may cause stuttering.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
this.solidHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
|
||||
this.lightBlockingHeightMap = new int[LodUtil.CHUNK_WIDTH][LodUtil.CHUNK_WIDTH];
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
public int getSolidHeightMapValue(int xRel, int zRel) { return this.chunk.getOrCreateHeightmapUnprimed(Heightmap.Types.WORLD_SURFACE).getFirstAvailable(xRel, zRel); }
|
||||
|
||||
@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];
|
||||
}
|
||||
}
|
||||
|
||||
@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
|
||||
@@ -371,7 +276,6 @@ 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);
|
||||
}
|
||||
|
||||
@@ -388,91 +292,6 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
return BlockStateWrapper.fromBlockState(this.chunk.getBlockState(pos), this.wrappedLevel, guess);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
// Commented out experimental LevelChunkSection cloning logic to fix extremely rare concurrency modification issue
|
||||
// James has only ever seen a report relating to LevelSection concurrent modification once,
|
||||
// the issue can cause DH lighting/LOD building to fail due to the chunk being modified on the server.
|
||||
// James has only heard of this issue once, so it isn't a high priority issue.
|
||||
// And from James' quick look at a few different MC versions it appears the LevelChunkSection object changes quite drastically between MC versions,
|
||||
// meaning any cloning logic would have to either be a new wrapper or very MC version dependent, either way a lot of additional work.
|
||||
// Due to the large time cost and extremely rare nature of the issue, this logic is commented out unless this issue pops up again in the future.
|
||||
|
||||
// instance variable to hold the cloned sections
|
||||
private final LevelChunkSection[] levelChunkSections;
|
||||
|
||||
// new constructor logic to clone the sections
|
||||
public constructor(...)
|
||||
{
|
||||
// other constructor logic //
|
||||
|
||||
LevelChunkSection[] sections = this.chunk.getSections();
|
||||
this.levelChunkSections = new LevelChunkSection[sections.length];
|
||||
for (int i = 0; i < sections.length; i++)
|
||||
{
|
||||
LevelChunkSection section = sections[i];
|
||||
if (section != null)
|
||||
{
|
||||
// TODO implement section cloning for older MC versions, only 1.21.4 MC (and maybe other semi recent versions) have a clean way to handle this
|
||||
// TODO we probably want a wrapper object instead
|
||||
#if MC_VER < MC_1_21_4
|
||||
this.levelChunkSections[i] = section;
|
||||
#else
|
||||
this.levelChunkSections[i] = section.copy();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// replacement getters
|
||||
@Override
|
||||
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ)
|
||||
{
|
||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
|
||||
return this.getBlockStateInternal(relX, relY, relZ, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockStateWrapper getBlockState(int relX, int relY, int relZ, IMutableBlockPosWrapper mcBlockPos, IBlockStateWrapper guess)
|
||||
{
|
||||
this.throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(relX, relY, relZ);
|
||||
return this.getBlockStateInternal(relX, relY, relZ, guess);
|
||||
}
|
||||
|
||||
// internal getter logic
|
||||
private IBlockStateWrapper getBlockStateInternal(int relX, int y, int relZ, @Nullable IBlockStateWrapper guess)
|
||||
{
|
||||
try
|
||||
{
|
||||
// attempt to get the section for this position
|
||||
int i = (y - this.getInclusiveMinBuildHeight()) / 16;
|
||||
if (i >= 0 && i < this.levelChunkSections.length)
|
||||
{
|
||||
LevelChunkSection section = this.levelChunkSections[i];
|
||||
if (!section.hasOnlyAir())
|
||||
{
|
||||
if (guess != null)
|
||||
{
|
||||
return BlockStateWrapper.fromBlockState(section.getBlockState(relX & 15, y & 15, relZ & 15), this.wrappedLevel, guess);
|
||||
}
|
||||
else
|
||||
{
|
||||
return BlockStateWrapper.fromBlockState(section.getBlockState(relX & 15, y & 15, relZ & 15), this.wrappedLevel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return BlockStateWrapper.AIR;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return BlockStateWrapper.AIR;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public IMutableBlockPosWrapper getMutableBlockPosWrapper() { return MUTABLE_BLOCK_POS_WRAPPER_REF.get(); }
|
||||
|
||||
@@ -481,20 +300,6 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
|
||||
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)
|
||||
{
|
||||
@@ -620,6 +425,41 @@ public class ChunkWrapper implements IChunkWrapper
|
||||
|
||||
|
||||
|
||||
//===============//
|
||||
// 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; }
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
+417
-772
File diff suppressed because it is too large
Load Diff
-98
@@ -1,98 +0,0 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.gui;
|
||||
|
||||
#if MC_VER < MC_1_21_9
|
||||
// not supported for older MC versions
|
||||
#else
|
||||
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.client.gui.components.debug.DebugScreenDisplayer;
|
||||
import net.minecraft.client.gui.components.debug.DebugScreenEntries;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.chunk.LevelChunk;
|
||||
|
||||
#if MC_VER <= MC_1_21_10
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_21_9
|
||||
// not supported for older MC versions
|
||||
public class DhDebugScreenEntry
|
||||
{}
|
||||
#else
|
||||
public class DhDebugScreenEntry implements net.minecraft.client.gui.components.debug.DebugScreenEntry
|
||||
{
|
||||
public static void register()
|
||||
{
|
||||
// This method is private, so its access will need to be widened
|
||||
DebugScreenEntries.register(
|
||||
// The id, this will be displayed on the options screen
|
||||
#if MC_VER <= MC_1_21_10
|
||||
ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, "distant_horizons"),
|
||||
#else
|
||||
"distant_horizons",
|
||||
#endif
|
||||
|
||||
// The screen entry
|
||||
new DhDebugScreenEntry()
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void display(@NotNull DebugScreenDisplayer displayer, @Nullable Level level, @Nullable LevelChunk clientChunk, @Nullable LevelChunk serverChunk)
|
||||
{
|
||||
List<String> messageList = new ArrayList<>();
|
||||
F3Screen.addStringToDisplay(messageList);
|
||||
|
||||
for (String message : messageList)
|
||||
{
|
||||
displayer.addLine(message);
|
||||
}
|
||||
|
||||
//// The following will display like so if it is the only entry on the screen:
|
||||
//// First left! First Right!
|
||||
////
|
||||
//// Hello world! Random text!
|
||||
//// Lorem ipsum.
|
||||
//// I am another group!
|
||||
//// I am one group This will appear after with no line breaks!
|
||||
//// All in a row
|
||||
//// Provided in a list.
|
||||
////
|
||||
//
|
||||
//displayer.addLine("Hello world!");
|
||||
//displayer.addLine("Lorem ipsum.");
|
||||
//displayer.addLine("Random text!");
|
||||
//
|
||||
//// These will be displayed first
|
||||
//displayer.addPriorityLine("First left!");
|
||||
//displayer.addPriorityLine("First right!");
|
||||
//
|
||||
//// These will be grouped separately based on the key
|
||||
//displayer.addToGroup(GROUP_ONE, List.of(
|
||||
// "I am one group",
|
||||
// "All in a row",
|
||||
// "Provided in a list."
|
||||
//));
|
||||
//
|
||||
//displayer.addToGroup(GROUP_TWO, "I am another group!");
|
||||
//displayer.addToGroup(GROUP_TWO, "This will appear after with no line breaks!");
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isAllowed(boolean reducedDebugInfo)
|
||||
{
|
||||
// Always show regardless of accessibility option
|
||||
return true;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
@@ -52,27 +52,6 @@ public class DhScreen extends Screen
|
||||
{
|
||||
renderTooltip(guiStack, comp, x, y);
|
||||
}
|
||||
#elif MC_VER < MC_1_21_6
|
||||
protected void DhDrawCenteredString(GuiGraphics guiStack, Font font, Component text, int x, int y, int color)
|
||||
{
|
||||
guiStack.drawCenteredString(font, text, x, y, color);
|
||||
}
|
||||
protected void DhDrawString(GuiGraphics guiStack, Font font, Component text, int x, int y, int color)
|
||||
{
|
||||
guiStack.drawString(font, text, x, y, color);
|
||||
}
|
||||
//protected void DhRenderTooltip(GuiGraphics guiStack, Font font, List<? extends net.minecraft.util.FormattedCharSequence> text, int x, int y)
|
||||
//{
|
||||
// guiStack.renderTooltip(font, text, x, y);
|
||||
//}
|
||||
protected void DhRenderComponentTooltip(GuiGraphics guiStack, Font font, List<Component> comp, int x, int y)
|
||||
{
|
||||
guiStack.renderComponentTooltip(font, comp, x, y);
|
||||
}
|
||||
protected void DhRenderTooltip(GuiGraphics guiStack, Font font, Component text, int x, int y)
|
||||
{
|
||||
guiStack.renderTooltip(font, text, x, y);
|
||||
}
|
||||
#else
|
||||
protected void DhDrawCenteredString(GuiGraphics guiStack, Font font, Component text, int x, int y, int color)
|
||||
{
|
||||
@@ -82,20 +61,17 @@ public class DhScreen extends Screen
|
||||
{
|
||||
guiStack.drawString(font, text, x, y, color);
|
||||
}
|
||||
//protected void DhRenderTooltip(GuiGraphics guiStack, Font font, List<? extends net.minecraft.util.FormattedCharSequence> text, int x, int y)
|
||||
//{
|
||||
// //guiStack.renderTooltip(font, text, x, y);
|
||||
//}
|
||||
protected void DhRenderTooltip(GuiGraphics guiStack, Font font, List<? extends net.minecraft.util.FormattedCharSequence> text, int x, int y)
|
||||
{
|
||||
guiStack.renderTooltip(font, text, x, y);
|
||||
}
|
||||
protected void DhRenderComponentTooltip(GuiGraphics guiStack, Font font, List<Component> comp, int x, int y)
|
||||
{
|
||||
guiStack.setComponentTooltipForNextFrame(font, comp, x, y);
|
||||
guiStack.renderComponentTooltip(font, comp, x, y);
|
||||
}
|
||||
protected void DhRenderTooltip(GuiGraphics guiStack, Font font, Component text, int x, int y)
|
||||
{
|
||||
guiStack.setTooltipForNextFrame(font, text, x, y);
|
||||
guiStack.renderTooltip(font, text, x, y);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
+18
-30
@@ -1,52 +1,40 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.gui;
|
||||
|
||||
import com.seibel.distanthorizons.core.config.ConfigHandler;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import com.seibel.distanthorizons.core.config.ConfigBase;
|
||||
import com.seibel.distanthorizons.core.config.gui.ConfigScreen;
|
||||
import com.seibel.distanthorizons.core.config.gui.JavaScreenHandlerScreen;
|
||||
import com.seibel.distanthorizons.core.config.gui.OpenGLConfigScreen;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
|
||||
public class GetConfigScreen
|
||||
{
|
||||
protected static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
public static type useScreen = type.Classic;
|
||||
|
||||
public static EType useScreen = EType.Classic;
|
||||
|
||||
public enum EType
|
||||
public enum type
|
||||
{
|
||||
Classic,
|
||||
JavaSwing;
|
||||
@Deprecated
|
||||
OpenGL, // This was just an attempt, it didn't work out, and we are going to change to javafx soon (as soon as that works)
|
||||
JavaFX;
|
||||
}
|
||||
|
||||
public static Screen getScreen(Screen parent)
|
||||
{
|
||||
// TODO it'd be nice to have this run automatically on startup
|
||||
// but this will only work once MC has added our lang file,
|
||||
// which won't be for sure added until we request a GUI
|
||||
if (ModInfo.IS_DEV_BUILD)
|
||||
{
|
||||
String missingLangEntries = ConfigHandler.INSTANCE.generateLang(true, true);
|
||||
|
||||
// trim to remove any newlines/spaces
|
||||
// that may be present when no lang entries need changing
|
||||
// then we can check length != 0 if any items are missing and need adding
|
||||
String trimmedMissingEntries = missingLangEntries.trim();
|
||||
if (!trimmedMissingEntries.isEmpty())
|
||||
{
|
||||
LOGGER.warn("One or more language entries is missing:");
|
||||
LOGGER.warn(missingLangEntries);
|
||||
}
|
||||
}
|
||||
|
||||
// Generate the language
|
||||
// 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)
|
||||
{
|
||||
case Classic:
|
||||
return ClassicConfigGUI.getScreen(parent, "client");
|
||||
case JavaSwing:
|
||||
//return MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new ConfigScreen()), ModInfo.ID + ".title");
|
||||
return MinecraftScreen.getScreen(parent, new JavaScreenHandlerScreen(new JavaScreenHandlerScreen.ExampleScreen()), ModInfo.ID + ".title");
|
||||
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+"].");
|
||||
}
|
||||
|
||||
+38
-53
@@ -24,26 +24,26 @@ public class MinecraftScreen
|
||||
private static class ConfigScreenRenderer extends DhScreen
|
||||
{
|
||||
private final Screen parent;
|
||||
private ConfigListWidget configListWidget;
|
||||
private ConfigListWidget list;
|
||||
private AbstractScreen screen;
|
||||
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
public static net.minecraft.network.chat.TranslatableComponent translate(String str, Object... args)
|
||||
{ return new net.minecraft.network.chat.TranslatableComponent(str, args); }
|
||||
{
|
||||
return new net.minecraft.network.chat.TranslatableComponent(str, args);
|
||||
}
|
||||
#else
|
||||
public static net.minecraft.network.chat.MutableComponent translate(String str, Object... args)
|
||||
{ return net.minecraft.network.chat.Component.translatable(str, args); }
|
||||
#endif
|
||||
{
|
||||
return net.minecraft.network.chat.Component.translatable(str, args);
|
||||
}
|
||||
#endif
|
||||
|
||||
protected ConfigScreenRenderer(Screen parent, AbstractScreen screen, String translationName)
|
||||
{
|
||||
super(translate(translationName));
|
||||
#if MC_VER < MC_1_21_9
|
||||
screen.minecraftWindow = Minecraft.getInstance().getWindow().getWindow();
|
||||
#else
|
||||
screen.minecraftWindow = Minecraft.getInstance().getWindow().handle();
|
||||
#endif
|
||||
this.parent = parent;
|
||||
this.screen = screen;
|
||||
}
|
||||
@@ -53,22 +53,20 @@ public class MinecraftScreen
|
||||
{
|
||||
super.init(); // Init Minecraft's screen
|
||||
Window mcWindow = this.minecraft.getWindow();
|
||||
this.screen.width = mcWindow.getWidth();
|
||||
this.screen.height = mcWindow.getHeight();
|
||||
this.screen.scaledWidth = this.width;
|
||||
this.screen.scaledHeight = this.height;
|
||||
this.screen.init(); // Init our own config screen
|
||||
screen.width = mcWindow.getWidth();
|
||||
screen.height = mcWindow.getHeight();
|
||||
screen.scaledWidth = this.width;
|
||||
screen.scaledHeight = this.height;
|
||||
screen.init(); // Init our own config screen
|
||||
|
||||
this.configListWidget = new ConfigListWidget(this.minecraft, this.width, this.height, 0, 0, 25); // Select the area to tint
|
||||
this.list = new ConfigListWidget(this.minecraft, this.width, this.height, 0, 0, 25); // Select the area to tint
|
||||
|
||||
#if MC_VER < MC_1_20_6 // no background is rendered in MC 1.20.6+
|
||||
if (this.minecraft != null && this.minecraft.level != null) // Check if in game
|
||||
{
|
||||
this.configListWidget.setRenderBackground(false); // Disable from rendering
|
||||
}
|
||||
this.list.setRenderBackground(false); // Disable from rendering
|
||||
#endif
|
||||
|
||||
this.addWidget(this.configListWidget); // Add the tint to the things to be rendered
|
||||
this.addWidget(this.list); // Add the tint to the things to be rendered
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,71 +78,58 @@ public class MinecraftScreen
|
||||
{
|
||||
#if MC_VER < MC_1_20_2
|
||||
this.renderBackground(matrices); // Render background
|
||||
#elif MC_VER < MC_1_21_6
|
||||
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
|
||||
#else
|
||||
// background blur is already being rendered, rendering again causes the game to crash
|
||||
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
|
||||
#endif
|
||||
this.list.render(matrices, mouseX, mouseY, delta); // Renders the items in the render list (currently only used to tint background darker)
|
||||
|
||||
this.configListWidget.render(matrices, mouseX, mouseY, delta); // Renders the items in the render list (currently only used to tint background darker)
|
||||
|
||||
this.screen.mouseX = mouseX;
|
||||
this.screen.mouseY = mouseY;
|
||||
this.screen.render(delta); // Render everything on the main screen
|
||||
screen.mouseX = mouseX;
|
||||
screen.mouseY = mouseY;
|
||||
screen.render(delta); // Render everything on the main screen
|
||||
|
||||
super.render(matrices, mouseX, mouseY, delta); // Render the vanilla stuff (currently only used for the background and tint)
|
||||
}
|
||||
|
||||
#if MC_VER <= MC_1_21_10
|
||||
@Override
|
||||
public void resize(Minecraft mc, int width, int height)
|
||||
#else
|
||||
@Override
|
||||
public void resize(int width, int height)
|
||||
#endif
|
||||
{
|
||||
// Resize Minecraft's screen
|
||||
#if MC_VER <= MC_1_21_10
|
||||
super.resize(mc, width, height);
|
||||
#else
|
||||
super.resize(width, height);
|
||||
#endif
|
||||
|
||||
|
||||
super.resize(mc, width, height); // Resize Minecraft's screen
|
||||
Window mcWindow = this.minecraft.getWindow();
|
||||
this.screen.width = mcWindow.getWidth();
|
||||
this.screen.height = mcWindow.getHeight();
|
||||
this.screen.scaledWidth = this.width;
|
||||
this.screen.scaledHeight = this.height;
|
||||
this.screen.onResize(); // Resize our screen
|
||||
screen.width = mcWindow.getWidth();
|
||||
screen.height = mcWindow.getHeight();
|
||||
screen.scaledWidth = this.width;
|
||||
screen.scaledHeight = this.height;
|
||||
screen.onResize(); // Resize our screen
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick()
|
||||
{
|
||||
super.tick(); // Tick Minecraft's screen
|
||||
this.screen.tick(); // Tick our screen
|
||||
if (this.screen.close) // If we decide to close the screen, then actually close the screen
|
||||
{
|
||||
this.onClose();
|
||||
}
|
||||
screen.tick(); // Tick our screen
|
||||
if (screen.close) // If we decide to close the screen, then actually close the screen
|
||||
onClose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose()
|
||||
{
|
||||
this.screen.onClose(); // Close our screen
|
||||
Objects.requireNonNull(this.minecraft).setScreen(this.parent); // Goto the parent screen
|
||||
screen.onClose(); // Close our screen
|
||||
Objects.requireNonNull(minecraft).setScreen(this.parent); // Goto the parent screen
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFilesDrop(@NotNull List<Path> files)
|
||||
{ this.screen.onFilesDrop(files); }
|
||||
{
|
||||
screen.onFilesDrop(files);
|
||||
}
|
||||
|
||||
// For checking if it should close when you press the escape key
|
||||
@Override
|
||||
public boolean shouldCloseOnEsc()
|
||||
{ return this.screen.shouldCloseOnEsc; }
|
||||
{
|
||||
return screen.shouldCloseOnEsc;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
+33
-113
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -19,49 +19,31 @@
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.gui;
|
||||
|
||||
import net.minecraft.network.chat.Component;
|
||||
|
||||
#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;
|
||||
#elif MC_VER < MC_1_21_6
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
#elif MC_VER <= MC_1_21_10
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.RenderPipelines;
|
||||
#else
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
import net.minecraft.client.renderer.RenderPipelines;
|
||||
#endif
|
||||
|
||||
#if MC_VER <= MC_1_21_10
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
#else
|
||||
import net.minecraft.resources.Identifier;
|
||||
#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
|
||||
@@ -75,36 +57,20 @@ public class TexturedButtonWidget extends Button
|
||||
private final int v;
|
||||
private final int hoveredVOffset;
|
||||
|
||||
#if MC_VER <= MC_1_21_10
|
||||
private final ResourceLocation textureResourceLocation;
|
||||
#else
|
||||
private final Identifier textureResourceLocation;
|
||||
#endif
|
||||
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,
|
||||
#if MC_VER <= MC_1_21_10 ResourceLocation textureResourceLocation,
|
||||
#else Identifier textureResourceLocation,
|
||||
#endif
|
||||
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,
|
||||
#if MC_VER <= MC_1_21_10 ResourceLocation textureResourceLocation,
|
||||
#else Identifier textureResourceLocation,
|
||||
#endif
|
||||
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
|
||||
@@ -114,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;
|
||||
@@ -126,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
|
||||
@@ -154,7 +119,6 @@ public class TexturedButtonWidget extends Button
|
||||
|
||||
super.renderButton(matrices, mouseX, mouseY, delta);
|
||||
}
|
||||
|
||||
#else
|
||||
#if MC_VER < MC_1_20_1
|
||||
@Override
|
||||
@@ -190,68 +154,24 @@ public class TexturedButtonWidget extends Button
|
||||
super.renderWidget(matrices, mouseX, mouseY, delta);
|
||||
}
|
||||
#endif
|
||||
|
||||
#else
|
||||
#if MC_VER < MC_1_21_11
|
||||
@Override
|
||||
public void renderWidget(GuiGraphics matrices, int mouseX, int mouseY, float delta)
|
||||
#else
|
||||
@Override
|
||||
protected void renderContents(GuiGraphics matrices, int mouseX, int mouseY, float delta)
|
||||
#endif
|
||||
{
|
||||
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());
|
||||
#elif MC_VER < MC_1_21_6
|
||||
matrices.blitSprite(
|
||||
RenderType::guiTextured,
|
||||
SPRITES.get(this.active, this.isHoveredOrFocused()),
|
||||
this.getX(), this.getY(),
|
||||
this.getWidth(), this.getHeight());
|
||||
#else
|
||||
matrices.blitSprite(
|
||||
RenderPipelines.GUI_TEXTURED,
|
||||
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);
|
||||
#elif MC_VER < MC_1_21_6
|
||||
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);
|
||||
#else
|
||||
matrices.blit(
|
||||
RenderPipelines.GUI_TEXTURED,
|
||||
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
|
||||
}
|
||||
|
||||
-33
@@ -1,33 +0,0 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.gui.config;
|
||||
|
||||
import com.seibel.distanthorizons.core.config.gui.IConfigGuiInfo;
|
||||
import com.seibel.distanthorizons.core.config.types.AbstractConfigBase;
|
||||
import net.minecraft.client.gui.components.Button;
|
||||
import net.minecraft.client.gui.components.EditBox;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.util.AbstractMap;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* holds information needed by the config GUI for rendering.
|
||||
*
|
||||
* @see AbstractConfigBase
|
||||
*/
|
||||
public class ConfigGuiInfo implements IConfigGuiInfo
|
||||
{
|
||||
/**
|
||||
* Used to display validation errors.
|
||||
* Null if no error is present.
|
||||
*/
|
||||
@Nullable
|
||||
public Component errorMessage;
|
||||
|
||||
public BiFunction<EditBox, Button, Predicate<String>> tooltipFunction;
|
||||
/** determines which options the button will show */
|
||||
public AbstractMap.SimpleEntry<Button.OnPress, Function<Object, Component>> buttonOptionMap;
|
||||
|
||||
}
|
||||
+38
-64
@@ -1,8 +1,8 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.gui.updater;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import com.seibel.distanthorizons.common.wrappers.gui.DhScreen;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import com.seibel.distanthorizons.core.jar.installer.MarkdownFormatter;
|
||||
@@ -14,7 +14,6 @@ import net.minecraft.client.gui.components.ContainerObjectSelectionList;
|
||||
import net.minecraft.client.gui.components.events.GuiEventListener;
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
|
||||
#if MC_VER >= MC_1_17_1
|
||||
import net.minecraft.client.gui.narration.NarratableEntry;
|
||||
@@ -22,7 +21,6 @@ import net.minecraft.client.gui.narration.NarratableEntry;
|
||||
|
||||
#if MC_VER < MC_1_20_1
|
||||
import net.minecraft.client.gui.GuiComponent;
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
#else
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
#endif
|
||||
@@ -41,9 +39,6 @@ import java.util.*;
|
||||
// TODO: Make this
|
||||
public class ChangelogScreen extends DhScreen
|
||||
{
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
|
||||
|
||||
private Screen parent;
|
||||
private String versionID;
|
||||
private List<String> changelog;
|
||||
@@ -56,33 +51,24 @@ public class ChangelogScreen extends DhScreen
|
||||
this(parent, null);
|
||||
|
||||
if (!ModrinthGetter.initted) // Make sure the modrinth stuff is initted
|
||||
{
|
||||
ModrinthGetter.init();
|
||||
}
|
||||
if (!ModrinthGetter.initted) // If its not initted, then this isnt usable
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ModrinthGetter.mcVersions.contains(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
String versionID = ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion());
|
||||
if (versionID == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
this.setupChangelog(versionID);
|
||||
this.usable = true;
|
||||
setupChangelog(versionID);
|
||||
usable = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.error("failed to setup changelog, error: ["+e.getMessage()+"].", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,13 +80,11 @@ public class ChangelogScreen extends DhScreen
|
||||
|
||||
|
||||
if (versionID == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
try
|
||||
{
|
||||
this.setupChangelog(versionID);
|
||||
this.usable = true;
|
||||
setupChangelog(versionID);
|
||||
usable = true;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -145,10 +129,8 @@ public class ChangelogScreen extends DhScreen
|
||||
protected void init()
|
||||
{
|
||||
super.init();
|
||||
if (!this.usable)
|
||||
{
|
||||
if (!usable)
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
this.addBtn( // Close
|
||||
@@ -159,9 +141,9 @@ public class ChangelogScreen extends DhScreen
|
||||
|
||||
|
||||
this.changelogArea = new TextArea(this.minecraft, this.width * 2, this.height, 32, 32, 10);
|
||||
for (int i = 0; i < this.changelog.size(); i++)
|
||||
for (int i = 0; i < changelog.size(); i++)
|
||||
{
|
||||
this.changelogArea.addButton(TextOrLiteral(this.changelog.get(i)));
|
||||
this.changelogArea.addButton(TextOrLiteral(changelog.get(i)));
|
||||
// drawString(matrices, this.font, changelog.get(i), this.width / 2 - 175, this.height / 2 - 100 + i*10, 0xFFFFFF);
|
||||
}
|
||||
|
||||
@@ -176,47 +158,32 @@ public class ChangelogScreen extends DhScreen
|
||||
{
|
||||
#if MC_VER < MC_1_20_2
|
||||
this.renderBackground(matrices); // Render background
|
||||
#elif MC_VER < MC_1_21_6
|
||||
#else
|
||||
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
|
||||
#else
|
||||
// background blur is already being rendered, rendering again causes the game to crash
|
||||
#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
|
||||
|
||||
|
||||
// render order matters, otherwise on 1.20.6+ the blurred background will render on top of the text
|
||||
super.render(matrices, mouseX, mouseY, delta); // Render the buttons
|
||||
this.changelogArea.render(matrices, mouseX, mouseY, delta); // Render the changelog
|
||||
this.DhDrawCenteredString(matrices, this.font, this.title, this.width / 2, 15, 0xFFFFFF); // Render title
|
||||
DhDrawCenteredString(matrices, font, title, width / 2, 15, 0xFFFFFF); // Render title
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose()
|
||||
{
|
||||
Objects.requireNonNull(this.minecraft).setScreen(this.parent); // Goto the parent screen
|
||||
Objects.requireNonNull(minecraft).setScreen(this.parent); // Goto the parent screen
|
||||
}
|
||||
|
||||
public static class TextArea extends ContainerObjectSelectionList<ButtonEntry>
|
||||
@@ -231,7 +198,7 @@ public class ChangelogScreen extends DhScreen
|
||||
super(minecraftClient, canvasWidth, canvasHeight - (topMargin + botMargin), topMargin, itemSpacing);
|
||||
#endif
|
||||
this.centerListVertically = false;
|
||||
this.textRenderer = minecraftClient.font;
|
||||
textRenderer = minecraftClient.font;
|
||||
}
|
||||
|
||||
public void addButton(Component text)
|
||||
@@ -253,35 +220,42 @@ public class ChangelogScreen extends DhScreen
|
||||
private final Component text;
|
||||
private final List<AbstractWidget> children = new ArrayList<>();
|
||||
|
||||
private ButtonEntry(Component text) { this.text = text; }
|
||||
private ButtonEntry(Component text)
|
||||
{
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public static ButtonEntry create(Component text)
|
||||
{ return new ButtonEntry(text); }
|
||||
{
|
||||
return new ButtonEntry(text);
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_20_1
|
||||
@Override
|
||||
public void render(PoseStack matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
|
||||
{ GuiComponent.drawString(matrices, textRenderer, text, 12, y + 5, 0xFFFFFF); }
|
||||
#elif MC_VER < MC_1_21_9
|
||||
{
|
||||
GuiComponent.drawString(matrices, textRenderer, text, 12, y + 5, 0xFFFFFF);
|
||||
}
|
||||
#else
|
||||
@Override
|
||||
public void render(GuiGraphics matrices, int index, int y, int x, int entryWidth, int entryHeight, int mouseX, int mouseY, boolean hovered, float tickDelta)
|
||||
{ matrices.drawString(textRenderer, this.text, 12, y + 5, 0xFFFFFF); }
|
||||
#else
|
||||
@Override
|
||||
public void renderContent(GuiGraphics matrices, int y, int x, boolean hovered, float tickDelta)
|
||||
{ matrices.drawString(textRenderer, this.text, 12, y + 5, 0xFFFFFF); }
|
||||
{
|
||||
matrices.drawString(textRenderer, text, 12, y + 5, 0xFFFFFF);
|
||||
}
|
||||
#endif
|
||||
|
||||
@Override
|
||||
public List<? extends GuiEventListener> children() { return this.children; }
|
||||
|
||||
public List<? extends GuiEventListener> children()
|
||||
{
|
||||
return children;
|
||||
}
|
||||
#if MC_VER >= MC_1_17_1
|
||||
@Override
|
||||
public List<? extends NarratableEntry> narratables() { return this.children; }
|
||||
public List<? extends NarratableEntry> narratables()
|
||||
{
|
||||
return children;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+16
-55
@@ -4,26 +4,17 @@ import com.seibel.distanthorizons.api.enums.config.EDhApiUpdateBranch;
|
||||
import com.seibel.distanthorizons.common.wrappers.gui.DhScreen;
|
||||
import com.seibel.distanthorizons.common.wrappers.gui.TexturedButtonWidget;
|
||||
import com.seibel.distanthorizons.core.jar.ModJarInfo;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
|
||||
import com.seibel.distanthorizons.core.jar.updater.SelfUpdater;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
|
||||
#if MC_VER >= MC_1_20_1
|
||||
import net.minecraft.client.gui.GuiGraphics;
|
||||
#else
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
#endif
|
||||
|
||||
#if MC_VER <= MC_1_21_10
|
||||
import net.minecraft.client.gui.screens.Screen;
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
#else
|
||||
import net.minecraft.resources.Identifier;
|
||||
#endif
|
||||
|
||||
import static com.seibel.distanthorizons.common.wrappers.gui.GuiHelper.*;
|
||||
|
||||
@@ -38,9 +29,6 @@ import java.util.*;
|
||||
// and also maybe add this suggestion https://discord.com/channels/881614130614767666/1035863487110467625/1035949054485594192
|
||||
public class UpdateModScreen extends DhScreen
|
||||
{
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
|
||||
|
||||
private Screen parent;
|
||||
private String newVersionID;
|
||||
|
||||
@@ -48,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;
|
||||
@@ -66,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
|
||||
@@ -88,21 +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_20_6
|
||||
#if MC_VER < MC_1_21_1
|
||||
new ResourceLocation(ModInfo.ID, "logo.png"),
|
||||
#elif MC_VER <= MC_1_21_10
|
||||
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "logo.png"),
|
||||
#else
|
||||
Identifier.fromNamespaceAndPath(ModInfo.ID, "logo.png"),
|
||||
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 +93,7 @@ public class UpdateModScreen extends DhScreen
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.error("Failed to setup update mod screen, error: ["+e.getMessage()+"].", e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (!ModInfo.IS_DEV_BUILD)
|
||||
@@ -130,14 +109,12 @@ public class UpdateModScreen extends DhScreen
|
||||
0,
|
||||
#if MC_VER < MC_1_21_1
|
||||
new ResourceLocation(ModInfo.ID, "textures/gui/changelog.png"),
|
||||
#elif MC_VER <= MC_1_21_10
|
||||
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
|
||||
#else
|
||||
Identifier.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
|
||||
ResourceLocation.fromNamespaceAndPath(ModInfo.ID, "textures/gui/changelog.png"),
|
||||
#endif
|
||||
20, 20,
|
||||
// Create the button and tell it where to go
|
||||
(buttonWidget) -> Objects.requireNonNull(this.minecraft).setScreen(new ChangelogScreen(this, this.newVersionID)),
|
||||
(buttonWidget) -> Objects.requireNonNull(minecraft).setScreen(new ChangelogScreen(this, this.newVersionID)), // TODO: Add a proper easter egg to pressing the logo (maybe with confetti)
|
||||
// Add a title to the button
|
||||
Translatable(ModInfo.ID + ".updater.title")
|
||||
));
|
||||
@@ -180,10 +157,8 @@ public class UpdateModScreen extends DhScreen
|
||||
{
|
||||
#if MC_VER < MC_1_20_2
|
||||
this.renderBackground(matrices); // Render background
|
||||
#elif MC_VER < MC_1_21_6
|
||||
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
|
||||
#else
|
||||
// background blur is already being rendered, rendering again causes the game to crash
|
||||
this.renderBackground(matrices, mouseX, mouseY, delta); // Render background
|
||||
#endif
|
||||
|
||||
// TODO: add the tooltips for the buttons
|
||||
@@ -191,30 +166,16 @@ public class UpdateModScreen extends DhScreen
|
||||
// TODO: Add tooltips
|
||||
|
||||
// Render the text's
|
||||
this.DhDrawCenteredString(matrices, this.font,
|
||||
Translatable(ModInfo.ID + ".updater.text1"),
|
||||
this.width / 2, this.height / 2 - 35,
|
||||
#if MC_VER < MC_1_21_6
|
||||
0xFFFFFF // RGB
|
||||
#else
|
||||
0xFFFFFFFF // ARGB
|
||||
#endif
|
||||
);
|
||||
this.DhDrawCenteredString(matrices, this.font,
|
||||
Translatable(ModInfo.ID + ".updater.text2", this.currentVer, this.nextVer),
|
||||
this.width / 2, this.height / 2 - 20,
|
||||
#if MC_VER < MC_1_21_6
|
||||
0x52FD52 // RGB
|
||||
#else
|
||||
0xFF52FD52 // ARGB
|
||||
#endif
|
||||
);
|
||||
DhDrawCenteredString(matrices, this.font, Translatable(ModInfo.ID + ".updater.text1"), this.width / 2, this.height / 2 - 35, 0xFFFFFF);
|
||||
DhDrawCenteredString(matrices, this.font,
|
||||
Translatable(ModInfo.ID + ".updater.text2", currentVer, nextVer),
|
||||
this.width / 2, this.height / 2 - 20, 0x52FD52);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose()
|
||||
{
|
||||
Objects.requireNonNull(this.minecraft).setScreen(this.parent); // Go to the parent screen
|
||||
Objects.requireNonNull(minecraft).setScreen(this.parent); // Goto the parent screen
|
||||
}
|
||||
|
||||
}
|
||||
+3
-10
@@ -13,9 +13,6 @@ public class KeyedClientLevelManager implements IKeyedClientLevelManager
|
||||
/** 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;
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -36,20 +33,16 @@ public class KeyedClientLevelManager implements IKeyedClientLevelManager
|
||||
public IServerKeyedClientLevel getServerKeyedLevel() { return this.serverKeyedLevel; }
|
||||
|
||||
@Override
|
||||
public IServerKeyedClientLevel setServerKeyedLevel(IClientLevelWrapper clientLevel, String serverKey, String levelKey)
|
||||
public IServerKeyedClientLevel setServerKeyedLevel(IClientLevelWrapper clientLevel, String levelKey)
|
||||
{
|
||||
IServerKeyedClientLevel keyedLevel = new ServerKeyedClientLevelWrapper((ClientLevel) clientLevel.getWrappedMcObject(), serverKey, levelKey);
|
||||
IServerKeyedClientLevel keyedLevel = new ServerKeyedClientLevel((ClientLevel) clientLevel.getWrappedMcObject(), levelKey);
|
||||
this.serverKeyedLevel = keyedLevel;
|
||||
this.enabled = true;
|
||||
return keyedLevel;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearKeyedLevel() { this.serverKeyedLevel = null; }
|
||||
@Override
|
||||
public boolean isEnabled() { return this.enabled; }
|
||||
@Override
|
||||
public void disable() { this.enabled = false; }
|
||||
|
||||
public boolean hasLevelSet() { return this.serverKeyedLevel != null; }
|
||||
|
||||
}
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.level;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
|
||||
public class ServerKeyedClientLevel extends ClientLevelWrapper implements IServerKeyedClientLevel
|
||||
{
|
||||
/** 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 getDimensionName() { return this.getServerLevelKey(); }
|
||||
|
||||
}
|
||||
-44
@@ -1,44 +0,0 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.level;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
|
||||
public class ServerKeyedClientLevelWrapper extends ClientLevelWrapper implements IServerKeyedClientLevel
|
||||
{
|
||||
/** Returns the folder name the server wants the client to use. */
|
||||
private final String serverKey;
|
||||
|
||||
/** A unique identifier (generally the level's name) for differentiating multiverse levels */
|
||||
private final String serverLevelKey;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public ServerKeyedClientLevelWrapper(ClientLevel level, String serverKey, String serverLevelKey)
|
||||
{
|
||||
super(level);
|
||||
this.serverKey = serverKey;
|
||||
this.serverLevelKey = serverLevelKey;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getServerKey() { return this.serverKey; }
|
||||
|
||||
//======================//
|
||||
// level identification //
|
||||
//======================//
|
||||
|
||||
@Override
|
||||
public String getServerLevelKey() { return this.serverLevelKey; }
|
||||
|
||||
@Override
|
||||
public String getDhIdentifier() { return this.getServerLevelKey(); }
|
||||
|
||||
|
||||
|
||||
}
|
||||
+145
-160
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -20,40 +20,42 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.minecraft;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
import java.util.ArrayList;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiLodShading;
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
|
||||
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
|
||||
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.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
|
||||
import net.minecraft.CrashReport;
|
||||
import net.minecraft.client.CloudStatus;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.client.multiplayer.ServerData;
|
||||
import net.minecraft.client.player.LocalPlayer;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.util.profiling.ProfilerFiller;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import net.minecraft.core.Direction;
|
||||
#if MC_VER < MC_1_19_2
|
||||
import net.minecraft.network.chat.TextComponent;
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
#else
|
||||
import net.minecraft.util.profiling.Profiler;
|
||||
#endif
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A singleton that wraps the Minecraft object.
|
||||
@@ -62,34 +64,95 @@ import net.minecraft.util.profiling.Profiler;
|
||||
*/
|
||||
public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecraftSharedWrapper
|
||||
{
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
|
||||
private static final Minecraft MINECRAFT = Minecraft.getInstance();
|
||||
|
||||
public static final MinecraftClientWrapper INSTANCE = new MinecraftClientWrapper();
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* The lightmap for the current:
|
||||
* Time, dimension, brightness setting, etc.
|
||||
*/
|
||||
private NativeImage lightMap = null;
|
||||
|
||||
private ProfilerWrapper profilerWrapper;
|
||||
|
||||
|
||||
private MinecraftClientWrapper()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//======================//
|
||||
// multiplayer handling //
|
||||
//======================//
|
||||
|
||||
|
||||
//================//
|
||||
// helper methods //
|
||||
//================//
|
||||
|
||||
/**
|
||||
* This should be called at the beginning of every frame to
|
||||
* clear any Minecraft data that becomes out of date after a frame. <br> <br>
|
||||
* <p>
|
||||
* LightMaps and other time sensitive objects fall in this category. <br> <br>
|
||||
* <p>
|
||||
* This doesn't affect OpenGL objects in any way.
|
||||
*/
|
||||
@Override
|
||||
public void clearFrameObjectCache() { this.lightMap = null; }
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// method wrappers //
|
||||
//=================//
|
||||
|
||||
@Override
|
||||
public float getShade(EDhDirection lodDirection)
|
||||
{
|
||||
EDhApiLodShading lodShading = Config.Client.Advanced.Graphics.AdvancedGraphics.lodShading.get();
|
||||
switch (lodShading)
|
||||
{
|
||||
default:
|
||||
case AUTO:
|
||||
if (MINECRAFT.level != null)
|
||||
{
|
||||
Direction mcDir = McObjectConverter.Convert(lodDirection);
|
||||
return MINECRAFT.level.getShade(mcDir, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
case DISABLED:
|
||||
return 1.0F;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasSinglePlayerServer() { return MINECRAFT.hasSingleplayerServer(); }
|
||||
@Override
|
||||
public boolean clientConnectedToDedicatedServer()
|
||||
{
|
||||
return MINECRAFT.getCurrentServer() != null
|
||||
&& !this.hasSinglePlayerServer();
|
||||
}
|
||||
public boolean clientConnectedToDedicatedServer() { return MINECRAFT.getCurrentServer() != null && !this.hasSinglePlayerServer(); }
|
||||
@Override
|
||||
public boolean connectedToReplay()
|
||||
{
|
||||
return MINECRAFT.getCurrentServer() == null
|
||||
&& !this.hasSinglePlayerServer() ;
|
||||
}
|
||||
public boolean connectedToReplay() { return !MINECRAFT.hasSingleplayerServer() && MINECRAFT.getCurrentServer() == null; }
|
||||
|
||||
@Override
|
||||
public String getCurrentServerName()
|
||||
@@ -104,6 +167,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
return (server != null) ? server.name : "NULL";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentServerIp()
|
||||
{
|
||||
@@ -117,6 +181,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
return (server != null) ? server.ip : "NA";
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentServerVersion()
|
||||
{
|
||||
@@ -124,53 +189,36 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
return (server != null) ? server.version.getString() : "UNKOWN";
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// player handling //
|
||||
//=================//
|
||||
//=============//
|
||||
// Simple gets //
|
||||
//=============//
|
||||
|
||||
public LocalPlayer getPlayer() { return MINECRAFT.player; }
|
||||
|
||||
@Override
|
||||
public boolean playerExists() { return MINECRAFT.player != null; }
|
||||
|
||||
@Override
|
||||
public UUID getPlayerUUID() { return this.getPlayer().getUUID(); }
|
||||
|
||||
@Override
|
||||
public DhBlockPos getPlayerBlockPos()
|
||||
{
|
||||
LocalPlayer player = this.getPlayer();
|
||||
if (player == null)
|
||||
{
|
||||
return new DhBlockPos(0, 0, 0);
|
||||
}
|
||||
|
||||
BlockPos playerPos = player.blockPosition();
|
||||
BlockPos playerPos = this.getPlayer().blockPosition();
|
||||
return new DhBlockPos(playerPos.getX(), playerPos.getY(), playerPos.getZ());
|
||||
}
|
||||
|
||||
@Override
|
||||
public DhChunkPos getPlayerChunkPos()
|
||||
{
|
||||
LocalPlayer player = this.getPlayer();
|
||||
if (player == null)
|
||||
{
|
||||
return new DhChunkPos(0, 0);
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_17_1
|
||||
ChunkPos playerPos = new ChunkPos(player.blockPosition());
|
||||
ChunkPos playerPos = new ChunkPos(this.getPlayer().blockPosition());
|
||||
#else
|
||||
ChunkPos playerPos = player.chunkPosition();
|
||||
ChunkPos playerPos = this.getPlayer().chunkPosition();
|
||||
#endif
|
||||
return new DhChunkPos(playerPos.x, playerPos.z);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// level handling //
|
||||
//================//
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public IClientLevelWrapper getWrappedClientLevel() { return this.getWrappedClientLevel(false); }
|
||||
@@ -188,11 +236,37 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
return ClientLevelWrapper.getWrapper(level, bypassLevelKeyManager);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IProfilerWrapper getProfiler()
|
||||
{
|
||||
if (this.profilerWrapper == null)
|
||||
{
|
||||
this.profilerWrapper = new ProfilerWrapper(MINECRAFT.getProfiler());
|
||||
}
|
||||
else if (MINECRAFT.getProfiler() != this.profilerWrapper.profiler)
|
||||
{
|
||||
this.profilerWrapper.profiler = MINECRAFT.getProfiler();
|
||||
}
|
||||
|
||||
return this.profilerWrapper;
|
||||
}
|
||||
|
||||
/** Returns all worlds available to the server */
|
||||
@Override
|
||||
public ArrayList<ILevelWrapper> getAllServerWorlds()
|
||||
{
|
||||
ArrayList<ILevelWrapper> worlds = new ArrayList<ILevelWrapper>();
|
||||
|
||||
Iterable<ServerLevel> serverWorlds = MINECRAFT.getSingleplayerServer().getAllLevels();
|
||||
for (ServerLevel world : serverWorlds)
|
||||
{
|
||||
worlds.add(ServerLevelWrapper.getWrapper(world));
|
||||
}
|
||||
|
||||
return worlds;
|
||||
}
|
||||
|
||||
|
||||
//===========//
|
||||
// messaging //
|
||||
//===========//
|
||||
|
||||
@Override
|
||||
public void sendChatMessage(String string)
|
||||
@@ -202,115 +276,36 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
player.sendMessage(new TextComponent(string), getPlayer().getUUID());
|
||||
#elif MC_VER < MC_1_21_9
|
||||
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false);
|
||||
#else
|
||||
|
||||
GLProxy.queueRunningOnRenderThread(() ->
|
||||
{
|
||||
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false);
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendOverlayMessage(String string)
|
||||
{
|
||||
LocalPlayer player = this.getPlayer();
|
||||
if (player == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
player.displayClientMessage(new TextComponent(string), /*isOverlay*/true);
|
||||
#else
|
||||
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/true);
|
||||
player.sendSystemMessage(net.minecraft.network.chat.Component.translatable(string));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==========================//
|
||||
// vanilla option overrides //
|
||||
//==========================//
|
||||
|
||||
public void disableVanillaClouds()
|
||||
{
|
||||
#if MC_VER <= MC_1_18_2
|
||||
MINECRAFT.options.renderClouds = CloudStatus.OFF;
|
||||
#else
|
||||
MINECRAFT.options.cloudStatus().set(CloudStatus.OFF);
|
||||
#endif
|
||||
}
|
||||
|
||||
public void disableVanillaChunkFadeIn()
|
||||
{
|
||||
#if MC_VER <= MC_1_21_10
|
||||
// chunk fade in was added MC 1.21.11
|
||||
#else
|
||||
MINECRAFT.options.chunkSectionFadeInTime().set(0.0);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//======//
|
||||
// misc //
|
||||
//======//
|
||||
|
||||
@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);
|
||||
}
|
||||
else if (profiler != this.profilerWrapper.profiler)
|
||||
{
|
||||
this.profilerWrapper.profiler = profiler;
|
||||
}
|
||||
|
||||
return this.profilerWrapper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Crashes Minecraft, displaying the given errorMessage <br> <br>
|
||||
* In the following format: <br>
|
||||
*
|
||||
* The game crashed whilst <strong>errorMessage</strong> <br>
|
||||
* Error: <strong>ExceptionClass: exceptionErrorMessage</strong> <br>
|
||||
* Exit Code: -1 <br>
|
||||
*/
|
||||
@Override
|
||||
public void crashMinecraft(String errorMessage, Throwable exception)
|
||||
{
|
||||
LOGGER.fatal(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...", exception);
|
||||
LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...", exception);
|
||||
CrashReport report = new CrashReport(errorMessage, exception);
|
||||
#if MC_VER < MC_1_20_4
|
||||
Minecraft.crash(report);
|
||||
#else
|
||||
MINECRAFT.delayCrash(report);
|
||||
Minecraft.getInstance().delayCrash(report);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// mod support //
|
||||
//=============//
|
||||
|
||||
@Override
|
||||
public Object getOptionsObject() { return MINECRAFT.options; }
|
||||
|
||||
|
||||
|
||||
//========//
|
||||
// shared //
|
||||
//========//
|
||||
|
||||
@Override
|
||||
public boolean isDedicatedServer() { return false; }
|
||||
|
||||
@@ -318,19 +313,9 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
||||
public File getInstallationDirectory() { return MINECRAFT.gameDirectory; }
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
||||
public void executeOnRenderThread(Runnable runnable) { MINECRAFT.execute(runnable); }
|
||||
|
||||
@Override
|
||||
public boolean isWorldNew() { throw new UnsupportedOperationException("Not Implemented"); }
|
||||
|
||||
}
|
||||
|
||||
-252
@@ -1,252 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.minecraft;
|
||||
|
||||
#if MC_VER < MC_1_21_5
|
||||
import com.mojang.blaze3d.platform.GlStateManager;
|
||||
#else
|
||||
import com.mojang.blaze3d.opengl.GlStateManager;
|
||||
#endif
|
||||
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
|
||||
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
|
||||
/**
|
||||
* <b>Why does DH often call GL methods twice? </b><br>
|
||||
* Once using the base {@link GL32} function and a second time using
|
||||
* Minecraft's {@link GlStateManager}?<br><br>
|
||||
*
|
||||
* <b>Answer: </b><br>
|
||||
* Compatibility and robustness<br>
|
||||
* In general all MC rendering should go through MC's {@link GlStateManager},
|
||||
* however that isn't always the case.
|
||||
* So, to prevent issues if a mod (or MC itself) calls a direct GL function
|
||||
* instead of the {@link GlStateManager} wrapper, we need to be sure about what the actual
|
||||
* set value is (whether setting or getting) and that MC knows what DH has done.
|
||||
* This way whether a mod (or MC) is using the {@link GlStateManager} or direct GL calls,
|
||||
* they should always have the correct value for anything DH has modified.
|
||||
* <br><br>
|
||||
* This may slow down some low end GPUs that are driver limited,
|
||||
* however James would rather have slow correct rendering vs fast broken rendering.
|
||||
*/
|
||||
public class MinecraftGLWrapper implements IMinecraftGLWrapper
|
||||
{
|
||||
public static final MinecraftGLWrapper INSTANCE = new MinecraftGLWrapper();
|
||||
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
|
||||
|
||||
|
||||
/*
|
||||
private static final StencilState STENCIL;
|
||||
*/
|
||||
|
||||
|
||||
// scissor //
|
||||
|
||||
/** @see GL32#GL_SCISSOR_TEST */
|
||||
@Override
|
||||
public void enableScissorTest()
|
||||
{
|
||||
GL32.glEnable(GL32.GL_SCISSOR_TEST);
|
||||
GlStateManager._enableScissorTest();
|
||||
}
|
||||
/** @see GL32#GL_SCISSOR_TEST */
|
||||
@Override
|
||||
public void disableScissorTest()
|
||||
{
|
||||
GL32.glDisable(GL32.GL_SCISSOR_TEST);
|
||||
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()
|
||||
{
|
||||
GL32.glEnable(GL32.GL_DEPTH_TEST);
|
||||
GlStateManager._enableDepthTest();
|
||||
}
|
||||
/** @see GL32#GL_DEPTH_TEST */
|
||||
@Override
|
||||
public void disableDepthTest()
|
||||
{
|
||||
GL32.glDisable(GL32.GL_DEPTH_TEST);
|
||||
GlStateManager._disableDepthTest();
|
||||
}
|
||||
|
||||
/** @see GL32#glDepthFunc(int) */
|
||||
@Override
|
||||
public void glDepthFunc(int func)
|
||||
{
|
||||
GL32.glDepthFunc(func);
|
||||
GlStateManager._depthFunc(func);
|
||||
}
|
||||
|
||||
/** @see GL32#glDepthMask(boolean) */
|
||||
@Override
|
||||
public void enableDepthMask()
|
||||
{
|
||||
GL32.glDepthMask(true);
|
||||
GlStateManager._depthMask(true);
|
||||
}
|
||||
/** @see GL32#glDepthMask(boolean) */
|
||||
@Override
|
||||
public void disableDepthMask()
|
||||
{
|
||||
GL32.glDepthMask(false);
|
||||
GlStateManager._depthMask(false);
|
||||
}
|
||||
|
||||
|
||||
// blending //
|
||||
|
||||
/** @see GL32#GL_BLEND */
|
||||
@Override
|
||||
public void enableBlend()
|
||||
{
|
||||
GL32.glEnable(GL32.GL_BLEND);
|
||||
GlStateManager._enableBlend();
|
||||
}
|
||||
/** @see GL32#GL_BLEND */
|
||||
@Override
|
||||
public void disableBlend()
|
||||
{
|
||||
GL32.glDisable(GL32.GL_BLEND);
|
||||
GlStateManager._disableBlend();
|
||||
}
|
||||
|
||||
/** @see GL32#glBlendFunc */
|
||||
@Override
|
||||
public void glBlendFunc(int sfactor, int dfactor)
|
||||
{
|
||||
GL32.glBlendFunc(sfactor, dfactor);
|
||||
|
||||
#if MC_VER < MC_1_21_5
|
||||
GlStateManager._blendFunc(sfactor, dfactor);
|
||||
#endif
|
||||
}
|
||||
/** @see GL32#glBlendFuncSeparate */
|
||||
@Override
|
||||
public void glBlendFuncSeparate(int sfactorRGB, int dfactorRGB, int sfactorAlpha, int dfactorAlpha)
|
||||
{
|
||||
GL32.glBlendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
|
||||
GlStateManager._blendFuncSeparate(sfactorRGB, dfactorRGB, sfactorAlpha, dfactorAlpha);
|
||||
}
|
||||
|
||||
|
||||
// frame buffers //
|
||||
|
||||
/** @see GL32#glBindFramebuffer */
|
||||
@Override
|
||||
public void glBindFramebuffer(int target, int framebuffer)
|
||||
{
|
||||
GL32.glBindFramebuffer(target, 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)
|
||||
{
|
||||
GL32.glDeleteBuffers(buffer);
|
||||
|
||||
// MC's implementation has a bug where it will throw:
|
||||
// GL_INVALID_OPERATION in glBufferData(immutable)
|
||||
// when attempting to delete Storage Buffers
|
||||
// So we need to manually delete the buffers ourselves
|
||||
//GlStateManager._glDeleteBuffers(buffer);
|
||||
}
|
||||
|
||||
|
||||
// culling //
|
||||
|
||||
/** @see GL32#GL_CULL_FACE */
|
||||
@Override
|
||||
public void enableFaceCulling()
|
||||
{
|
||||
GL32.glEnable(GL32.GL_CULL_FACE);
|
||||
GlStateManager._enableCull();
|
||||
}
|
||||
/** @see GL32#GL_CULL_FACE */
|
||||
@Override
|
||||
public void disableFaceCulling()
|
||||
{
|
||||
GL32.glDisable(GL32.GL_CULL_FACE);
|
||||
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)
|
||||
{
|
||||
GL32.glActiveTexture(textureId);
|
||||
GlStateManager._activeTexture(textureId);
|
||||
}
|
||||
@Override
|
||||
public int getActiveTexture() { return GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D); }
|
||||
|
||||
/**
|
||||
* Always binds to {@link GL32#GL_TEXTURE_2D}
|
||||
* @see GL32#glBindTexture(int, int)
|
||||
*/
|
||||
@Override
|
||||
public void glBindTexture(int texture)
|
||||
{
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, texture);
|
||||
GlStateManager._bindTexture(texture);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
+77
-303
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -25,23 +25,16 @@ import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.mojang.blaze3d.pipeline.RenderTarget;
|
||||
import com.mojang.blaze3d.platform.NativeImage;
|
||||
import com.seibel.distanthorizons.api.enums.config.EDhApiLodShading;
|
||||
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
|
||||
import com.seibel.distanthorizons.common.wrappers.WrapperFactory;
|
||||
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
||||
|
||||
import com.seibel.distanthorizons.core.enums.EDhDirection;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
|
||||
|
||||
#if MC_VER < MC_1_17_1
|
||||
#elif MC_VER < MC_1_21_6
|
||||
#if MC_VER >= MC_1_17_1
|
||||
import net.minecraft.client.renderer.FogRenderer;
|
||||
import com.mojang.blaze3d.systems.RenderSystem;
|
||||
#else
|
||||
import net.minecraft.client.renderer.fog.FogRenderer;
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_19_4
|
||||
@@ -49,6 +42,9 @@ import org.joml.Matrix4f;
|
||||
import org.joml.Vector3f;
|
||||
#else
|
||||
#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;
|
||||
@@ -56,19 +52,13 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWra
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
|
||||
import com.seibel.distanthorizons.core.util.math.Vec3d;
|
||||
import com.seibel.distanthorizons.core.util.math.Vec3f;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
|
||||
|
||||
import net.minecraft.client.Camera;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.joml.Vector4f;
|
||||
|
||||
#if MC_VER < MC_1_17_1
|
||||
import net.minecraft.tags.FluidTags;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
@@ -77,27 +67,27 @@ import org.lwjgl.opengl.GL15;
|
||||
#else
|
||||
import net.minecraft.world.level.material.FogType;
|
||||
#endif
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
#if MC_VER >= MC_1_21_5
|
||||
import com.mojang.blaze3d.opengl.GlTexture;
|
||||
#else
|
||||
#endif
|
||||
|
||||
#if MC_VER <= MC_1_21_10
|
||||
#else
|
||||
import net.minecraft.world.attribute.EnvironmentAttributes;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* A singleton that contains everything
|
||||
* related to rendering in Minecraft.
|
||||
*
|
||||
* @author James Seibel
|
||||
* @version 12-12-2021
|
||||
*/
|
||||
//@Environment(EnvType.CLIENT)
|
||||
public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
{
|
||||
public static final MinecraftRenderWrapper INSTANCE = new MinecraftRenderWrapper();
|
||||
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
|
||||
private static final Minecraft MC = Minecraft.getInstance();
|
||||
private static final IWrapperFactory FACTORY = WrapperFactory.INSTANCE;
|
||||
|
||||
private static final IOptifineAccessor OPTIFINE_ACCESSOR = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class);
|
||||
|
||||
/**
|
||||
* In the case of immersive portals multiple levels may be active at once, causing conflicting lightmaps. <br>
|
||||
@@ -111,66 +101,31 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
*/
|
||||
public int finalLevelFrameBufferId = -1;
|
||||
|
||||
public boolean colorTextureCastFailLogged = false;
|
||||
public boolean depthTextureCastFailLogged = false;
|
||||
|
||||
#if MC_VER < MC_1_21_6
|
||||
#else
|
||||
private static FogRenderer mcFogRenderer = null;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// methods //
|
||||
//=========//
|
||||
|
||||
@Override
|
||||
public Vec3f getLookAtVector()
|
||||
{
|
||||
#if MC_VER <= MC_1_21_10
|
||||
Camera camera = MC.gameRenderer.getMainCamera();
|
||||
return new Vec3f(camera.getLookVector().x(), camera.getLookVector().y(), camera.getLookVector().z());
|
||||
#else
|
||||
Camera camera = MC.gameRenderer.getMainCamera();
|
||||
return new Vec3f(camera.forwardVector().x(), camera.forwardVector().y(), camera.forwardVector().z());
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Unless you really need to know if the player is blind,
|
||||
* use {@link MinecraftRenderWrapper#isFogStateSpecial()} or {@link IMinecraftRenderWrapper#isFogStateSpecial()} instead
|
||||
*/
|
||||
@Override
|
||||
/** Unless you really need to know if the player is blind, use {@link MinecraftRenderWrapper#isFogStateSpecial()}/{@link IMinecraftRenderWrapper#isFogStateSpecial()} instead */
|
||||
public boolean playerHasBlindingEffect()
|
||||
{
|
||||
if (MC.player == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (MC.player.getActiveEffectsMap() == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return MC.player.getActiveEffectsMap().get(MobEffects.BLINDNESS) != null
|
||||
return MC.player.getActiveEffectsMap().get(MobEffects.BLINDNESS) != null
|
||||
#if MC_VER >= MC_1_19_2
|
||||
|| MC.player.getActiveEffectsMap().get(MobEffects.DARKNESS) != null // Deep dark effect
|
||||
|| MC.player.getActiveEffectsMap().get(MobEffects.DARKNESS) != null // Deep dark effect
|
||||
#endif
|
||||
;
|
||||
}
|
||||
;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3d getCameraExactPosition()
|
||||
{
|
||||
Camera camera = MC.gameRenderer.getMainCamera();
|
||||
#if MC_VER <= MC_1_21_10
|
||||
Vec3 projectedView = camera.getPosition();
|
||||
#else
|
||||
Vec3 projectedView = camera.position();
|
||||
#endif
|
||||
|
||||
return new Vec3d(projectedView.x, projectedView.y, projectedView.z);
|
||||
}
|
||||
@@ -181,80 +136,18 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
#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
|
||||
);
|
||||
#elif MC_VER < MC_1_21_6
|
||||
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
|
||||
);
|
||||
#elif MC_VER <= MC_1_21_10
|
||||
if (mcFogRenderer == null)
|
||||
{
|
||||
mcFogRenderer = new FogRenderer();
|
||||
}
|
||||
|
||||
if (MC.level == null)
|
||||
{
|
||||
// shouldn't happen, but just in case
|
||||
return Color.white;
|
||||
}
|
||||
|
||||
boolean isFoggy =
|
||||
MC.level.effects().isFoggyAt(
|
||||
MC.gameRenderer.getMainCamera().getBlockPosition().getX(),
|
||||
MC.gameRenderer.getMainCamera().getBlockPosition().getZ())
|
||||
|| MC.gui.getBossOverlay().shouldCreateWorldFog();
|
||||
Vector4f colorValues = mcFogRenderer.setupFog(MC.gameRenderer.getMainCamera(), MC.options.getEffectiveRenderDistance(), isFoggy, MC.deltaTracker, MC.gameRenderer.getDarkenWorldAmount(MC.deltaTracker.getGameTimeDeltaPartialTick(true)), MC.level);
|
||||
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
|
||||
);
|
||||
#else
|
||||
|
||||
if (mcFogRenderer == null)
|
||||
{
|
||||
mcFogRenderer = new FogRenderer();
|
||||
}
|
||||
|
||||
if (MC.level == null)
|
||||
{
|
||||
// shouldn't happen, but just in case
|
||||
return Color.white;
|
||||
}
|
||||
|
||||
Vector4f colorValues = mcFogRenderer.setupFog(
|
||||
MC.gameRenderer.getMainCamera(),
|
||||
MC.options.getEffectiveRenderDistance(),
|
||||
MC.deltaTracker,
|
||||
MC.gameRenderer.getDarkenWorldAmount(MC.deltaTracker.getGameTimeDeltaPartialTick(true)),
|
||||
MC.level);
|
||||
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()
|
||||
|
||||
@Override
|
||||
public Color getSkyColor()
|
||||
@@ -264,27 +157,16 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
float frameTime;
|
||||
#if MC_VER < MC_1_21_1
|
||||
frameTime = MC.getFrameTime();
|
||||
#elif MC_VER < MC_1_21_3
|
||||
frameTime = MC.getTimer().getRealtimeDeltaTicks();
|
||||
#elif MC_VER <= MC_1_21_10
|
||||
frameTime = MC.deltaTracker.getGameTimeDeltaTicks();
|
||||
#else
|
||||
frameTime = 0f; // unused
|
||||
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);
|
||||
#elif MC_VER <= MC_1_21_10
|
||||
int argbColorInt = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);
|
||||
return ColorUtil.toColorObjARGB(argbColorInt);
|
||||
#else
|
||||
int argbColor = MC.level.environmentAttributes().getValue(EnvironmentAttributes.SKY_COLOR, BlockPos.ZERO);
|
||||
return new Color(ColorUtil.getRed(argbColor), ColorUtil.getGreen(argbColor), ColorUtil.getBlue(argbColor), 255 /* ignore alpha since DH clouds don't render correctly with transparency */);
|
||||
Vec3 colorValues = MC.level.getSkyColor(MC.gameRenderer.getMainCamera().getPosition(), frameTime);
|
||||
#endif
|
||||
return new Color((float) colorValues.x, (float) colorValues.y, (float) colorValues.z);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -293,7 +175,10 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getFov(float partialTicks) { return MC.gameRenderer.getFov(MC.gameRenderer.getMainCamera(), partialTicks, true); }
|
||||
public double getFov(float partialTicks)
|
||||
{
|
||||
return MC.gameRenderer.getFov(MC.gameRenderer.getMainCamera(), partialTicks, true);
|
||||
}
|
||||
|
||||
/** Measured in chunks */
|
||||
@Override
|
||||
@@ -307,30 +192,45 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
#endif
|
||||
}
|
||||
|
||||
protected RenderTarget getRenderTarget() { return MC.getMainRenderTarget(); }
|
||||
|
||||
@Override
|
||||
public boolean mcRendersToFrameBuffer()
|
||||
public int getScreenWidth()
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
// alternate ways of getting the window's resolution,
|
||||
// using one of these methods may fix the optifine render resolution bug
|
||||
// TODO: test these once we can run with Optifine again
|
||||
// int[] heightArray = new int[1];
|
||||
// int[] widthArray = new int[1];
|
||||
//
|
||||
// long window = GLProxy.getInstance().minecraftGlContext;
|
||||
// GLFW.glfwGetWindowSize(window, widthArray, heightArray); // option 1
|
||||
// GLFW.glfwGetFramebufferSize(window, widthArray, heightArray); // option 2
|
||||
|
||||
|
||||
|
||||
int width = MC.getWindow().getWidth();
|
||||
if (OPTIFINE_ACCESSOR != null)
|
||||
{
|
||||
// TODO remove comment after testing:
|
||||
// this should fix the issue where different optifine render resolutions screw up the LOD rendering
|
||||
width *= OPTIFINE_ACCESSOR.getRenderResolutionMultiplier();
|
||||
}
|
||||
return width;
|
||||
}
|
||||
@Override
|
||||
public int getScreenHeight()
|
||||
{
|
||||
int height = MC.getWindow().getHeight();
|
||||
if (OPTIFINE_ACCESSOR != null)
|
||||
{
|
||||
height *= OPTIFINE_ACCESSOR.getRenderResolutionMultiplier();
|
||||
}
|
||||
return height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean runningLegacyOpenGL()
|
||||
{
|
||||
#if MC_VER <= MC_1_16_5
|
||||
return true;
|
||||
#else
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
private RenderTarget getRenderTarget() { return MC.getMainRenderTarget(); }
|
||||
|
||||
@Override
|
||||
public int getTargetFramebuffer()
|
||||
public int getTargetFrameBuffer()
|
||||
{
|
||||
// used so we can access the framebuffer shaders end up rendering to
|
||||
if (AbstractOptifineAccessor.optifinePresent())
|
||||
@@ -338,100 +238,29 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
|
||||
return this.finalLevelFrameBufferId;
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_21_5
|
||||
return this.getRenderTarget().frameBufferId;
|
||||
#else
|
||||
// MC renders to a texture and then directly to the default FBO now
|
||||
// we need to draw to their texture instead of the FBO
|
||||
return 0; // 0 is the ID for the default frame buffer
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearTargetFrameBuffer() { this.finalLevelFrameBufferId = -1; }
|
||||
|
||||
@Override
|
||||
public int getDepthTextureId()
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
return this.getRenderTarget().getDepthTextureId();
|
||||
#else
|
||||
try
|
||||
{
|
||||
GlTexture glTexture = (GlTexture) this.getRenderTarget().getDepthTexture();
|
||||
if (glTexture == null)
|
||||
{
|
||||
// shouldn't happen, but just in case
|
||||
return 0;
|
||||
}
|
||||
|
||||
return glTexture.glId();
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// only log this error once per session
|
||||
if (!this.depthTextureCastFailLogged)
|
||||
{
|
||||
this.depthTextureCastFailLogged = true;
|
||||
LOGGER.error("Unable to cast render Target depth texture to GlTexture. MC or a rendering mod may have changed the object type.", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
public int getDepthTextureId() { return this.getRenderTarget().getDepthTextureId(); }
|
||||
|
||||
@Override
|
||||
public int getColorTextureId()
|
||||
public int getTargetFrameBufferViewportWidth()
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
return this.getRenderTarget().getColorTextureId();
|
||||
#else
|
||||
try
|
||||
{
|
||||
GlTexture glTexture = (GlTexture) this.getRenderTarget().getColorTexture();
|
||||
if (glTexture == null)
|
||||
{
|
||||
// shouldn't happen, but just in case
|
||||
return 0;
|
||||
}
|
||||
|
||||
return glTexture.glId();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
// only log this error once per session
|
||||
if (!this.colorTextureCastFailLogged)
|
||||
{
|
||||
this.colorTextureCastFailLogged = true;
|
||||
LOGGER.error("Unable to cast render Target color texture to GlTexture. MC or a rendering mod may have changed the object type.", e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
return getRenderTarget().viewWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTargetFramebufferViewportWidth()
|
||||
public int getTargetFrameBufferViewportHeight()
|
||||
{
|
||||
#if MC_VER < MC_1_21_9
|
||||
return this.getRenderTarget().viewWidth;
|
||||
#else
|
||||
return this.getRenderTarget().width;
|
||||
#endif
|
||||
return getRenderTarget().viewHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTargetFramebufferViewportHeight()
|
||||
{
|
||||
#if MC_VER < MC_1_21_9
|
||||
return this.getRenderTarget().viewHeight;
|
||||
#else
|
||||
return this.getRenderTarget().height;
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public ILightMapWrapper getLightmapWrapper(@NotNull ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); }
|
||||
public ILightMapWrapper getLightmapWrapper(ILevelWrapper level) { return this.lightmapByDimensionType.get(level.getDimensionType()); }
|
||||
|
||||
@Override
|
||||
public boolean isFogStateSpecial()
|
||||
@@ -450,10 +279,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
|
||||
@@ -464,56 +289,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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getShade(EDhDirection lodDirection)
|
||||
{
|
||||
EDhApiLodShading lodShading = Config.Client.Advanced.Graphics.Quality.lodShading.get();
|
||||
switch (lodShading)
|
||||
{
|
||||
default:
|
||||
case AUTO:
|
||||
if (MC.level != null)
|
||||
{
|
||||
Direction mcDir = McObjectConverter.Convert(lodDirection);
|
||||
return MC.level.getShade(mcDir, true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return 0.0f;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
case DISABLED:
|
||||
return 1.0F;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+4
-16
@@ -2,20 +2,17 @@ package com.seibel.distanthorizons.common.wrappers.minecraft;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
|
||||
import net.minecraft.server.dedicated.DedicatedServer;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
//@Environment(EnvType.SERVER)
|
||||
public class MinecraftServerWrapper implements IMinecraftSharedWrapper
|
||||
{
|
||||
public static final MinecraftServerWrapper INSTANCE = new MinecraftServerWrapper();
|
||||
|
||||
/** set during server startup */
|
||||
@Nullable
|
||||
public DedicatedServer dedicatedServer = null;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
@@ -36,7 +33,7 @@ public class MinecraftServerWrapper implements IMinecraftSharedWrapper
|
||||
{
|
||||
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 completed initialization!");
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_21_1
|
||||
@@ -47,16 +44,7 @@ public class MinecraftServerWrapper implements IMinecraftSharedWrapper
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getPlayerCount()
|
||||
{
|
||||
if (this.dedicatedServer == null)
|
||||
{
|
||||
throw new IllegalStateException("Trying to get player count before dedicated server completed initialization!");
|
||||
}
|
||||
|
||||
return this.dedicatedServer.getPlayerCount();
|
||||
}
|
||||
|
||||
|
||||
public boolean isWorldNew()
|
||||
{ return this.dedicatedServer.getWorldData().overworldData().isInitialized(); }
|
||||
|
||||
}
|
||||
|
||||
+17
-5
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+12
-46
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -20,24 +20,16 @@
|
||||
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.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import org.lwjgl.opengl.GL32;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
public class LightMapWrapper implements ILightMapWrapper
|
||||
{
|
||||
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
|
||||
private int textureId = 0;
|
||||
|
||||
|
||||
|
||||
//==============//
|
||||
// constructors //
|
||||
//==============//
|
||||
@@ -46,67 +38,41 @@ public class LightMapWrapper implements ILightMapWrapper
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// lightmap syncing //
|
||||
//==================//
|
||||
//=========//
|
||||
// methods //
|
||||
//=========//
|
||||
|
||||
public void uploadLightmap(NativeImage image)
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
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);
|
||||
|
||||
// getActiveTexture() may return textures that aren't valid and attempting to bind them will
|
||||
// throw a GL error in MC 1.21.1
|
||||
if (GL32.glIsTexture(currentTexture))
|
||||
{
|
||||
GLMC.glBindTexture(currentTexture);
|
||||
}
|
||||
#else
|
||||
throw new UnsupportedOperationException("setLightmapId should be used for MC versions after 1.21.5"); // TODO that MC version number is wrong, when did we actually start using setLightmapId()?
|
||||
#endif
|
||||
GL32.glBindTexture(GL32.GL_TEXTURE_2D, currentBind);
|
||||
}
|
||||
private void createLightmap(NativeImage image)
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
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);
|
||||
#else
|
||||
throw new UnsupportedOperationException("setLightmapId should be used for MC versions after 1.21.5"); // TODO that MC version number is wrong, when did we actually start using setLightmapId()?
|
||||
#endif
|
||||
}
|
||||
|
||||
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); }
|
||||
|
||||
}
|
||||
|
||||
|
||||
+14
-3
@@ -54,10 +54,8 @@ public class ServerPlayerWrapper implements IServerPlayerWrapper
|
||||
{
|
||||
#if MC_VER < MC_1_20_1
|
||||
level = this.getServerPlayer().getLevel();
|
||||
#elif MC_VER < MC_1_21_6
|
||||
level = this.getServerPlayer().serverLevel();
|
||||
#else
|
||||
level = this.getServerPlayer().level();
|
||||
level = this.getServerPlayer().serverLevel();
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -71,6 +69,19 @@ public class ServerPlayerWrapper implements IServerPlayerWrapper
|
||||
return new Vec3d(position.x, position.y, position.z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewDistance() { return this.getServerPlayer().server.getPlayerList().getViewDistance(); }
|
||||
|
||||
@Override
|
||||
public SocketAddress getRemoteAddress()
|
||||
{
|
||||
#if MC_VER >= MC_1_19_4
|
||||
return this.getServerPlayer().connection.getRemoteAddress();
|
||||
#else // < 1.19.4
|
||||
return this.getServerPlayer().connection.connection.getRemoteAddress();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
|
||||
+83
-155
@@ -2,11 +2,11 @@ package com.seibel.distanthorizons.common.wrappers.world;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
|
||||
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
|
||||
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.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.level.*;
|
||||
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
|
||||
@@ -21,23 +21,18 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWra
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.multiplayer.ClientLevel;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
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.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
@@ -45,40 +40,21 @@ import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
#else
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
#endif
|
||||
|
||||
#if MC_VER <= MC_1_21_10
|
||||
#else
|
||||
import net.minecraft.world.attribute.EnvironmentAttributes;
|
||||
#endif
|
||||
|
||||
|
||||
public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
{
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
/**
|
||||
* weak references are to prevent rare issues
|
||||
* where, upon world closure, some levels aren't shutdown/removed properly
|
||||
* and/or for servers were the level object isn't consistent
|
||||
*/
|
||||
private static final Map<ClientLevel, WeakReference<ClientLevelWrapper>> LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL = Collections.synchronizedMap(new WeakHashMap<>());
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger(ClientLevelWrapper.class.getSimpleName());
|
||||
private static final ConcurrentHashMap<ClientLevel, ClientLevelWrapper> LEVEL_WRAPPER_BY_CLIENT_LEVEL = new ConcurrentHashMap<>(); // TODO can leak
|
||||
private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
|
||||
|
||||
private static final Minecraft MINECRAFT = Minecraft.getInstance();
|
||||
|
||||
private final ClientLevel level;
|
||||
private final ConcurrentHashMap<BlockState, ClientBlockStateColorCache> blockColorCacheByBlockState = new ConcurrentHashMap<>();
|
||||
|
||||
/** cached method reference to reduce GC overhead */
|
||||
private final Function<BlockState, ClientBlockStateColorCache> createCachedBlockColorCacheFunc = (blockState) -> new ClientBlockStateColorCache(blockState, this);
|
||||
|
||||
private final ConcurrentHashMap<BlockState, ClientBlockStateColorCache> blockCache = new ConcurrentHashMap<>();
|
||||
|
||||
private BlockStateWrapper dirtBlockWrapper;
|
||||
private IDhLevel dhLevel;
|
||||
private BiomeWrapper plainsBiomeWrapper;
|
||||
@Deprecated // TODO circular references are bad
|
||||
private IDhLevel parentDhLevel;
|
||||
|
||||
|
||||
|
||||
@@ -90,33 +66,10 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// instance methods //
|
||||
//==================//
|
||||
//===============//
|
||||
// wrapper logic //
|
||||
//===============//
|
||||
|
||||
/**
|
||||
* can be used when speed is important and the same level is likely to be passed in,
|
||||
* IE rendering.
|
||||
*/
|
||||
@Nullable
|
||||
public static IClientLevelWrapper getWrapperIfDifferent(@Nullable IClientLevelWrapper levelWrapper, @NotNull ClientLevel level) // TODO handle null level
|
||||
{
|
||||
if (KEYED_CLIENT_LEVEL_MANAGER.isEnabled() && KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel() != levelWrapper)
|
||||
{
|
||||
return getWrapper(level);
|
||||
}
|
||||
|
||||
ClientLevelWrapper clientLevelWrapper = (ClientLevelWrapper)levelWrapper;
|
||||
if (clientLevelWrapper == null
|
||||
|| clientLevelWrapper.level != level)
|
||||
{
|
||||
return getWrapper(level);
|
||||
}
|
||||
|
||||
return clientLevelWrapper;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static IClientLevelWrapper getWrapper(@NotNull ClientLevel level) { return getWrapper(level, false); }
|
||||
|
||||
@Nullable
|
||||
@@ -137,31 +90,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
WeakReference<ClientLevelWrapper> levelRef = LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.get(level);
|
||||
if (levelRef != null)
|
||||
{
|
||||
ClientLevelWrapper levelWrapper = levelRef.get();
|
||||
if (levelWrapper != null)
|
||||
{
|
||||
return levelWrapper;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.compute(level, (newLevel, newLevelRef) ->
|
||||
{
|
||||
if (newLevelRef != null)
|
||||
{
|
||||
ClientLevelWrapper oldLevelWrapper = newLevelRef.get();
|
||||
if (oldLevelWrapper != null)
|
||||
{
|
||||
return newLevelRef;
|
||||
}
|
||||
}
|
||||
|
||||
return new WeakReference<>(new ClientLevelWrapper(newLevel));
|
||||
}).get();
|
||||
return LEVEL_WRAPPER_BY_CLIENT_LEVEL.computeIfAbsent(level, ClientLevelWrapper::new);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -170,17 +99,13 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
{
|
||||
try
|
||||
{
|
||||
// this method only makes sense if we are running a single-player server
|
||||
if (MINECRAFT.getSingleplayerServer() == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
Iterable<ServerLevel> serverLevels = MINECRAFT.getSingleplayerServer().getAllLevels();
|
||||
|
||||
// attempt to find the server level with the same dimension type
|
||||
// Note: this assumes only one level per dimension type, multiverse servers may not behave correctly
|
||||
// TODO this assumes only one level per dimension type, the SubDimensionLevelMatcher will need to be added for supporting multiple levels per dimension
|
||||
ServerLevelWrapper foundLevelWrapper = null;
|
||||
|
||||
// TODO: Surely there is a more efficient way to write this code
|
||||
for (ServerLevel serverLevel : serverLevels)
|
||||
{
|
||||
if (serverLevel.dimension() == this.level.dimension())
|
||||
@@ -199,27 +124,20 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//====================//
|
||||
// base level methods //
|
||||
//====================//
|
||||
|
||||
@Override
|
||||
public int getBlockColor(DhBlockPos blockPos, IBiomeWrapper biome, FullDataSourceV2 fullDataSource, IBlockStateWrapper blockWrapper)
|
||||
public int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockWrapper)
|
||||
{
|
||||
ClientBlockStateColorCache blockColorCache = this.blockColorCacheByBlockState.get(((BlockStateWrapper) blockWrapper).blockState);
|
||||
if (blockColorCache == null)
|
||||
{
|
||||
blockColorCache = this.blockColorCacheByBlockState.computeIfAbsent(
|
||||
((BlockStateWrapper) blockWrapper).blockState,
|
||||
this.createCachedBlockColorCacheFunc);
|
||||
}
|
||||
ClientBlockStateColorCache blockColorCache = this.blockCache.computeIfAbsent(
|
||||
((BlockStateWrapper) blockWrapper).blockState,
|
||||
(block) -> new ClientBlockStateColorCache(block, this));
|
||||
|
||||
return blockColorCache.getColor((BiomeWrapper) biome, fullDataSource, blockPos);
|
||||
return blockColorCache.getColor((BiomeWrapper) biome, pos);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getDirtBlockColor()
|
||||
{
|
||||
@@ -237,37 +155,38 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
}
|
||||
}
|
||||
|
||||
return this.getBlockColor(DhBlockPos.ZERO, BiomeWrapper.EMPTY_WRAPPER, null, this.dirtBlockWrapper);
|
||||
return this.getBlockColor(DhBlockPos.ZERO,BiomeWrapper.EMPTY_WRAPPER, this.dirtBlockWrapper);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clearBlockColorCache() { this.blockColorCacheByBlockState.clear(); }
|
||||
public void clearBlockColorCache() { this.blockCache.clear(); }
|
||||
|
||||
@Override
|
||||
public IDimensionTypeWrapper getDimensionType()
|
||||
public IBiomeWrapper getPlainsBiomeWrapper()
|
||||
{
|
||||
#if MC_VER <= MC_1_21_10
|
||||
return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType());
|
||||
#else
|
||||
return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType(), this.getDimensionName());
|
||||
#endif
|
||||
if (this.plainsBiomeWrapper == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
this.plainsBiomeWrapper = (BiomeWrapper) BiomeWrapper.deserialize(BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING, this);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// shouldn't happen, but just in case
|
||||
LOGGER.warn("Unable to get planes biome with resource location ["+BiomeWrapper.PLAINS_RESOURCE_LOCATION_STRING+"] with level ["+this+"].", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
return this.plainsBiomeWrapper;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDimensionName()
|
||||
{
|
||||
#if MC_VER <= MC_1_21_10
|
||||
return this.level.dimension().location().toString();
|
||||
#else
|
||||
return this.level.dimension().identifier().toString();
|
||||
#endif
|
||||
}
|
||||
public IDimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); }
|
||||
|
||||
|
||||
@Override
|
||||
public long getHashedSeed() { return this.level.getBiomeManager().biomeZoomSeed; }
|
||||
|
||||
@Override
|
||||
public String getDhIdentifier() { return this.getHashedSeedEncoded() + "@" + this.getDimensionName(); }
|
||||
public String getDimensionName() { return this.level.dimension().location().toString(); }
|
||||
|
||||
@Override
|
||||
public EDhApiLevelType getLevelType() { return EDhApiLevelType.CLIENT_LEVEL; }
|
||||
@@ -288,35 +207,54 @@ 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()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ChunkAccess chunk = this.level.getChunk(pos.getX(), pos.getZ(), ChunkStatus.EMPTY, false);
|
||||
if (chunk == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ChunkWrapper(chunk, this.level, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasChunkLoaded(int chunkX, int chunkZ)
|
||||
{
|
||||
ChunkSource source = this.level.getChunkSource();
|
||||
return source.hasChunk(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockStateWrapper getBlockState(DhBlockPos pos)
|
||||
{
|
||||
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); }
|
||||
|
||||
@Override
|
||||
public ClientLevel getWrappedMcObject() { return this.level; }
|
||||
|
||||
@Override
|
||||
public void onUnload()
|
||||
{
|
||||
LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.remove(this.level);
|
||||
this.dhLevel = null;
|
||||
LEVEL_WRAPPER_BY_CLIENT_LEVEL.remove(this.level);
|
||||
this.parentDhLevel = null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDhSaveFolder()
|
||||
{
|
||||
if (this.dhLevel == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.dhLevel.getSaveStructure().getSaveFolder(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//===================//
|
||||
@@ -324,34 +262,24 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
//===================//
|
||||
|
||||
@Override
|
||||
public void setDhLevel(IDhLevel dhLevel) { this.dhLevel = dhLevel; }
|
||||
@Override
|
||||
public IDhLevel getDhLevel() { return this.dhLevel; }
|
||||
public void setParentLevel(IDhLevel parentLevel) { this.parentDhLevel = parentLevel; }
|
||||
|
||||
@Override
|
||||
public IDhApiCustomRenderRegister getRenderRegister()
|
||||
{
|
||||
if (this.dhLevel == null)
|
||||
if (this.parentDhLevel == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.dhLevel.getGenericRenderer();
|
||||
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);
|
||||
#elif MC_VER <= MC_1_21_10
|
||||
int argbColor = this.level.getCloudColor(tickDelta);
|
||||
return ColorUtil.toColorObjARGB(argbColor);
|
||||
#else
|
||||
int argbColor = this.level.environmentAttributes().getValue(EnvironmentAttributes.CLOUD_COLOR, BlockPos.ZERO);
|
||||
return new Color(ColorUtil.getRed(argbColor), ColorUtil.getGreen(argbColor), ColorUtil.getBlue(argbColor), 255 /* ignore alpha since DH clouds don't render correctly with transparency */);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -368,7 +296,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
|
||||
return "Wrapped{null}";
|
||||
}
|
||||
|
||||
return "Wrapped{" + this.level.toString() + "@" + this.getDhIdentifier() + "}";
|
||||
return "Wrapped{" + this.level.toString() + "@" + this.getDimensionName() + "}";
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+38
-72
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -26,107 +26,74 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IDimensionTypeWra
|
||||
|
||||
import net.minecraft.world.level.dimension.DimensionType;
|
||||
|
||||
/**
|
||||
* @author James Seibel
|
||||
* @version 2022-9-16
|
||||
*/
|
||||
public class DimensionTypeWrapper implements IDimensionTypeWrapper
|
||||
{
|
||||
private static final ConcurrentMap<String, DimensionTypeWrapper> DIMENSION_WRAPPER_BY_NAME = new ConcurrentHashMap<>();
|
||||
private static final ConcurrentMap<DimensionType, DimensionTypeWrapper> dimensionTypeWrapperMap = new ConcurrentHashMap<>();
|
||||
private final DimensionType dimensionType;
|
||||
|
||||
private final String name;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// Constructor //
|
||||
//=============//
|
||||
|
||||
#if MC_VER <= MC_1_21_10
|
||||
public DimensionTypeWrapper(DimensionType dimensionType)
|
||||
#else
|
||||
public DimensionTypeWrapper(DimensionType dimensionType, String name)
|
||||
#endif
|
||||
{
|
||||
this.dimensionType = dimensionType;
|
||||
|
||||
#if MC_VER <= MC_1_21_10
|
||||
this.name = determineName(dimensionType);
|
||||
#else
|
||||
this.name = name;
|
||||
#endif
|
||||
this.dimensionType = dimensionType;
|
||||
}
|
||||
|
||||
#if MC_VER <= MC_1_21_10
|
||||
public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType)
|
||||
#else
|
||||
public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType, String name)
|
||||
#endif
|
||||
{
|
||||
#if MC_VER <= MC_1_21_10
|
||||
String dimName = determineName(dimensionType);
|
||||
#else
|
||||
String dimName = name;
|
||||
#endif
|
||||
|
||||
// check if the dimension has already been wrapped
|
||||
if (DIMENSION_WRAPPER_BY_NAME.containsKey(dimName)
|
||||
&& DIMENSION_WRAPPER_BY_NAME.get(dimName) != null)
|
||||
//first we check if the biome has already been wrapped
|
||||
if (dimensionTypeWrapperMap.containsKey(dimensionType) && dimensionTypeWrapperMap.get(dimensionType) != null)
|
||||
{
|
||||
return DIMENSION_WRAPPER_BY_NAME.get(dimName);
|
||||
return dimensionTypeWrapperMap.get(dimensionType);
|
||||
}
|
||||
|
||||
|
||||
// create the missing wrapper
|
||||
#if MC_VER <= MC_1_21_10
|
||||
//if it hasn't been created yet, we create it and save it in the map
|
||||
DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType);
|
||||
#else
|
||||
DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType, dimName);
|
||||
#endif
|
||||
dimensionTypeWrapperMap.put(dimensionType, dimensionTypeWrapper);
|
||||
|
||||
DIMENSION_WRAPPER_BY_NAME.put(dimName, dimensionTypeWrapper);
|
||||
//we return the newly created wrapper
|
||||
return dimensionTypeWrapper;
|
||||
}
|
||||
private static String determineName(DimensionType dimensionType)
|
||||
|
||||
public static void clearMap()
|
||||
{
|
||||
dimensionTypeWrapperMap.clear();
|
||||
}
|
||||
|
||||
|
||||
private 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();
|
||||
#elif MC_VER <= MC_1_21_10
|
||||
return dimensionType.effectsLocation().getPath();
|
||||
#else
|
||||
throw new UnsupportedOperationException("As of MC 1.21.11 the dimension type no longer stores it's name and must be determined from the level.");
|
||||
return this.dimensionType.effectsLocation().getPath();
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void clearMap() { DIMENSION_WRAPPER_BY_NAME.clear(); }
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// wrapper methods //
|
||||
//=================//
|
||||
@Override
|
||||
public boolean hasCeiling()
|
||||
{
|
||||
return this.dimensionType.hasCeiling();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() { return this.name; }
|
||||
public boolean hasSkyLight()
|
||||
{
|
||||
return this.dimensionType.hasSkyLight();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasCeiling() { return this.dimensionType.hasCeiling(); }
|
||||
public Object getWrappedMcObject()
|
||||
{
|
||||
return this.dimensionType;
|
||||
}
|
||||
|
||||
// there's definitely a better way of doing this, but it should work well enough for now
|
||||
@Override
|
||||
public boolean hasSkyLight() { return this.dimensionType.hasSkyLight(); }
|
||||
|
||||
@Override
|
||||
public Object getWrappedMcObject() { return this.dimensionType; }
|
||||
|
||||
@Override
|
||||
public boolean isTheEnd() { return this.getName().equalsIgnoreCase("the_end"); }
|
||||
|
||||
@Override
|
||||
public double getCoordinateScale() { return this.dimensionType.coordinateScale(); }
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
public boolean isTheEnd() { return this.getDimensionName().equalsIgnoreCase("the_end"); }
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj)
|
||||
@@ -138,10 +105,9 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
|
||||
else
|
||||
{
|
||||
DimensionTypeWrapper other = (DimensionTypeWrapper) obj;
|
||||
return other.getName().equals(this.getName());
|
||||
return other.getDimensionName().equals(this.getDimensionName());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
+69
-157
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -20,26 +20,24 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.world;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Collections;
|
||||
import java.util.Map;
|
||||
import java.util.WeakHashMap;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
|
||||
import com.seibel.distanthorizons.api.interfaces.render.IDhApiCustomRenderRegister;
|
||||
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.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.level.IDhLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
|
||||
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.world.EWorldEnvironment;
|
||||
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.IServerLevelWrapper;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ChunkSource;
|
||||
|
||||
@@ -49,26 +47,19 @@ import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
/**
|
||||
* @version 2022-9-16
|
||||
*/
|
||||
public class ServerLevelWrapper implements IServerLevelWrapper
|
||||
{
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
/**
|
||||
* weak references are to prevent rare issues
|
||||
* where, upon world closure, some levels aren't shutdown/removed properly
|
||||
*/
|
||||
private static final Map<ServerLevel, WeakReference<ServerLevelWrapper>> LEVEL_WRAPPER_REF_BY_SERVER_LEVEL = Collections.synchronizedMap(new WeakHashMap<>());
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
private static final ConcurrentHashMap<ServerLevel, ServerLevelWrapper> LEVEL_WRAPPER_BY_SERVER_LEVEL = new ConcurrentHashMap<>();
|
||||
|
||||
private final ServerLevel level;
|
||||
private IDhLevel dhLevel;
|
||||
|
||||
/**
|
||||
* this name is cached to prevent issues during shutdown where
|
||||
* the server variables needed may no longer be available.
|
||||
*/
|
||||
private final String KeyedLevelDimensionName;
|
||||
@Deprecated // TODO circular references are bad
|
||||
private IDhLevel parentDhLevel;
|
||||
|
||||
|
||||
|
||||
@@ -76,126 +67,27 @@ public class ServerLevelWrapper implements IServerLevelWrapper
|
||||
// constructors //
|
||||
//==============//
|
||||
|
||||
public static ServerLevelWrapper getWrapper(ServerLevel level)
|
||||
{
|
||||
return LEVEL_WRAPPER_REF_BY_SERVER_LEVEL.compute(level, (newLevel, levelRef) ->
|
||||
{
|
||||
if (levelRef != null)
|
||||
{
|
||||
ServerLevelWrapper oldLevelWrapper = levelRef.get();
|
||||
if (oldLevelWrapper != null)
|
||||
{
|
||||
return levelRef;
|
||||
}
|
||||
}
|
||||
|
||||
return new WeakReference<>(new ServerLevelWrapper(newLevel));
|
||||
}).get();
|
||||
}
|
||||
public static ServerLevelWrapper getWrapper(ServerLevel level) { return LEVEL_WRAPPER_BY_SERVER_LEVEL.computeIfAbsent(level, ServerLevelWrapper::new); }
|
||||
|
||||
public ServerLevelWrapper(ServerLevel level)
|
||||
{
|
||||
public ServerLevelWrapper(ServerLevel level)
|
||||
{
|
||||
this.level = level;
|
||||
this.KeyedLevelDimensionName = this.createKeyedLevelDimensionName();
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// instance methods //
|
||||
//==================//
|
||||
//=========//
|
||||
// methods //
|
||||
//=========//
|
||||
|
||||
@Override
|
||||
public File getMcSaveFolder()
|
||||
{
|
||||
#if MC_VER < MC_1_21_3
|
||||
return this.level.getChunkSource().getDataStorage().dataFolder;
|
||||
#else
|
||||
return this.level.getChunkSource().getDataStorage().dataFolder.toFile();
|
||||
#endif
|
||||
}
|
||||
public File getSaveFolder() { return this.level.getChunkSource().getDataStorage().dataFolder; }
|
||||
|
||||
@Override
|
||||
public String getKeyedLevelDimensionName() { return this.KeyedLevelDimensionName; }
|
||||
|
||||
private String createKeyedLevelDimensionName()
|
||||
{
|
||||
String dimensionName = this.getDhIdentifier();
|
||||
|
||||
if (Config.Server.sendLevelKeys.get())
|
||||
{
|
||||
String levelKeyPrefix = Config.Server.levelKeyPrefix.get();
|
||||
|
||||
if (SharedApi.getEnvironment() == EWorldEnvironment.CLIENT_SERVER)
|
||||
{
|
||||
String cleanWorldFolderName = this.getWorldFolderName()
|
||||
.replaceAll("[^" + LevelInitMessage.ALLOWED_CHARS_REGEX + " ]", "")
|
||||
.replaceAll(" ", "_");
|
||||
|
||||
levelKeyPrefix += (!levelKeyPrefix.isEmpty() ? "_" : "") + cleanWorldFolderName
|
||||
+ "_" + this.getHashedSeedEncoded();
|
||||
}
|
||||
|
||||
if (levelKeyPrefix.isEmpty())
|
||||
{
|
||||
levelKeyPrefix = this.getHashedSeedEncoded();
|
||||
}
|
||||
|
||||
String mainPart = "@" + dimensionName;
|
||||
|
||||
return levelKeyPrefix.substring(0, Math.min(
|
||||
LevelInitMessage.MAX_LENGTH - mainPart.length(),
|
||||
levelKeyPrefix.length()
|
||||
)) + mainPart;
|
||||
}
|
||||
|
||||
return dimensionName;
|
||||
}
|
||||
private String getWorldFolderName()
|
||||
{
|
||||
try
|
||||
{
|
||||
// We use the overworld since it's the only dimension that is stored in the server root folder
|
||||
|
||||
#if MC_VER >= MC_1_21_3
|
||||
return this.level.getServer().getLevel(Level.OVERWORLD).getChunkSource().getDataStorage().dataFolder.getParent().getFileName().toString();
|
||||
#else // <= 1.21.3
|
||||
return this.level.getServer().getLevel(Level.OVERWORLD).getChunkSource().getDataStorage().dataFolder.getParentFile().getName();
|
||||
#endif
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.warn("Unable to get world folder name. LODs may not load or save correctly. Error: ["+e.getMessage()+"].", e);
|
||||
return "unknown_world";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public DimensionTypeWrapper getDimensionType() { return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType()); }
|
||||
|
||||
@Override
|
||||
public DimensionTypeWrapper getDimensionType()
|
||||
{
|
||||
#if MC_VER <= MC_1_21_10
|
||||
return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType());
|
||||
#else
|
||||
return DimensionTypeWrapper.getDimensionTypeWrapper(this.level.dimensionType(), this.getDimensionName());
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDimensionName()
|
||||
{
|
||||
#if MC_VER <= MC_1_21_10
|
||||
return this.level.dimension().location().toString();
|
||||
#else
|
||||
return this.level.dimension().identifier().toString();
|
||||
#endif
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getHashedSeed() { return this.level.getBiomeManager().biomeZoomSeed; }
|
||||
|
||||
@Override
|
||||
public String getDhIdentifier() { return this.getDimensionName(); }
|
||||
public String getDimensionName() { return this.level.dimension().location().toString(); }
|
||||
|
||||
@Override
|
||||
public EDhApiLevelType getLevelType() { return EDhApiLevelType.SERVER_LEVEL; }
|
||||
@@ -216,49 +108,69 @@ public class ServerLevelWrapper implements IServerLevelWrapper
|
||||
{
|
||||
#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()))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
ChunkAccess chunk = this.level.getChunk(pos.getX(), pos.getZ(), ChunkStatus.FULL, false);
|
||||
if (chunk == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return new ChunkWrapper(chunk, this.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();
|
||||
return source.hasChunk(chunkX, chunkZ);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBlockStateWrapper getBlockState(DhBlockPos pos)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ServerLevel getWrappedMcObject() { return this.level; }
|
||||
|
||||
@Override
|
||||
public void onUnload() { LEVEL_WRAPPER_REF_BY_SERVER_LEVEL.remove(this.level); }
|
||||
public void onUnload() { LEVEL_WRAPPER_BY_SERVER_LEVEL.remove(this.level); }
|
||||
|
||||
|
||||
@Override
|
||||
public void setDhLevel(IDhLevel dhLevel) { this.dhLevel = dhLevel; }
|
||||
@Override
|
||||
@Nullable
|
||||
public IDhLevel getDhLevel() { return this.dhLevel; }
|
||||
public void setParentLevel(IDhLevel parentLevel) { this.parentDhLevel = parentLevel; }
|
||||
|
||||
@Override
|
||||
public IDhApiCustomRenderRegister getRenderRegister()
|
||||
{
|
||||
if (this.dhLevel == null)
|
||||
if (this.parentDhLevel == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.dhLevel.getGenericRenderer();
|
||||
return this.parentDhLevel.getGenericRenderer();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDhSaveFolder()
|
||||
{
|
||||
if (this.dhLevel == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return this.dhLevel.getSaveStructure().getSaveFolder(this);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
@@ -266,6 +178,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.getDimensionName() + "}"; }
|
||||
|
||||
}
|
||||
|
||||
+571
-456
File diff suppressed because it is too large
Load Diff
-92
@@ -1,92 +0,0 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
|
||||
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
public class ChunkPosGenStream
|
||||
{
|
||||
|
||||
public static Iterator<ChunkPos> getIterator(int genMinX, int genMinZ, int width, int extraRadius)
|
||||
{ return getStream(genMinX, genMinZ, width, extraRadius).iterator(); }
|
||||
/** @param extraRadius in both the positive and negative directions */
|
||||
public static Stream<ChunkPos> getStream(int genMinX, int genMinZ, int width, int extraRadius)
|
||||
{ return StreamSupport.stream(new InclusiveChunkPosIterator(genMinX, genMinZ, width, extraRadius), false); }
|
||||
|
||||
private static class InclusiveChunkPosIterator extends Spliterators.AbstractSpliterator<ChunkPos>
|
||||
{
|
||||
private final int minX;
|
||||
private final int minZ;
|
||||
|
||||
private final int maxX;
|
||||
private final int maxZ;
|
||||
|
||||
|
||||
/** current X pos */
|
||||
int x;
|
||||
/** current Z pos */
|
||||
private int z;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
protected InclusiveChunkPosIterator(int genMinX, int genMinZ, int width, int extraRadius)
|
||||
{
|
||||
super(getCount(width, extraRadius), Spliterator.SIZED);
|
||||
|
||||
this.minX = genMinX - extraRadius;
|
||||
this.minZ = genMinZ - extraRadius;
|
||||
|
||||
this.maxX = genMinX + (width - 1) + extraRadius;
|
||||
this.maxZ = genMinZ + (width - 1) + extraRadius;
|
||||
|
||||
// X starts at 1 minus the minX so we can immediately re-add 1 in the tryAdvance() loop
|
||||
this.x = this.minX - 1;
|
||||
this.z = this.minZ;
|
||||
}
|
||||
private static int getCount(int width, int extraRadius)
|
||||
{
|
||||
int widthPlusExtra = width + (extraRadius * 2);
|
||||
return widthPlusExtra * widthPlusExtra;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// iterator method //
|
||||
//=================//
|
||||
|
||||
@Override
|
||||
public boolean tryAdvance(Consumer<? super ChunkPos> consumer)
|
||||
{
|
||||
if (this.x == this.maxX && this.z == this.maxZ)
|
||||
{
|
||||
// the last returned position was the final valid position
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.x == this.maxX)
|
||||
{
|
||||
// we reached the max X position, loop back around in the next Z row
|
||||
this.x = this.minX;
|
||||
this.z++;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.x++;
|
||||
}
|
||||
|
||||
consumer.accept(new ChunkPos(this.x, this.z));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+74
-108
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -19,153 +19,119 @@
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.lang.invoke.MethodHandles;
|
||||
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.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
|
||||
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
||||
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.util.objects.EventTimer;
|
||||
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
public final class GenerationEvent
|
||||
{
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();;
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
|
||||
private static int generationFutureDebugIDs = 0;
|
||||
|
||||
private static final AtomicInteger DEBUG_ID_REF = new AtomicInteger(0);
|
||||
|
||||
|
||||
/** can be used for troubleshooting */
|
||||
public final int id;
|
||||
|
||||
public final ThreadWorldGenParams threadedParam;
|
||||
public final ThreadedParameters threadedParam;
|
||||
public final DhChunkPos minPos;
|
||||
public final int widthInChunks;
|
||||
/** the number of chunks wide this event is */
|
||||
public final int size;
|
||||
public final EDhApiWorldGenerationStep targetGenerationStep;
|
||||
public final EDhApiDistantGeneratorMode generatorMode;
|
||||
public final CompletableFuture<Void> future;
|
||||
public EventTimer timer = null;
|
||||
public long inQueueTime;
|
||||
public long timeoutTime = -1;
|
||||
public CompletableFuture<Void> future = null;
|
||||
public final Consumer<IChunkWrapper> resultConsumer;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
private GenerationEvent(
|
||||
DhChunkPos minPos, int widthInChunks, BatchGenerationEnvironment generationGroup,
|
||||
EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep targetGenerationStep, Consumer<IChunkWrapper> resultConsumer)
|
||||
public GenerationEvent(
|
||||
DhChunkPos minPos, int size, BatchGenerationEnvironment generationGroup,
|
||||
EDhApiWorldGenerationStep targetGenerationStep, Consumer<IChunkWrapper> resultConsumer)
|
||||
{
|
||||
this.id = DEBUG_ID_REF.getAndIncrement();
|
||||
|
||||
this.inQueueTime = System.nanoTime();
|
||||
this.id = generationFutureDebugIDs++;
|
||||
this.minPos = minPos;
|
||||
this.widthInChunks = widthInChunks;
|
||||
this.size = size;
|
||||
this.targetGenerationStep = targetGenerationStep;
|
||||
this.generatorMode = generatorMode;
|
||||
this.threadedParam = ThreadWorldGenParams.getOrMake(generationGroup.globalParams);
|
||||
this.future = new CompletableFuture<>();
|
||||
this.threadedParam = ThreadedParameters.getOrMake(generationGroup.params);
|
||||
this.resultConsumer = resultConsumer;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=======//
|
||||
// start //
|
||||
//=======//
|
||||
|
||||
public static GenerationEvent start(
|
||||
DhChunkPos minPos, int widthInChunks, BatchGenerationEnvironment genEnvironment,
|
||||
EDhApiDistantGeneratorMode generatorMode, EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer,
|
||||
public static GenerationEvent startEvent(
|
||||
DhChunkPos minPos, int size, BatchGenerationEnvironment genEnvironment,
|
||||
EDhApiWorldGenerationStep target, Consumer<IChunkWrapper> resultConsumer,
|
||||
ExecutorService worldGeneratorThreadPool)
|
||||
{
|
||||
GenerationEvent genEvent = new GenerationEvent(minPos, widthInChunks, genEnvironment, generatorMode, target, resultConsumer);
|
||||
//if (size % 2 == 0)
|
||||
//{
|
||||
// size += 1; // size must be odd for vanilla world gen regions to work
|
||||
//}
|
||||
|
||||
try
|
||||
|
||||
GenerationEvent generationEvent = new GenerationEvent(minPos, size, genEnvironment, target, resultConsumer);
|
||||
generationEvent.future = CompletableFuture.runAsync(() ->
|
||||
{
|
||||
worldGeneratorThreadPool.execute(() ->
|
||||
long runStartTime = System.nanoTime();
|
||||
generationEvent.timeoutTime = runStartTime;
|
||||
generationEvent.inQueueTime = runStartTime - generationEvent.inQueueTime;
|
||||
generationEvent.timer = new EventTimer("setup");
|
||||
|
||||
BatchGenerationEnvironment.isDistantGeneratorThread.set(true);
|
||||
try
|
||||
{
|
||||
try
|
||||
{
|
||||
BatchGenerationEnvironment.isDhWorldGenThreadRef.set(true);
|
||||
|
||||
|
||||
if (genEvent.generatorMode == EDhApiDistantGeneratorMode.INTERNAL_SERVER)
|
||||
{
|
||||
genEnvironment.internalServerGenerator.generateChunksViaInternalServer(genEvent);
|
||||
genEvent.future.complete(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
genEnvironment.generateEvent(genEvent);
|
||||
}
|
||||
catch (Throwable throwable)
|
||||
{
|
||||
handleWorldGenThrowable(genEvent, throwable);
|
||||
}
|
||||
finally
|
||||
{
|
||||
genEvent.future.complete(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Throwable initialThrowable)
|
||||
{
|
||||
handleWorldGenThrowable(genEvent, initialThrowable);
|
||||
}
|
||||
finally
|
||||
{
|
||||
BatchGenerationEnvironment.isDhWorldGenThreadRef.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
catch (RejectedExecutionException e)
|
||||
{
|
||||
genEvent.future.completeExceptionally(e);
|
||||
}
|
||||
|
||||
return genEvent;
|
||||
//LOGGER.info("generating [{}]", event.minPos);
|
||||
genEnvironment.generateLodFromList(generationEvent);
|
||||
}
|
||||
catch (InterruptedException ignored) { }
|
||||
finally
|
||||
{
|
||||
BatchGenerationEnvironment.isDistantGeneratorThread.remove();
|
||||
}
|
||||
}, 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)
|
||||
|
||||
public boolean isComplete() { return this.future.isDone(); }
|
||||
|
||||
public boolean hasTimeout(int duration, TimeUnit unit)
|
||||
{
|
||||
Throwable throwable = initialThrowable;
|
||||
while (throwable instanceof CompletionException)
|
||||
if (this.timeoutTime == -1)
|
||||
{
|
||||
throwable = throwable.getCause();
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean isShutdownException = ExceptionUtil.isShutdownException(throwable);
|
||||
if (isShutdownException)
|
||||
{
|
||||
// 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);
|
||||
}
|
||||
long currentTime = System.nanoTime();
|
||||
long delta = currentTime - this.timeoutTime;
|
||||
return (delta > TimeUnit.NANOSECONDS.convert(duration, unit));
|
||||
}
|
||||
|
||||
public boolean terminate()
|
||||
{
|
||||
LOGGER.info("======================DUMPING ALL THREADS FOR WORLD GEN=======================");
|
||||
ThreadPoolUtil.WORLD_GEN_THREAD_FACTORY.dumpAllThreadStacks();
|
||||
this.future.cancel(true);
|
||||
return this.future.isCancelled();
|
||||
}
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
public void refreshTimeout()
|
||||
{
|
||||
this.timeoutTime = System.nanoTime();
|
||||
UncheckedInterruptedException.throwIfInterrupted();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() { return this.id + ":" + this.widthInChunks + "@" + this.minPos + "(" + this.targetGenerationStep + ")"; }
|
||||
|
||||
|
||||
public String toString() { return this.id + ":" + this.size + "@" + this.minPos + "(" + this.targetGenerationStep + ")"; }
|
||||
|
||||
}
|
||||
+35
-68
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -17,7 +17,7 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.params;
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
|
||||
|
||||
import com.mojang.datafixers.DataFixer;
|
||||
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
|
||||
@@ -27,112 +27,79 @@ import net.minecraft.core.Registry;
|
||||
import net.minecraft.core.RegistryAccess;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.ThreadedLevelLightEngine;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.BiomeManager;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.world.level.chunk.storage.ChunkScanAccess;
|
||||
#endif
|
||||
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
#if MC_VER < MC_1_19_2
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
|
||||
#elif MC_VER < MC_1_19_2
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureManager;
|
||||
#else
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||
import net.minecraft.world.level.levelgen.RandomState;
|
||||
#if MC_VER >= MC_1_19_4
|
||||
import net.minecraft.world.level.levelgen.WorldOptions;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
#endif
|
||||
#endif
|
||||
import net.minecraft.world.level.storage.WorldData;
|
||||
|
||||
#if MC_VER < MC_1_19_4
|
||||
#elif MC_VER < MC_1_21_3
|
||||
import net.minecraft.core.registries.Registries;
|
||||
#else
|
||||
import net.minecraft.core.registries.Registries;
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_19_4
|
||||
import net.minecraft.world.level.levelgen.WorldGenSettings;
|
||||
#else
|
||||
import net.minecraft.world.level.levelgen.WorldOptions;
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Handles parameters that are relevant for the entire MC world.
|
||||
*
|
||||
* @see ThreadWorldGenParams
|
||||
*/
|
||||
public final class GlobalWorldGenParams
|
||||
public final class GlobalParameters
|
||||
{
|
||||
public final ChunkGenerator generator;
|
||||
public final IDhServerLevel dhServerLevel;
|
||||
public final ServerLevel mcServerLevel;
|
||||
public final Registry<Biome> biomes;
|
||||
public final RegistryAccess registry;
|
||||
public final long worldSeed;
|
||||
public final DataFixer dataFixer;
|
||||
|
||||
#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;
|
||||
public final ChunkScanAccess chunkScanner; // FIXME: Figure out if this is actually needed
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public GlobalWorldGenParams(IDhServerLevel dhServerLevel)
|
||||
public GlobalParameters(IDhServerLevel lodLevel)
|
||||
{
|
||||
this.dhServerLevel = dhServerLevel;
|
||||
this.lodLevel = lodLevel;
|
||||
|
||||
this.mcServerLevel = ((ServerLevelWrapper) dhServerLevel.getServerLevelWrapper()).getWrappedMcObject();
|
||||
MinecraftServer server = this.mcServerLevel.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.mcServerLevel, BiomeManager.obfuscateSeed(this.worldSeed));
|
||||
this.chunkScanner = this.mcServerLevel.getChunkSource().chunkScanner();
|
||||
biomeManager = new BiomeManager(level, BiomeManager.obfuscateSeed(worldSeed));
|
||||
chunkScanner = level.getChunkSource().chunkScanner();
|
||||
#endif
|
||||
|
||||
this.structures = server.getStructureManager();
|
||||
this.generator = this.mcServerLevel.getChunkSource().getGenerator();
|
||||
this.dataFixer = server.getFixerUpper();
|
||||
|
||||
structures = server.getStructureManager();
|
||||
generator = level.getChunkSource().getGenerator();
|
||||
fixerUpper = server.getFixerUpper();
|
||||
#if MC_VER >= MC_1_19_2
|
||||
this.randomState = this.mcServerLevel.getChunkSource().randomState();
|
||||
randomState = level.getChunkSource().randomState();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
-311
@@ -1,311 +0,0 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
|
||||
|
||||
import com.seibel.distanthorizons.api.DhApi;
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.GlobalWorldGenParams;
|
||||
import com.seibel.distanthorizons.core.api.internal.ClientApi;
|
||||
import com.seibel.distanthorizons.core.api.internal.SharedApi;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
|
||||
import com.seibel.distanthorizons.core.enums.EMinecraftColor;
|
||||
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
|
||||
import com.seibel.distanthorizons.core.level.IDhServerLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.util.TimerUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IC2meAccessor;
|
||||
import com.seibel.distanthorizons.coreapi.ModInfo;
|
||||
import net.minecraft.server.level.ChunkHolder;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.server.level.TicketType;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
#else
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class InternalServerGenerator
|
||||
{
|
||||
public static final DhLogger LOGGER = new DhLoggerBuilder()
|
||||
.name("LOD World Gen - Internal Server")
|
||||
.fileLevelConfig(Config.Common.Logging.logWorldGenEventToFile)
|
||||
.build();
|
||||
|
||||
public static final DhLogger CHUNK_LOAD_LOGGER = new DhLoggerBuilder()
|
||||
.name("LOD Chunk Loading")
|
||||
.fileLevelConfig(Config.Common.Logging.logWorldGenChunkLoadEventToFile)
|
||||
.build();
|
||||
|
||||
private static final IC2meAccessor C2ME_ACCESSOR = ModAccessorInjector.INSTANCE.get(IC2meAccessor.class);
|
||||
|
||||
/**
|
||||
* Used to revert the ignore logic in {@link SharedApi} so
|
||||
* that a given chunk pos can be handled again.
|
||||
* A timer is used so we don't have to inject into MC's code and it works sell enough
|
||||
* most of the time.
|
||||
* If a chunk does get through due the timeout not being long enough that isn't the end of the world.
|
||||
*/
|
||||
private static final int MS_TO_IGNORE_CHUNK_AFTER_COMPLETION = 5_000;
|
||||
|
||||
#if MC_VER < MC_1_21_5
|
||||
private static final TicketType<ChunkPos> DH_SERVER_GEN_TICKET = TicketType.create("dh_server_gen_ticket", Comparator.comparingLong(ChunkPos::toLong));
|
||||
#elif MC_VER < MC_1_21_9
|
||||
private static final TicketType DH_SERVER_GEN_TICKET = new TicketType(/* timeout, 0 = disabled*/0L, /* persist */ false, TicketType.TicketUse.LOADING);
|
||||
#else
|
||||
private static final TicketType DH_SERVER_GEN_TICKET = new TicketType(/* timeout, 0 = disabled*/0L, /* flags */TicketType.FLAG_LOADING);
|
||||
#endif
|
||||
|
||||
private static boolean c2meMissingWarningLogged = false;
|
||||
|
||||
|
||||
private final GlobalWorldGenParams params;
|
||||
private final IDhServerLevel dhServerLevel;
|
||||
private final Timer chunkSaveIgnoreTimer = TimerUtil.CreateTimer("ChunkSaveIgnoreTimer");
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public InternalServerGenerator(GlobalWorldGenParams params, IDhServerLevel dhServerLevel)
|
||||
{
|
||||
this.params = params;
|
||||
this.dhServerLevel = dhServerLevel;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//============//
|
||||
// generation //
|
||||
//============//
|
||||
|
||||
public void generateChunksViaInternalServer(GenerationEvent genEvent)
|
||||
{
|
||||
this.runValidation();
|
||||
|
||||
try
|
||||
{
|
||||
//=====================//
|
||||
// create gen requests //
|
||||
//=====================//
|
||||
|
||||
ArrayList<CompletableFuture<ChunkAccess>> getChunkFutureList = new ArrayList<>();
|
||||
{
|
||||
Iterator<ChunkPos> chunkPosIterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0);
|
||||
while (chunkPosIterator.hasNext())
|
||||
{
|
||||
ChunkPos chunkPos = chunkPosIterator.next();
|
||||
|
||||
CompletableFuture<ChunkAccess> requestChunkFuture =
|
||||
this.requestChunkFromServerAsync(chunkPos)
|
||||
// log errors if necessary
|
||||
.whenCompleteAsync(
|
||||
(chunk, throwable) ->
|
||||
{
|
||||
// unwrap the CompletionException if necessary
|
||||
Throwable actualThrowable = throwable;
|
||||
while (actualThrowable instanceof CompletionException)
|
||||
{
|
||||
actualThrowable = actualThrowable.getCause();
|
||||
}
|
||||
|
||||
if (actualThrowable != null)
|
||||
{
|
||||
// ignore expected shutdown exceptions
|
||||
boolean isShutdownException =
|
||||
ExceptionUtil.isShutdownException(actualThrowable)
|
||||
|| actualThrowable.getMessage().contains("Unloaded chunk");
|
||||
if (!isShutdownException)
|
||||
{
|
||||
CHUNK_LOAD_LOGGER.warn("DistantHorizons: Couldn't load chunk [" + chunkPos + "] from server, error: [" + actualThrowable.getMessage() + "].", actualThrowable);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
getChunkFutureList.add(requestChunkFuture);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==============================//
|
||||
// wait for generation requests //
|
||||
//==============================//
|
||||
|
||||
// Join-ing each thread will prevent DH from working on anything else
|
||||
// but will also prevent over-queuing world gen tasks.
|
||||
// If C2ME is present the CPU will still be well utilized.
|
||||
|
||||
ArrayList<IChunkWrapper> chunkWrappers = new ArrayList<>();
|
||||
for (int i = 0; i < getChunkFutureList.size(); i++)
|
||||
{
|
||||
CompletableFuture<ChunkAccess> getChunkFuture = getChunkFutureList.get(i);
|
||||
ChunkAccess chunk = getChunkFuture.join();
|
||||
if (chunk != null)
|
||||
{
|
||||
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, this.dhServerLevel.getLevelWrapper());
|
||||
chunkWrapper.createDhHeightMaps();
|
||||
chunkWrappers.add(chunkWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==========================//
|
||||
// process generated chunks //
|
||||
//==========================//
|
||||
|
||||
int maxSkyLight = this.dhServerLevel.getServerLevelWrapper().hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT;
|
||||
for (int i = 0; i < chunkWrappers.size(); i++)
|
||||
{
|
||||
ChunkWrapper chunkWrapper = (ChunkWrapper)chunkWrappers.get(i);
|
||||
|
||||
// pre-generated chunks should have lighting but new ones won't
|
||||
if (!chunkWrapper.isDhBlockLightingCorrect())
|
||||
{
|
||||
DhLightingEngine.INSTANCE.bakeChunkBlockLighting(chunkWrapper, chunkWrappers, maxSkyLight);
|
||||
}
|
||||
|
||||
this.dhServerLevel.updateBeaconBeamsForChunk(chunkWrapper, chunkWrappers);
|
||||
genEvent.resultConsumer.accept(chunkWrapper);
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
// release all chunks from the server to prevent out of memory issues
|
||||
Iterator<ChunkPos> chunkPosIterator = ChunkPosGenStream.getIterator(genEvent.minPos.getX(), genEvent.minPos.getZ(), genEvent.widthInChunks, 0);
|
||||
while (chunkPosIterator.hasNext())
|
||||
{
|
||||
ChunkPos chunkPos = chunkPosIterator.next();
|
||||
this.releaseChunkFromServer(this.params.mcServerLevel, chunkPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
private void runValidation()
|
||||
{
|
||||
// DH thread check
|
||||
if (!DhApi.isDhThread()
|
||||
&& ModInfo.IS_DEV_BUILD)
|
||||
{
|
||||
throw new IllegalStateException("Internal server generation should be called from one of DH's world gen thread. Current thread: ["+Thread.currentThread().getName()+"]");
|
||||
}
|
||||
|
||||
|
||||
// C2ME present?
|
||||
if (C2ME_ACCESSOR == null
|
||||
&& !c2meMissingWarningLogged)
|
||||
{
|
||||
c2meMissingWarningLogged = true;
|
||||
|
||||
String c2meWarning = "C2ME missing, \n" +
|
||||
"low CPU usage and slow world gen speeds expected. \n" +
|
||||
"DH is set to use MC's internal server for world gen \n" +
|
||||
"this mode is less efficient unless a mod like C2ME is present."
|
||||
;
|
||||
|
||||
if (Config.Common.Logging.Warning.showSlowWorldGenSettingWarnings.get())
|
||||
{
|
||||
String message =
|
||||
EMinecraftColor.ORANGE + "Distant Horizons: slow world gen." + EMinecraftColor.CLEAR_FORMATTING + "\n" +
|
||||
c2meWarning;
|
||||
ClientApi.INSTANCE.showChatMessageNextFrame(message);
|
||||
}
|
||||
|
||||
LOGGER.warn(c2meWarning);
|
||||
}
|
||||
}
|
||||
private CompletableFuture<ChunkAccess> requestChunkFromServerAsync(ChunkPos chunkPos)
|
||||
{
|
||||
return CompletableFuture.supplyAsync(() ->
|
||||
{
|
||||
ServerLevel level = this.params.mcServerLevel;
|
||||
|
||||
// ignore chunk update events for this position
|
||||
SharedApi.CHUNK_UPDATE_QUEUE_MANAGER.addPosToIgnore(new DhChunkPos(chunkPos.x, chunkPos.z));
|
||||
|
||||
#if MC_VER < MC_1_21_5
|
||||
int chunkLevel = 33; // 33 is equivalent to FULL Chunk
|
||||
level.getChunkSource().distanceManager.addTicket(DH_SERVER_GEN_TICKET, chunkPos, chunkLevel, chunkPos);
|
||||
#else
|
||||
level.getChunkSource().addTicketWithRadius(DH_SERVER_GEN_TICKET, chunkPos, 0);
|
||||
#endif
|
||||
|
||||
// probably not the most optimal to run updates here, but fast enough
|
||||
level.getChunkSource().distanceManager.runAllUpdates(level.getChunkSource().chunkMap);
|
||||
|
||||
ChunkHolder chunkHolder = level.getChunkSource().chunkMap.getUpdatingChunkIfPresent(chunkPos.toLong());
|
||||
if (chunkHolder == null)
|
||||
{
|
||||
throw new IllegalStateException("No chunk chunkHolder for pos ["+chunkPos+"] after ticket has been added.");
|
||||
}
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
return chunkHolder.getOrScheduleFuture(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
|
||||
.thenApply(result -> result.left().orElseThrow(() -> new RuntimeException(result.right().get().toString()))); // can throw if the server is shutting down
|
||||
#elif MC_VER <= MC_1_20_6
|
||||
return chunkHolder.getOrScheduleFuture(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
|
||||
.thenApply(result -> result.orElseThrow(() -> new RuntimeException(result.toString()))); // can throw if the server is shutting down
|
||||
#else
|
||||
return chunkHolder.scheduleChunkGenerationTask(ChunkStatus.FEATURES, level.getChunkSource().chunkMap)
|
||||
.thenApply(result -> result.orElseThrow(() -> new RuntimeException(result.getError()))); // can throw if the server is shutting down
|
||||
#endif
|
||||
|
||||
}, this.params.mcServerLevel.getChunkSource().chunkMap.mainThreadExecutor)
|
||||
.thenCompose(Function.identity());
|
||||
}
|
||||
/**
|
||||
* mitigates out of memory issues in the vanilla chunk system. <br>
|
||||
* See: https://github.com/pop4959/Chunky/pull/383
|
||||
*/
|
||||
private void releaseChunkFromServer(ServerLevel level, ChunkPos chunkPos)
|
||||
{
|
||||
level.getChunkSource().chunkMap.mainThreadExecutor.execute(() ->
|
||||
{
|
||||
try
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
int chunkLevel = 33; // 33 is equivalent to FULL Chunk
|
||||
level.getChunkSource().distanceManager.removeTicket(DH_SERVER_GEN_TICKET, chunkPos, chunkLevel, chunkPos);
|
||||
#else
|
||||
level.getChunkSource().removeTicketWithRadius(DH_SERVER_GEN_TICKET, chunkPos, 0);
|
||||
#endif
|
||||
|
||||
level.getChunkSource().chunkMap.tick(() -> false);
|
||||
|
||||
#if MC_VER > MC_1_16_5
|
||||
level.entityManager.tick();
|
||||
#endif
|
||||
|
||||
|
||||
// give MC a few seconds to save the chunk before
|
||||
// we can process update events there again
|
||||
this.chunkSaveIgnoreTimer.schedule(new TimerTask()
|
||||
{
|
||||
@Override
|
||||
public void run() { SharedApi.CHUNK_UPDATE_QUEUE_MANAGER.removePosToIgnore(new DhChunkPos(chunkPos.x, chunkPos.z)); }
|
||||
}, MS_TO_IGNORE_CHUNK_AFTER_COMPLETION);
|
||||
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.warn("Failed to release chunk back to internal server. Error: ["+e.getMessage()+"]", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+32
-8
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -17,16 +17,40 @@
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.neoforge.wrappers.modAccessor;
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IC2meAccessor;
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration;
|
||||
|
||||
public class C2meAccessor implements IC2meAccessor
|
||||
//FIXME: Move this outside the WorldGenerationStep thingy
|
||||
public class Rolling
|
||||
{
|
||||
@Override
|
||||
public String getModName()
|
||||
|
||||
private final int size;
|
||||
private double total = 0d;
|
||||
private int index = 0;
|
||||
private final double[] samples;
|
||||
|
||||
public Rolling(int size)
|
||||
{
|
||||
return "c2me";
|
||||
this.size = size;
|
||||
samples = new double[size];
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
samples[i] = 0d;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
public void add(double x)
|
||||
{
|
||||
total -= samples[index];
|
||||
samples[index] = x;
|
||||
total += x;
|
||||
if (++index == size)
|
||||
index = 0; // cheaper than modulus
|
||||
}
|
||||
|
||||
public double getAverage()
|
||||
{
|
||||
return size == 0 ? 0 : total / size;
|
||||
}
|
||||
|
||||
}
|
||||
+109
@@ -0,0 +1,109 @@
|
||||
/*
|
||||
* 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.worldGeneration;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment.PerfCalculator;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.WorldGenStructFeatManager;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.world.level.levelgen.structure.StructureCheck;
|
||||
#endif
|
||||
|
||||
public final class ThreadedParameters
|
||||
{
|
||||
private static final ThreadLocal<ThreadedParameters> LOCAL_PARAM = new ThreadLocal<>();
|
||||
|
||||
final ServerLevel level;
|
||||
public WorldGenStructFeatManager structFeat = null;
|
||||
#if MC_VER >= MC_1_18_2
|
||||
public StructureCheck structCheck;
|
||||
#endif
|
||||
boolean isValid = true;
|
||||
public final PerfCalculator perf = new PerfCalculator();
|
||||
|
||||
private static GlobalParameters previousGlobalParameters = null;
|
||||
|
||||
|
||||
|
||||
public static ThreadedParameters getOrMake(GlobalParameters param)
|
||||
{
|
||||
ThreadedParameters tParam = LOCAL_PARAM.get();
|
||||
if (tParam != null && tParam.isValid && tParam.level == param.level)
|
||||
{
|
||||
return tParam;
|
||||
}
|
||||
|
||||
tParam = new ThreadedParameters(param);
|
||||
LOCAL_PARAM.set(tParam);
|
||||
return tParam;
|
||||
}
|
||||
|
||||
private ThreadedParameters(GlobalParameters param)
|
||||
{
|
||||
previousGlobalParameters = param;
|
||||
|
||||
this.level = param.level;
|
||||
#if MC_VER < MC_1_18_2
|
||||
this.structFeat = new WorldGenStructFeatManager(param.worldGenSettings, level);
|
||||
#elif MC_VER < MC_1_19_2
|
||||
this.structCheck = this.createStructureCheck(param);
|
||||
#else
|
||||
this.structCheck = new StructureCheck(param.chunkScanner, param.registry, param.structures,
|
||||
param.level.dimension(), param.generator, param.randomState, level, param.generator.getBiomeSource(), param.worldSeed,
|
||||
param.fixerUpper);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void markAsInvalid() { isValid = false; }
|
||||
|
||||
public void makeStructFeat(WorldGenLevel genLevel, GlobalParameters param)
|
||||
{
|
||||
#if MC_VER < MC_1_19_4
|
||||
structFeat = new WorldGenStructFeatManager(param.worldGenSettings, genLevel #if MC_VER >= MC_1_18_2 , structCheck #endif );
|
||||
#else
|
||||
structFeat = new WorldGenStructFeatManager(param.worldOptions, genLevel, structCheck);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#if MC_VER >= MC_1_18_2 && MC_VER < MC_1_19_2
|
||||
public void recreateStructureCheck()
|
||||
{
|
||||
if (previousGlobalParameters != null)
|
||||
{
|
||||
this.structCheck = createStructureCheck(previousGlobalParameters);
|
||||
}
|
||||
}
|
||||
private StructureCheck createStructureCheck(GlobalParameters param)
|
||||
{
|
||||
return new StructureCheck(param.chunkScanner, param.registry, param.structures,
|
||||
param.level.dimension(), param.generator, this.level, param.generator.getBiomeSource(), param.worldSeed,
|
||||
param.fixerUpper);
|
||||
}
|
||||
#else
|
||||
public void recreateStructureCheck() { /* do nothing */ }
|
||||
#endif
|
||||
|
||||
}
|
||||
-745
@@ -1,745 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU GPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling;
|
||||
|
||||
import com.mojang.serialization.Codec;
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.level.IDhServerLevel;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
|
||||
import net.minecraft.core.Registry;
|
||||
#if MC_VER >= MC_1_19_4
|
||||
import net.minecraft.core.registries.Registries;
|
||||
#endif
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import net.minecraft.nbt.NbtOps;
|
||||
import net.minecraft.nbt.Tag;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
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
|
||||
|
||||
#if MC_VER < MC_1_21_9
|
||||
import net.minecraft.world.level.block.Blocks;
|
||||
#else
|
||||
#endif
|
||||
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
#if MC_VER >= MC_1_18_2
|
||||
#if MC_VER < MC_1_19_2
|
||||
import net.minecraft.world.level.levelgen.feature.StructureFeature;
|
||||
#endif
|
||||
import net.minecraft.world.ticks.LevelChunkTicks;
|
||||
#endif
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.core.Holder;
|
||||
#if MC_VER < MC_1_19_2
|
||||
import net.minecraft.world.level.levelgen.feature.ConfiguredStructureFeature;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
#endif
|
||||
|
||||
#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
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
|
||||
|
||||
public class ChunkCompoundTagParser
|
||||
{
|
||||
public static final DhLogger LOGGER = new DhLoggerBuilder()
|
||||
.name("LOD Chunk Reader")
|
||||
.fileLevelConfig(Config.Common.Logging.logWorldGenChunkLoadEventToFile)
|
||||
.build();
|
||||
|
||||
private static final AtomicBoolean ZERO_CHUNK_POS_ERROR_LOGGED_REF = new AtomicBoolean(false);
|
||||
private static final ConcurrentHashMap<String, Object> LOGGED_ERROR_MESSAGE_MAP = new ConcurrentHashMap<>();
|
||||
|
||||
private static boolean lightingSectionErrorLogged = false;
|
||||
|
||||
|
||||
|
||||
|
||||
//============//
|
||||
// read chunk //
|
||||
//============//
|
||||
|
||||
public static ChunkWrapper createFromTag(
|
||||
WorldGenLevel mcWorldGenLevel, IDhServerLevel dhServerLevel,
|
||||
ChunkPos chunkPos, CompoundTag chunkData)
|
||||
{
|
||||
#if MC_VER < MC_1_18_2
|
||||
CompoundTag tagLevel = chunkData.getCompound("Level");
|
||||
#else
|
||||
CompoundTag tagLevel = chunkData;
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//=======================//
|
||||
// validate the chunkPos //
|
||||
//=======================//
|
||||
|
||||
int chunkX = CompoundTagUtil.getInt(tagLevel,"xPos");
|
||||
int chunkZ = CompoundTagUtil.getInt(tagLevel, "zPos");
|
||||
ChunkPos actualChunkPos = new ChunkPos(chunkX, chunkZ);
|
||||
|
||||
// confirm chunk pos is correct
|
||||
if (!Objects.equals(chunkPos, actualChunkPos))
|
||||
{
|
||||
if (chunkX == 0 && chunkZ == 0)
|
||||
{
|
||||
if (!ZERO_CHUNK_POS_ERROR_LOGGED_REF.getAndSet(true))
|
||||
{
|
||||
// explicit chunkPos toString is necessary otherwise the JDK 17 compiler breaks
|
||||
LOGGER.warn("Chunk file at ["+chunkPos.toString()+"] doesn't have a chunk pos. \n" +
|
||||
"This might happen if the world was created using an external program. \n" +
|
||||
"DH will attempt to parse the chunk anyway and won't log this message again.\n" +
|
||||
"If issues arise please try optimizing your world to fix this issue. \n" +
|
||||
"World optimization can be done from the singleplayer world selection screen." +
|
||||
" ");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGGER.error("Chunk file at ["+chunkPos.toString()+"] is in the wrong location. \n" +
|
||||
"Please try optimizing your world to fix this issue. \n" +
|
||||
"World optimization can be done from the singleplayer world selection screen. \n" +
|
||||
"(Expected pos: ["+chunkPos.toString()+"], actual ["+actualChunkPos.toString()+"])" +
|
||||
" ");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//===========//
|
||||
// get ticks //
|
||||
//===========//
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
ChunkBiomeContainer chunkBiomeContainer = new ChunkBiomeContainer(
|
||||
mcWorldGenLevel.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), #if MC_VER >= MC_1_17_1 mcWorldGenLevel, #endif
|
||||
chunkPos, mcWorldGenLevel.getLevel().getChunkSource().getGenerator().getBiomeSource(),
|
||||
tagLevel.contains("Biomes", 11) ? tagLevel.getIntArray("Biomes") : null);
|
||||
|
||||
String BLOCK_TICKS_TAG_PRE18 = "TileTicks";
|
||||
TickList<Block> blockTicks = tagLevel.contains(BLOCK_TICKS_TAG_PRE18, 9)
|
||||
? ChunkTickList.create(tagLevel.getList(BLOCK_TICKS_TAG_PRE18, 10), Registry.BLOCK::getKey, Registry.BLOCK::get)
|
||||
: new ProtoTickList<Block>(block -> (block == null || block.defaultBlockState().isAir()), chunkPos,
|
||||
tagLevel.getList("ToBeTicked", 9) #if MC_VER >= MC_1_17_1 , mcWorldGenLevel #endif );
|
||||
|
||||
String FLUID_TICKS_TAG_PRE18 = "LiquidTicks";
|
||||
TickList<Fluid> fluidTicks = tagLevel.contains(FLUID_TICKS_TAG_PRE18, 9)
|
||||
? ChunkTickList.create(tagLevel.getList(FLUID_TICKS_TAG_PRE18, 10), Registry.FLUID::getKey, Registry.FLUID::get)
|
||||
: new ProtoTickList<Fluid>(fluid -> (fluid == null || fluid == Fluids.EMPTY), chunkPos,
|
||||
tagLevel.getList("LiquidsToBeTicked", 9) #if MC_VER >= MC_1_17_1 , mcWorldGenLevel #endif );
|
||||
#else
|
||||
// ticks shouldn't be needed so ignore them for MC versions after 1.18.2
|
||||
LevelChunkTicks<Block> blockTicks = new LevelChunkTicks<>();
|
||||
LevelChunkTicks<Fluid> fluidTicks = new LevelChunkTicks<>();
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//=====================//
|
||||
// get misc properties //
|
||||
//=====================//
|
||||
|
||||
int sectionYCount = #if MC_VER < MC_1_17_1 16; #else mcWorldGenLevel.getSectionsCount(); #endif
|
||||
LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYCount];
|
||||
boolean hasBlocks = readAndPopulateSections(mcWorldGenLevel, chunkPos, tagLevel, chunkSections);
|
||||
if (!hasBlocks)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
long inhabitedTime = CompoundTagUtil.getLong(tagLevel, "InhabitedTime");
|
||||
boolean isLightOn = CompoundTagUtil.getBoolean(tagLevel, "isLightOn");
|
||||
|
||||
|
||||
|
||||
//============//
|
||||
// make chunk //
|
||||
//============//
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
LevelChunk chunk = new LevelChunk((Level) mcWorldGenLevel.getLevel(), chunkPos, chunkBiomeContainer, UpgradeData.EMPTY, blockTicks,
|
||||
fluidTicks, inhabitedTime, chunkSections, null);
|
||||
#else
|
||||
LevelChunk chunk = new LevelChunk((Level) mcWorldGenLevel, chunkPos, UpgradeData.EMPTY, blockTicks,
|
||||
fluidTicks, inhabitedTime, chunkSections, null, null);
|
||||
#endif
|
||||
|
||||
// Set some states after object creation
|
||||
chunk.setLightCorrect(isLightOn);
|
||||
boolean hasHeightmapData = readHeightmaps(chunk, chunkData);
|
||||
|
||||
// chunk wrapper so we can pass along extra data more easily
|
||||
ChunkWrapper chunkWrapper = new ChunkWrapper(chunk, dhServerLevel.getServerLevelWrapper());
|
||||
chunkWrapper.createDhHeightMaps();
|
||||
|
||||
|
||||
|
||||
//===========================//
|
||||
// check if chunk has blocks //
|
||||
//===========================//
|
||||
|
||||
// in some MC versions all the NBT data will be there
|
||||
// but the chunk will be totally empty,
|
||||
// usually this means the chunk was only partially generated.
|
||||
// If that happens we should try to generate the chunk from scratch
|
||||
// otherwise we can end up with large empty holes in the world.
|
||||
|
||||
// walking through the heightmap (recreated by DH if missing)
|
||||
// is a fast way to check if there are any blocks in the chunk
|
||||
boolean chunkHasBlocks = false;
|
||||
int serverMinHeight = dhServerLevel.getServerLevelWrapper().getMinHeight();
|
||||
for (int x = 0; x < 16 && !chunkHasBlocks; x++)
|
||||
{
|
||||
for (int z = 0; z < 16 && !chunkHasBlocks; z++)
|
||||
{
|
||||
int heightMap = Math.max(
|
||||
// max between both heightmaps just in case there's a discrepancy
|
||||
chunkWrapper.getLightBlockingHeightMapValue(x, z),
|
||||
chunkWrapper.getSolidHeightMapValue(x, z)
|
||||
);
|
||||
if (heightMap != serverMinHeight)
|
||||
{
|
||||
chunkHasBlocks = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (chunkHasBlocks)
|
||||
{
|
||||
return chunkWrapper;
|
||||
}
|
||||
else
|
||||
{
|
||||
// no blocks detected, this chunk should be generated from scratch
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=================//
|
||||
// chunk sections //
|
||||
// (Blocks/biomes) //
|
||||
//=================//
|
||||
|
||||
/** handles both blocks and biomes */
|
||||
private static boolean readAndPopulateSections(
|
||||
LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData,
|
||||
LevelChunkSection[] chunkSections)
|
||||
{
|
||||
int sectionYCount = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif
|
||||
|
||||
ListTag tagSections = CompoundTagUtil.getListTag(chunkData, "Sections", 10);
|
||||
// try lower-case "sections" if capital "Sections" is missing
|
||||
if (tagSections == null
|
||||
|| tagSections.isEmpty())
|
||||
{
|
||||
tagSections = CompoundTagUtil.getListTag(chunkData, "sections", 10);
|
||||
}
|
||||
|
||||
|
||||
boolean blocksFound = false;
|
||||
if (tagSections != null)
|
||||
{
|
||||
for (int i = 0; i < tagSections.size(); ++i)
|
||||
{
|
||||
CompoundTag tagSection = CompoundTagUtil.getCompoundTag(tagSections, i);
|
||||
if (tagSection == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
final int sectionYPos = CompoundTagUtil.getByte(tagSection, "Y");
|
||||
|
||||
|
||||
|
||||
//===================//
|
||||
// get blocks/biomes //
|
||||
//===================//
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
if (tagSection.contains("Palette", 9)
|
||||
&& tagSection.contains("BlockStates", 12))
|
||||
{
|
||||
LevelChunkSection levelChunkSection = new LevelChunkSection(sectionYPos << 4);
|
||||
levelChunkSection.getStates().read(tagSection.getList("Palette", 10), tagSection.getLongArray("BlockStates"));
|
||||
levelChunkSection.recalcBlockCounts();
|
||||
if (!levelChunkSection.isEmpty())
|
||||
{
|
||||
int sectionIndex;
|
||||
#if MC_VER < MC_1_17_1
|
||||
sectionIndex = sectionYPos;
|
||||
#else
|
||||
sectionIndex = level.getSectionIndexFromSectionY(sectionYPos);
|
||||
#endif
|
||||
|
||||
chunkSections[sectionIndex] = levelChunkSection;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
int sectionId = level.getSectionIndexFromSectionY(sectionYPos);
|
||||
if (sectionId >= 0
|
||||
&& sectionId < chunkSections.length)
|
||||
{
|
||||
//========//
|
||||
// blocks //
|
||||
//========//
|
||||
|
||||
PalettedContainer<BlockState> blockStateContainer;
|
||||
|
||||
boolean containsBlockStates = CompoundTagUtil.contains(tagSection, "block_states", 10);
|
||||
if (containsBlockStates)
|
||||
{
|
||||
Codec<PalettedContainer<BlockState>> blockStateCodec = getBlockStateCodec(level);
|
||||
|
||||
#if MC_VER < MC_1_20_6
|
||||
blockStateContainer = blockStateCodec
|
||||
.parse(NbtOps.INSTANCE, CompoundTagUtil.getCompoundTag(tagSection, "block_states"))
|
||||
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
|
||||
.getOrThrow(false, (message) -> logParsingWarningOnce(message));
|
||||
#else
|
||||
blockStateContainer = blockStateCodec
|
||||
.parse(NbtOps.INSTANCE, CompoundTagUtil.getCompoundTag(tagSection, "block_states"))
|
||||
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
|
||||
.getOrThrow((message) -> logErrorAndReturnException(message));
|
||||
#endif
|
||||
|
||||
blocksFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
#if MC_VER < MC_1_21_9
|
||||
blockStateContainer = new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
|
||||
#else
|
||||
blockStateContainer = PalettedContainerFactory.create(level.registryAccess()).createForBlockStates();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//========//
|
||||
// biomes //
|
||||
//========//
|
||||
|
||||
Registry<Biome> biomeRegistry = getBiomeRegistry(level);
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
Codec<PalettedContainer<Biome>> biomeCodec;
|
||||
#else
|
||||
Codec<PalettedContainer<Holder<Biome>>> biomeCodec;
|
||||
#endif
|
||||
biomeCodec = getBiomeCodec(level, biomeRegistry);
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
PalettedContainer<Biome> biomeContainer;
|
||||
#else
|
||||
PalettedContainer<Holder<Biome>> biomeContainer;
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
biomeContainer = tagSection.contains("biomeRegistry", 10)
|
||||
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomeRegistry")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, (message) -> logWarningOnce(message))
|
||||
: new PalettedContainer<Biome>(biomeRegistry, biomeRegistry.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
||||
#else
|
||||
{
|
||||
CompoundTag biomeTag = CompoundTagUtil.getCompoundTag(tagSection, "biomeRegistry");
|
||||
if (biomeTag == null)
|
||||
{
|
||||
biomeTag = CompoundTagUtil.getCompoundTag(tagSection, "biomes");
|
||||
}
|
||||
|
||||
if (biomeTag != null
|
||||
&& !biomeTag.isEmpty())
|
||||
{
|
||||
#if MC_VER < MC_1_20_6
|
||||
biomeContainer = new PalettedContainer<Holder<Biome>>(
|
||||
biomeRegistry.asHolderIdMap(),
|
||||
biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
||||
#else
|
||||
biomeContainer = biomeCodec.parse(NbtOps.INSTANCE, biomeTag)
|
||||
.promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYCount, (String) string))
|
||||
.getOrThrow((message) -> logErrorAndReturnException(message));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// no biomes found, use the default (probably plains)
|
||||
|
||||
#if MC_VER < MC_1_21_3
|
||||
biomeContainer = new PalettedContainer<Holder<Biome>>(
|
||||
biomeRegistry.asHolderIdMap(),
|
||||
biomeRegistry.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
||||
#elif MC_VER < MC_1_21_9
|
||||
biomeContainer = new PalettedContainer<Holder<Biome>>(biomeRegistry.asHolderIdMap(),
|
||||
biomeRegistry.getOrThrow(Biomes.PLAINS),
|
||||
PalettedContainer.Strategy.SECTION_BIOMES);
|
||||
#else
|
||||
biomeContainer = PalettedContainerFactory.create(level.registryAccess()).createForBiomes();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_20_1
|
||||
chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer);
|
||||
#else
|
||||
chunkSections[sectionId] = new LevelChunkSection(blockStateContainer, biomeContainer);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return blocksFound;
|
||||
}
|
||||
|
||||
private static Codec<PalettedContainer<BlockState>> getBlockStateCodec(LevelAccessor level)
|
||||
{
|
||||
#if MC_VER < MC_1_18_2
|
||||
return null; // unused for older MC versions
|
||||
#elif MC_VER < MC_1_19_2
|
||||
return PalettedContainer.codec(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
|
||||
#elif MC_VER <= MC_1_21_8
|
||||
return PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
|
||||
#else
|
||||
return PalettedContainerFactory.create(level.registryAccess()).blockStatesContainerCodec();
|
||||
#endif
|
||||
}
|
||||
|
||||
private static Registry<Biome> getBiomeRegistry(LevelAccessor level)
|
||||
{
|
||||
#if MC_VER < MC_1_18_2
|
||||
// not needed
|
||||
return null;
|
||||
#elif MC_VER < MC_1_19_4
|
||||
return level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
|
||||
#elif MC_VER < MC_1_21_3
|
||||
return level.registryAccess().registryOrThrow(Registries.BIOME);
|
||||
#else
|
||||
return level.registryAccess().lookupOrThrow(Registries.BIOME);
|
||||
#endif
|
||||
}
|
||||
private static
|
||||
#if MC_VER < MC_1_19_2 Codec<PalettedContainer<Biome>>
|
||||
#else Codec<PalettedContainer<Holder<Biome>>>
|
||||
#endif
|
||||
getBiomeCodec(LevelAccessor level, Registry<Biome> biomeRegistry)
|
||||
{
|
||||
#if MC_VER < MC_1_18_2
|
||||
return null; // unused for older MC versions
|
||||
#elif MC_VER < MC_1_19_2
|
||||
return PalettedContainer.codec(
|
||||
biomeRegistry, biomeRegistry.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS));
|
||||
#elif MC_VER < MC_1_19_2
|
||||
return PalettedContainer.codec(
|
||||
biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getHolderOrThrow(Biomes.PLAINS));
|
||||
#elif MC_VER < MC_1_21_3
|
||||
return PalettedContainer.codecRW(
|
||||
biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getHolderOrThrow(Biomes.PLAINS));
|
||||
#elif MC_VER < MC_1_21_9
|
||||
return PalettedContainer.codecRW(
|
||||
biomeRegistry.asHolderIdMap(), biomeRegistry.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomeRegistry.getOrThrow(Biomes.PLAINS));
|
||||
#else
|
||||
return PalettedContainer.codecRW(
|
||||
biomeRegistry.holderByNameCodec(), PalettedContainerFactory.create(level.registryAccess()).biomeStrategy(), biomeRegistry.getOrThrow(Biomes.PLAINS));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//============//
|
||||
// heightmaps //
|
||||
//============//
|
||||
|
||||
private static boolean readHeightmaps(LevelChunk chunk, CompoundTag chunkData)
|
||||
{
|
||||
CompoundTag tagHeightmaps = CompoundTagUtil.getCompoundTag(chunkData, "Heightmaps");
|
||||
if (tagHeightmaps == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter())
|
||||
{
|
||||
String heightmapKey = type.getSerializationKey();
|
||||
|
||||
#if MC_VER < MC_1_21_5
|
||||
if (tagHeightmaps.contains(heightmapKey, 12))
|
||||
{
|
||||
chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmapKey));
|
||||
}
|
||||
#else
|
||||
if (tagHeightmaps.contains(heightmapKey))
|
||||
{
|
||||
Optional<long[]> optionalHeightmap = tagHeightmaps.getLongArray(heightmapKey);
|
||||
if (optionalHeightmap.isPresent())
|
||||
{
|
||||
chunk.setHeightmap(type, optionalHeightmap.get());
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// chunk lighting //
|
||||
//================//
|
||||
|
||||
/** source: https://minecraft.wiki/w/Chunk_format */
|
||||
public static CombinedChunkLightStorage readLight(ChunkAccess chunk, CompoundTag chunkData)
|
||||
{
|
||||
#if MC_VER <= MC_1_17_1
|
||||
// MC 1.16 and 1.17 doesn't have the necessary NBT info
|
||||
return null;
|
||||
#else
|
||||
|
||||
CombinedChunkLightStorage combinedStorage = new CombinedChunkLightStorage(ChunkWrapper.getInclusiveMinBuildHeight(chunk), ChunkWrapper.getExclusiveMaxBuildHeight(chunk));
|
||||
ChunkLightStorage blockLightStorage = combinedStorage.blockLightStorage;
|
||||
ChunkLightStorage skyLightStorage = combinedStorage.skyLightStorage;
|
||||
|
||||
boolean foundSkyLight = false;
|
||||
|
||||
|
||||
|
||||
//===================//
|
||||
// get NBT tags info //
|
||||
//===================//
|
||||
|
||||
Tag chunkSectionTags = chunkData.get("sections");
|
||||
if (chunkSectionTags == null)
|
||||
{
|
||||
if (!lightingSectionErrorLogged)
|
||||
{
|
||||
lightingSectionErrorLogged = true;
|
||||
LOGGER.error("No sections found for chunk at pos ["+chunk.getPos()+"] chunk data may be out of date.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else if (!(chunkSectionTags instanceof ListTag))
|
||||
{
|
||||
if (!lightingSectionErrorLogged)
|
||||
{
|
||||
lightingSectionErrorLogged = true;
|
||||
LOGGER.error("Chunk section tag list have unexpected type ["+chunkSectionTags.getClass().getName()+"], expected ["+ListTag.class.getName()+"].");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
ListTag chunkSectionListTag = (ListTag) chunkSectionTags;
|
||||
|
||||
|
||||
|
||||
//===================//
|
||||
// get lighting info //
|
||||
//===================//
|
||||
|
||||
for (int sectionIndex = 0; sectionIndex < chunkSectionListTag.size(); sectionIndex++)
|
||||
{
|
||||
Tag chunkSectionTag = chunkSectionListTag.get(sectionIndex);
|
||||
if (!(chunkSectionTag instanceof CompoundTag))
|
||||
{
|
||||
if (!lightingSectionErrorLogged)
|
||||
{
|
||||
lightingSectionErrorLogged = true;
|
||||
LOGGER.error("Chunk section tag has an unexpected type ["+chunkSectionTag.getClass().getName()+"], expected ["+CompoundTag.class.getName()+"].");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
CompoundTag chunkSectionCompoundTag = (CompoundTag) chunkSectionTag;
|
||||
|
||||
|
||||
// if null all lights = 0
|
||||
byte[] blockLightNibbleArray = CompoundTagUtil.getByteArray(chunkSectionCompoundTag, "BlockLight");
|
||||
byte[] skyLightNibbleArray = CompoundTagUtil.getByteArray(chunkSectionCompoundTag, "SkyLight");
|
||||
|
||||
if (blockLightNibbleArray != null
|
||||
&& skyLightNibbleArray != null)
|
||||
{
|
||||
// if any sky light was found then all lights above will be max brightness
|
||||
if (skyLightNibbleArray.length != 0)
|
||||
{
|
||||
foundSkyLight = true;
|
||||
}
|
||||
|
||||
for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++)
|
||||
{
|
||||
for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++)
|
||||
{
|
||||
// chunk sections are also 16 blocks tall
|
||||
for (int relY = 0; relY < LodUtil.CHUNK_WIDTH; relY++)
|
||||
{
|
||||
int blockPosIndex = relY*16*16 + relZ*16 + relX;
|
||||
byte blockLight = (blockLightNibbleArray.length == 0) ? 0 : getNibbleAtIndex(blockLightNibbleArray, blockPosIndex);
|
||||
byte skyLight = (skyLightNibbleArray.length == 0) ? 0 : getNibbleAtIndex(skyLightNibbleArray, blockPosIndex);
|
||||
if (skyLightNibbleArray.length == 0 && foundSkyLight)
|
||||
{
|
||||
skyLight = LodUtil.MAX_MC_LIGHT;
|
||||
}
|
||||
|
||||
int y = relY + (sectionIndex * LodUtil.CHUNK_WIDTH) + ChunkWrapper.getInclusiveMinBuildHeight(chunk);
|
||||
blockLightStorage.set(relX, y, relZ, blockLight);
|
||||
skyLightStorage.set(relX, y, relZ, skyLight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return combinedStorage;
|
||||
#endif
|
||||
}
|
||||
/** source: https://minecraft.wiki/w/Chunk_format#Block_Format */
|
||||
private static byte getNibbleAtIndex(byte[] arr, int index)
|
||||
{
|
||||
if (index % 2 == 0)
|
||||
{
|
||||
return (byte)(arr[index/2] & 0x0F);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (byte)((arr[index/2]>>4) & 0x0F);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=========//
|
||||
// logging //
|
||||
//=========//
|
||||
|
||||
private static void logBlockDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
|
||||
{
|
||||
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
|
||||
{
|
||||
LOGGER.warn("Unable to deserialize blocks for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
|
||||
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
||||
|
||||
return newMessage;
|
||||
});
|
||||
}
|
||||
private static void logBiomeDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
|
||||
{
|
||||
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
|
||||
{
|
||||
LOGGER.warn("Unable to deserialize biomes for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
|
||||
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
||||
|
||||
return newMessage;
|
||||
});
|
||||
}
|
||||
|
||||
private static void logParsingWarningOnce(String message) { logParsingWarningOnce(message, null); }
|
||||
private static void logParsingWarningOnce(String message, Exception e)
|
||||
{
|
||||
if (message == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
|
||||
{
|
||||
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
|
||||
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.",
|
||||
e);
|
||||
|
||||
return newMessage;
|
||||
});
|
||||
}
|
||||
|
||||
private static RuntimeException logErrorAndReturnException(String message)
|
||||
{
|
||||
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
|
||||
{
|
||||
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
|
||||
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
||||
|
||||
return newMessage;
|
||||
});
|
||||
|
||||
// Currently we want to ignore these errors, if returning null is a problem, we can change this later
|
||||
return null; //new RuntimeException(message);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
public static class CombinedChunkLightStorage
|
||||
{
|
||||
public ChunkLightStorage blockLightStorage;
|
||||
public ChunkLightStorage skyLightStorage;
|
||||
|
||||
public CombinedChunkLightStorage(int minY, int maxY)
|
||||
{
|
||||
this.blockLightStorage = ChunkLightStorage.createBlockLightStorage(minY, maxY);
|
||||
this.skyLightStorage = ChunkLightStorage.createSkyLightStorage(minY, maxY);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
-343
@@ -1,343 +0,0 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.GlobalWorldGenParams;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.RegionFileStorageExternalCache;
|
||||
import com.seibel.distanthorizons.core.config.Config;
|
||||
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.pos.DhChunkPos;
|
||||
import com.seibel.distanthorizons.core.util.ExceptionUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.chunk.UpgradeData;
|
||||
import net.minecraft.world.level.chunk.storage.IOWorker;
|
||||
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.channels.ClosedByInterruptException;
|
||||
import java.nio.channels.ClosedChannelException;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
#if MC_VER <= MC_1_17_1
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
#elif MC_VER <= MC_1_19_2
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.core.Registry;
|
||||
#elif MC_VER <= MC_1_19_4
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
#elif MC_VER <= MC_1_20_6
|
||||
import net.minecraft.core.registries.Registries;
|
||||
#elif MC_VER <= MC_1_21_3
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#elif MC_VER <= MC_1_21_8
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#elif MC_VER <= MC_1_21_9
|
||||
import net.minecraft.world.level.chunk.PalettedContainerFactory;
|
||||
#else
|
||||
import net.minecraft.world.level.chunk.PalettedContainerFactory;
|
||||
#endif
|
||||
|
||||
public class ChunkFileReader implements AutoCloseable
|
||||
{
|
||||
|
||||
public static final DhLogger LOGGER = new DhLoggerBuilder()
|
||||
.name("LOD World Gen")
|
||||
.fileLevelConfig(Config.Common.Logging.logWorldGenEventToFile)
|
||||
.build();
|
||||
|
||||
public static final DhLogger CHUNK_LOAD_LOGGER = new DhLoggerBuilder()
|
||||
.name("LOD Chunk Loading")
|
||||
.fileLevelConfig(Config.Common.Logging.logWorldGenChunkLoadEventToFile)
|
||||
.build();
|
||||
|
||||
private static final IModChecker MOD_CHECKER = SingletonInjector.INSTANCE.get(IModChecker.class);
|
||||
|
||||
public final GlobalWorldGenParams params;
|
||||
|
||||
/**
|
||||
* will be true if C2ME is installed (since they require us to
|
||||
* pull chunks using their async method), or if there
|
||||
* was an issue with the sync pulling method.
|
||||
*/
|
||||
private boolean pullExistingChunkUsingMcAsyncMethod = false;
|
||||
|
||||
private final AtomicReference<RegionFileStorageExternalCache> regionFileStorageCacheRef = new AtomicReference<>();
|
||||
public RegionFileStorageExternalCache getOrCreateRegionFileCache(RegionFileStorage storage)
|
||||
{
|
||||
RegionFileStorageExternalCache cache = this.regionFileStorageCacheRef.get();
|
||||
if (cache == null)
|
||||
{
|
||||
cache = new RegionFileStorageExternalCache(storage);
|
||||
if (!this.regionFileStorageCacheRef.compareAndSet(null, cache))
|
||||
{
|
||||
cache = this.regionFileStorageCacheRef.get();
|
||||
}
|
||||
}
|
||||
return cache;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public ChunkFileReader(GlobalWorldGenParams params)
|
||||
{
|
||||
this.params = params;
|
||||
|
||||
if (MOD_CHECKER.isModLoaded("c2me"))
|
||||
{
|
||||
LOGGER.info("C2ME detected: DH's pre-existing chunk accessing will use methods handled by C2ME.");
|
||||
this.pullExistingChunkUsingMcAsyncMethod = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
/**
|
||||
* If the given chunk pos already exists in the world, that chunk will be returned,
|
||||
* otherwise this will return an empty chunk.
|
||||
*/
|
||||
public CompletableFuture<ChunkWrapper> createEmptyOrPreExistingChunkWrapperAsync(
|
||||
int chunkX, int chunkZ,
|
||||
Map<DhChunkPos, ChunkLightStorage> chunkSkyLightingByDhPos,
|
||||
Map<DhChunkPos, ChunkLightStorage> chunkBlockLightingByDhPos,
|
||||
Map<DhChunkPos, ChunkWrapper> generatedChunkWrapperByDhPos)
|
||||
{
|
||||
ChunkPos chunkPos = new ChunkPos(chunkX, chunkZ);
|
||||
DhChunkPos dhChunkPos = new DhChunkPos(chunkX, chunkZ);
|
||||
|
||||
if (generatedChunkWrapperByDhPos.containsKey(dhChunkPos))
|
||||
{
|
||||
return CompletableFuture.completedFuture(generatedChunkWrapperByDhPos.get(dhChunkPos));
|
||||
}
|
||||
|
||||
return this.getChunkNbtDataAsync(chunkPos)
|
||||
.thenApply((CompoundTag chunkData) ->
|
||||
{
|
||||
ChunkWrapper newChunkWrapper = this.loadOrMakeChunkWrapper(chunkPos, chunkData);
|
||||
|
||||
// attempt to get chunk lighting
|
||||
ChunkCompoundTagParser.CombinedChunkLightStorage combinedLights = ChunkCompoundTagParser.readLight(newChunkWrapper.getChunk(), chunkData);
|
||||
if (combinedLights != null)
|
||||
{
|
||||
// may be empty, empty checks are handled later
|
||||
chunkSkyLightingByDhPos.put(dhChunkPos, combinedLights.skyLightStorage);
|
||||
chunkBlockLightingByDhPos.put(dhChunkPos, combinedLights.blockLightStorage);
|
||||
}
|
||||
|
||||
return newChunkWrapper;
|
||||
})
|
||||
// separate handle so we can cleanly handle missing chunks and/or thrown errors
|
||||
.handle((ChunkWrapper newChunkWrapper, Throwable throwable) ->
|
||||
{
|
||||
if (newChunkWrapper != null)
|
||||
{
|
||||
return newChunkWrapper;
|
||||
}
|
||||
else
|
||||
{
|
||||
return this.CreateProtoChunkWrapper(this.params.mcServerLevel, chunkPos);
|
||||
}
|
||||
})
|
||||
.thenApply((ChunkWrapper newChunkWrapper) ->
|
||||
{
|
||||
generatedChunkWrapperByDhPos.put(dhChunkPos, newChunkWrapper);
|
||||
return newChunkWrapper;
|
||||
});
|
||||
}
|
||||
private CompletableFuture<CompoundTag> getChunkNbtDataAsync(ChunkPos chunkPos)
|
||||
{
|
||||
ServerLevel level = this.params.mcServerLevel;
|
||||
|
||||
try
|
||||
{
|
||||
IOWorker ioWorker = level.getChunkSource().chunkMap.worker;
|
||||
|
||||
#if MC_VER <= MC_1_18_2
|
||||
return CompletableFuture.completedFuture(ioWorker.load(chunkPos));
|
||||
#else
|
||||
|
||||
// storage will be null if C2ME is installed
|
||||
if (!this.pullExistingChunkUsingMcAsyncMethod
|
||||
&& ioWorker.storage != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
RegionFileStorage storage = this.params.mcServerLevel.getChunkSource().chunkMap.worker.storage;
|
||||
RegionFileStorageExternalCache cache = this.getOrCreateRegionFileCache(storage);
|
||||
return CompletableFuture.completedFuture(cache.read(chunkPos));
|
||||
}
|
||||
catch (NullPointerException e)
|
||||
{
|
||||
// this shouldn't happen, if anything is null it should be
|
||||
// ioWorker.storage
|
||||
// but just in case
|
||||
LOGGER.error("Unexpected issue pulling pre-existing chunk ["+chunkPos+"], falling back to async chunk pulling. This may cause server-tick lag.", e);
|
||||
this.pullExistingChunkUsingMcAsyncMethod = true;
|
||||
|
||||
// try again now using the async method
|
||||
return this.getChunkNbtDataAsync(chunkPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// log if we unexpectedly weren't able to run the sync chunk pulling
|
||||
if (!this.pullExistingChunkUsingMcAsyncMethod)
|
||||
{
|
||||
// this shouldn't happen, but just in case
|
||||
LOGGER.info("Unable to pull pre-existing chunk using synchronous method. Falling back to async method. this may cause server-tick lag.");
|
||||
this.pullExistingChunkUsingMcAsyncMethod = true;
|
||||
}
|
||||
|
||||
//GET_CHUNK_COUNT_REF.incrementAndGet();
|
||||
|
||||
// When running in vanilla MC on versions before 1.21.4,
|
||||
// DH would attempt to run loadAsync on this same thread via a threading mixin,
|
||||
// to prevent causing lag on the server thread.
|
||||
// However, if a mod like C2ME is installed this will run on a C2ME thread instead.
|
||||
return ioWorker.loadAsync(chunkPos)
|
||||
.thenApply(optional ->
|
||||
{
|
||||
// Debugging note:
|
||||
// If there are reports of extreme memory use when C2ME is installed, that probably means
|
||||
// this method is queuing a lot of tasks (1,000+), which causes C2ME to explode.
|
||||
|
||||
//GET_CHUNK_COUNT_REF.decrementAndGet();
|
||||
//PREF_LOGGER.info("chunk getter count ["+F3Screen.NUMBER_FORMAT.format(GET_CHUNK_COUNT_REF.get())+"]");
|
||||
return optional.orElse(null);
|
||||
})
|
||||
.exceptionally((throwable) ->
|
||||
{
|
||||
// unwrap the CompletionException if necessary
|
||||
Throwable actualThrowable = throwable;
|
||||
while (actualThrowable instanceof CompletionException completionException)
|
||||
{
|
||||
actualThrowable = completionException.getCause();
|
||||
}
|
||||
|
||||
boolean isShutdownException = ExceptionUtil.isShutdownException(actualThrowable);
|
||||
if (!isShutdownException)
|
||||
{
|
||||
CHUNK_LOAD_LOGGER.warn("DistantHorizons: Couldn't load or make chunk ["+chunkPos+"], error: ["+actualThrowable.getMessage()+"].", actualThrowable);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
}
|
||||
#endif
|
||||
}
|
||||
catch (ClosedByInterruptException ignore)
|
||||
{
|
||||
// this just means the world generator is being shut down
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CHUNK_LOAD_LOGGER.warn("Couldn't load or make chunk [" + chunkPos + "]. Error: [" + e.getMessage() + "].", e);
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
}
|
||||
private ChunkWrapper loadOrMakeChunkWrapper(ChunkPos chunkPos, CompoundTag chunkTagData)
|
||||
{
|
||||
ServerLevel mcServerLevel = this.params.mcServerLevel;
|
||||
|
||||
if (chunkTagData == null)
|
||||
{
|
||||
return this.CreateProtoChunkWrapper(mcServerLevel, chunkPos);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
ChunkWrapper chunkWrapper = ChunkCompoundTagParser.createFromTag(mcServerLevel, this.params.dhServerLevel, chunkPos, chunkTagData);
|
||||
if (chunkWrapper == null)
|
||||
{
|
||||
chunkWrapper = this.CreateProtoChunkWrapper(mcServerLevel, chunkPos);
|
||||
}
|
||||
return chunkWrapper;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CHUNK_LOAD_LOGGER.error(
|
||||
"DistantHorizons: couldn't load or make chunk at [" + chunkPos + "]." +
|
||||
"Please try optimizing your world to fix this issue. \n" +
|
||||
"World optimization can be done from the singleplayer world selection screen.\n" +
|
||||
"Error: [" + e.getMessage() + "]."
|
||||
, e);
|
||||
|
||||
return this.CreateProtoChunkWrapper(mcServerLevel, chunkPos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public ChunkWrapper CreateProtoChunkWrapper(ServerLevel level, ChunkPos chunkPos)
|
||||
{
|
||||
ProtoChunk chunk = CreateProtoChunk(level, chunkPos);
|
||||
return new ChunkWrapper(chunk, this.params.dhServerLevel.getLevelWrapper());
|
||||
}
|
||||
public static ProtoChunk CreateProtoChunk(ServerLevel level, ChunkPos chunkPos)
|
||||
{
|
||||
#if MC_VER <= MC_1_16_5
|
||||
return new ProtoChunk(chunkPos, UpgradeData.EMPTY);
|
||||
#elif MC_VER <= MC_1_17_1
|
||||
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level);
|
||||
#elif MC_VER <= MC_1_19_2
|
||||
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY), null);
|
||||
#elif MC_VER <= MC_1_19_4
|
||||
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().registryOrThrow(Registries.BIOME), null);
|
||||
#elif MC_VER < MC_1_21_3
|
||||
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().registryOrThrow(Registries.BIOME), null);
|
||||
#elif MC_VER < MC_1_21_9
|
||||
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().lookupOrThrow(Registries.BIOME), null);
|
||||
#else
|
||||
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, PalettedContainerFactory.create(level.registryAccess()), null);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// base overrides //
|
||||
//================//
|
||||
|
||||
@Override
|
||||
public void close()
|
||||
{
|
||||
RegionFileStorageExternalCache regionStorage = this.regionFileStorageCacheRef.get();
|
||||
if (regionStorage != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
regionStorage.close();
|
||||
}
|
||||
catch (ClosedChannelException ignore) { /* world generator is being shut down */ }
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.error("Failed to close region file storage cache, error: ["+e.getMessage()+"].", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
-148
@@ -1,148 +0,0 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling;
|
||||
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.ListTag;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* these tag helpers are usedd to simplify tag accessing between MC versions
|
||||
*/
|
||||
public class CompoundTagUtil
|
||||
{
|
||||
|
||||
/** defaults to "false" if the tag isn't present */
|
||||
public static boolean getBoolean(CompoundTag tag, String key)
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
return tag.getBoolean(key);
|
||||
#else
|
||||
return tag.getBoolean(key).orElse(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** defaults to "0" if the tag isn't present */
|
||||
public static byte getByte(CompoundTag tag, String key)
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
return tag.getByte(key);
|
||||
#else
|
||||
return tag.getByte(key).orElse((byte)0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** defaults to "0" if the tag isn't present */
|
||||
public static short getShort(ListTag tag, int index)
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
return tag.getShort(index);
|
||||
#else
|
||||
return tag.getShort(index).orElse((short)0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** defaults to "0" if the tag isn't present */
|
||||
public static int getInt(CompoundTag tag, String key)
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
return tag.getInt(key);
|
||||
#else
|
||||
return tag.getInt(key).orElse(0);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** defaults to "0" if the tag isn't present */
|
||||
public static long getLong(CompoundTag tag, String key)
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
return tag.getInt(key);
|
||||
#else
|
||||
return tag.getLong(key).orElse(0L);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** defaults to null if the tag isn't present */
|
||||
@Nullable
|
||||
public static String getString(CompoundTag tag, String key)
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
return tag.getString(key);
|
||||
#else
|
||||
return tag.getString(key).orElse(null);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** defaults to null if the tag isn't present */
|
||||
@Nullable
|
||||
public static byte[] getByteArray(CompoundTag tag, String key)
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
return tag.getByteArray(key);
|
||||
#else
|
||||
return tag.getByteArray(key).orElse(null);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** defaults to null if the tag isn't present */
|
||||
@Nullable
|
||||
public static CompoundTag getCompoundTag(CompoundTag tag, String key)
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
return tag.getCompound(key);
|
||||
#else
|
||||
return tag.getCompound(key).orElse(null);
|
||||
#endif
|
||||
}
|
||||
/** defaults to null if the tag isn't present */
|
||||
@Nullable
|
||||
public static CompoundTag getCompoundTag(ListTag tag, int index)
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
return tag.getCompound(index);
|
||||
#else
|
||||
return tag.getCompound(index).orElse(null);
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* defaults to null if the tag isn't present
|
||||
* @param elementType unused after MC 1.21.5
|
||||
*/
|
||||
@Nullable
|
||||
public static ListTag getListTag(CompoundTag tag, String key, int elementType)
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
return tag.getList(key, elementType);
|
||||
#else
|
||||
return tag.getList(key).orElse(null);
|
||||
#endif
|
||||
}
|
||||
|
||||
/** defaults to null if the tag isn't present */
|
||||
@Nullable
|
||||
public static ListTag getListTag(ListTag tag, int index)
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
return tag.getList(index);
|
||||
#else
|
||||
return tag.getList(index).orElse(null);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static boolean contains(CompoundTag tag, String key, int index)
|
||||
{
|
||||
#if MC_VER < MC_1_21_5
|
||||
return tag.contains(key, index);
|
||||
#else
|
||||
return tag.contains(key);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+526
@@ -0,0 +1,526 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU GPL 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 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
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;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
|
||||
|
||||
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 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;
|
||||
#endif
|
||||
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;
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
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.*;
|
||||
import net.minecraft.world.level.chunk.storage.ChunkSerializer;
|
||||
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
|
||||
#endif
|
||||
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
import net.minecraft.world.level.material.Fluids;
|
||||
#endif
|
||||
|
||||
#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
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.ChunkType;
|
||||
#endif
|
||||
|
||||
import net.minecraft.world.level.material.Fluid;
|
||||
|
||||
|
||||
public class ChunkLoader
|
||||
{
|
||||
private static boolean zeroChunkPosErrorLogged = false;
|
||||
|
||||
#if MC_VER >= MC_1_19_2
|
||||
private static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
|
||||
#elif MC_VER >= MC_1_18_2
|
||||
private static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codec(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
|
||||
#endif
|
||||
private static final String TAG_UPGRADE_DATA = "UpgradeData";
|
||||
private static final String BLOCK_TICKS_TAG_18 = "block_ticks";
|
||||
private static final String FLUID_TICKS_TAG_18 = "fluid_ticks";
|
||||
private static final String BLOCK_TICKS_TAG_PRE18 = "TileTicks";
|
||||
private static final String FLUID_TICKS_TAG_PRE18 = "LiquidTicks";
|
||||
private static final ConfigBasedLogger LOGGER = BatchGenerationEnvironment.LOAD_LOGGER;
|
||||
|
||||
private static boolean lightingSectionErrorLogged = false;
|
||||
|
||||
|
||||
|
||||
//============//
|
||||
// read chunk //
|
||||
//============//
|
||||
|
||||
public static LevelChunk read(WorldGenLevel level, ChunkPos chunkPos, CompoundTag chunkData)
|
||||
{
|
||||
#if MC_VER < MC_1_18_2
|
||||
CompoundTag tagLevel = chunkData.getCompound("Level");
|
||||
#else
|
||||
CompoundTag tagLevel = chunkData;
|
||||
#endif
|
||||
|
||||
ChunkPos actualPos = new ChunkPos(tagLevel.getInt("xPos"), tagLevel.getInt("zPos"));
|
||||
if (!Objects.equals(chunkPos, actualPos))
|
||||
{
|
||||
#if MC_VER >= MC_1_18_2
|
||||
if (actualPos.equals(ChunkPos.ZERO))
|
||||
#else
|
||||
if (actualPos.equals(ChunkPos.INVALID_CHUNK_POS))
|
||||
#endif
|
||||
{
|
||||
if (!zeroChunkPosErrorLogged)
|
||||
{
|
||||
zeroChunkPosErrorLogged = true;
|
||||
|
||||
// explicit chunkPos toString is necessary otherwise the JDK 17 compiler breaks
|
||||
LOGGER.warn("Chunk file at ["+chunkPos.toString()+"] doesn't have a chunk pos. \n" +
|
||||
"This might happen if the world was created using an external program. \n" +
|
||||
"DH will attempt to parse the chunk anyway and won't log this message again.\n" +
|
||||
"If issues arise please try optimizing your world to fix this issue. \n" +
|
||||
"World optimization can be done from the singleplayer world selection screen."+
|
||||
"");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// everything is on one line to fix a JDK 17 compiler issue
|
||||
// if the issue is ever resolved, feel free to make this multi-line for readability
|
||||
LOGGER.error("Chunk file at ["+chunkPos.toString()+"] is in the wrong location. \nPlease try optimizing your world to fix this issue. \nWorld optimization can be done from the singleplayer world selection screen. \n(Expected pos: ["+chunkPos.toString()+"], actual ["+actualPos.toString()+"])");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_20_6
|
||||
ChunkStatus.ChunkType chunkType;
|
||||
#else
|
||||
ChunkType chunkType;
|
||||
#endif
|
||||
chunkType = readChunkType(tagLevel);
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
if (chunkType != ChunkStatus.ChunkType.LEVELCHUNK)
|
||||
return null;
|
||||
#else
|
||||
|
||||
BlendingData blendingData = readBlendingData(tagLevel);
|
||||
#if MC_VER < MC_1_19_2
|
||||
if (chunkType == ChunkStatus.ChunkType.PROTOCHUNK && (blendingData == null || !blendingData.oldNoise()))
|
||||
return null;
|
||||
#else
|
||||
if (chunkType == #if MC_VER < MC_1_20_6 ChunkStatus.ChunkType.PROTOCHUNK #else ChunkType.PROTOCHUNK #endif && blendingData == null)
|
||||
return null;
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
long inhabitedTime = tagLevel.getLong("InhabitedTime");
|
||||
|
||||
//================== Read params for making the LevelChunk ==================
|
||||
UpgradeData upgradeData = tagLevel.contains(TAG_UPGRADE_DATA, 10)
|
||||
? new UpgradeData(tagLevel.getCompound(TAG_UPGRADE_DATA)#if MC_VER >= MC_1_17_1 , level #endif )
|
||||
: UpgradeData.EMPTY;
|
||||
|
||||
boolean isLightOn = tagLevel.getBoolean("isLightOn");
|
||||
#if MC_VER < MC_1_18_2
|
||||
ChunkBiomeContainer chunkBiomeContainer = new ChunkBiomeContainer(
|
||||
level.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY)#if MC_VER >= MC_1_17_1 , level #endif ,
|
||||
chunkPos, level.getLevel().getChunkSource().getGenerator().getBiomeSource(),
|
||||
tagLevel.contains("Biomes", 11) ? tagLevel.getIntArray("Biomes") : null);
|
||||
|
||||
TickList<Block> blockTicks = tagLevel.contains(BLOCK_TICKS_TAG_PRE18, 9)
|
||||
? ChunkTickList.create(tagLevel.getList(BLOCK_TICKS_TAG_PRE18, 10), Registry.BLOCK::getKey, Registry.BLOCK::get)
|
||||
: new ProtoTickList<Block>(block -> (block == null || block.defaultBlockState().isAir()), chunkPos,
|
||||
tagLevel.getList("ToBeTicked", 9)#if MC_VER >= MC_1_17_1 , level #endif );
|
||||
|
||||
TickList<Fluid> fluidTicks = tagLevel.contains(FLUID_TICKS_TAG_PRE18, 9)
|
||||
? ChunkTickList.create(tagLevel.getList(FLUID_TICKS_TAG_PRE18, 10), Registry.FLUID::getKey, Registry.FLUID::get)
|
||||
: new ProtoTickList<Fluid>(fluid -> (fluid == null || fluid == Fluids.EMPTY), chunkPos,
|
||||
tagLevel.getList("LiquidsToBeTicked", 9)#if MC_VER >= MC_1_17_1 , level #endif );
|
||||
#else
|
||||
#if MC_VER < MC_1_19_4
|
||||
LevelChunkTicks<Block> blockTicks = LevelChunkTicks.load(tagLevel.getList(BLOCK_TICKS_TAG_18, 10),
|
||||
string -> Registry.BLOCK.getOptional(ResourceLocation.tryParse(string)), chunkPos);
|
||||
LevelChunkTicks<Fluid> fluidTicks = LevelChunkTicks.load(tagLevel.getList(FLUID_TICKS_TAG_18, 10),
|
||||
string -> Registry.FLUID.getOptional(ResourceLocation.tryParse(string)), chunkPos);
|
||||
#else
|
||||
LevelChunkTicks<Block> blockTicks = LevelChunkTicks.load(tagLevel.getList(BLOCK_TICKS_TAG_18, 10),
|
||||
(string -> BuiltInRegistries.BLOCK.getOptional(ResourceLocation.tryParse(string))), chunkPos);
|
||||
LevelChunkTicks<Fluid> fluidTicks = LevelChunkTicks.load(tagLevel.getList(FLUID_TICKS_TAG_18, 10),
|
||||
string -> BuiltInRegistries.FLUID.getOptional(ResourceLocation.tryParse(string)), chunkPos);
|
||||
#endif
|
||||
#endif
|
||||
|
||||
LevelChunkSection[] levelChunkSections = readSections(level, chunkPos, tagLevel);
|
||||
|
||||
// ====================== Make the chunk =========================
|
||||
#if MC_VER < MC_1_18_2
|
||||
LevelChunk chunk = new LevelChunk((Level) level.getLevel(), chunkPos, chunkBiomeContainer, upgradeData, blockTicks,
|
||||
fluidTicks, inhabitedTime, levelChunkSections, null);
|
||||
#else
|
||||
|
||||
LevelChunk chunk = new LevelChunk((Level) level, chunkPos, upgradeData, blockTicks,
|
||||
fluidTicks, inhabitedTime, levelChunkSections, null, blendingData);
|
||||
#endif
|
||||
// Set some states after object creation
|
||||
chunk.setLightCorrect(isLightOn);
|
||||
readHeightmaps(chunk, chunkData);
|
||||
readPostPocessings(chunk, chunkData);
|
||||
return chunk;
|
||||
}
|
||||
private static LevelChunkSection[] readSections(LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData)
|
||||
{
|
||||
#if MC_VER >= MC_1_18_2
|
||||
#if MC_VER < MC_1_19_4
|
||||
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
|
||||
#else
|
||||
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registries.BIOME);
|
||||
#endif
|
||||
#if MC_VER < MC_1_18_2
|
||||
Codec<PalettedContainer<Biome>> biomeCodec = PalettedContainer.codec(
|
||||
biomes, biomes.byNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
|
||||
#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));
|
||||
#else
|
||||
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
|
||||
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
|
||||
LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYIndex];
|
||||
|
||||
boolean isLightOn = chunkData.getBoolean("isLightOn");
|
||||
boolean hasSkyLight = level.dimensionType().hasSkyLight();
|
||||
ListTag tagSections = chunkData.getList("Sections", 10);
|
||||
if (tagSections.isEmpty()) tagSections = chunkData.getList("sections", 10);
|
||||
|
||||
for (int j = 0; j < tagSections.size(); ++j)
|
||||
{
|
||||
CompoundTag tagSection = tagSections.getCompound(j);
|
||||
int sectionYPos = tagSection.getByte("Y");
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
if (tagSection.contains("Palette", 9) && tagSection.contains("BlockStates", 12))
|
||||
{
|
||||
LevelChunkSection levelChunkSection = new LevelChunkSection(sectionYPos << 4);
|
||||
levelChunkSection.getStates().read(tagSection.getList("Palette", 10),
|
||||
tagSection.getLongArray("BlockStates"));
|
||||
levelChunkSection.recalcBlockCounts();
|
||||
if (!levelChunkSection.isEmpty())
|
||||
chunkSections[#if MC_VER < MC_1_17_1 sectionYPos #else level.getSectionIndexFromSectionY(sectionYPos) #endif ]
|
||||
= levelChunkSection;
|
||||
}
|
||||
#else
|
||||
int sectionId = level.getSectionIndexFromSectionY(sectionYPos);
|
||||
if (sectionId >= 0 && sectionId < chunkSections.length)
|
||||
{
|
||||
PalettedContainer<BlockState> blockStateContainer;
|
||||
#if MC_VER < MC_1_18_2
|
||||
PalettedContainer<Biome> biomeContainer;
|
||||
#else
|
||||
PalettedContainer<Holder<Biome>> biomeContainer;
|
||||
#endif
|
||||
|
||||
blockStateContainer = tagSection.contains("block_states", 10)
|
||||
? BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagSection.getCompound("block_states")).promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
|
||||
#if MC_VER < MC_1_20_6
|
||||
.getOrThrow(false, LOGGER::error)
|
||||
#else
|
||||
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null))
|
||||
#endif
|
||||
: new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
biomeContainer = tagSection.contains("biomes", 10)
|
||||
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, LOGGER::error)
|
||||
: new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
||||
#else
|
||||
|
||||
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)
|
||||
#else
|
||||
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null))
|
||||
#endif
|
||||
: new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(), biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
||||
#endif
|
||||
|
||||
#if MC_VER < MC_1_20_1
|
||||
chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer);
|
||||
#else
|
||||
chunkSections[sectionId] = new LevelChunkSection(blockStateContainer, biomeContainer);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
return chunkSections;
|
||||
}
|
||||
private static
|
||||
#if MC_VER < MC_1_20_6 ChunkStatus.ChunkType
|
||||
#elif MC_VER < MC_1_21_1 ChunkType
|
||||
#else ChunkType #endif
|
||||
readChunkType(CompoundTag tagLevel)
|
||||
{
|
||||
ChunkStatus chunkStatus = ChunkStatus.byName(tagLevel.getString("Status"));
|
||||
if (chunkStatus != null)
|
||||
{
|
||||
return chunkStatus.getChunkType();
|
||||
}
|
||||
|
||||
return
|
||||
#if MC_VER <= MC_1_20_4 ChunkStatus.ChunkType.PROTOCHUNK;
|
||||
#else ChunkType.PROTOCHUNK; #endif
|
||||
}
|
||||
private static void readHeightmaps(LevelChunk chunk, CompoundTag chunkData)
|
||||
{
|
||||
CompoundTag tagHeightmaps = chunkData.getCompound("Heightmaps");
|
||||
for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter())
|
||||
{
|
||||
String heightmap = type.getSerializationKey();
|
||||
if (tagHeightmaps.contains(heightmap, 12))
|
||||
chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmap));
|
||||
}
|
||||
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
|
||||
}
|
||||
private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData)
|
||||
{
|
||||
ListTag tagPostProcessings = chunkData.getList("PostProcessing", 9);
|
||||
for (int n = 0; n < tagPostProcessings.size(); ++n)
|
||||
{
|
||||
ListTag listTag3 = tagPostProcessings.getList(n);
|
||||
for (int o = 0; o < listTag3.size(); ++o)
|
||||
{
|
||||
chunk.addPackedPostProcess(listTag3.getShort(o), n);
|
||||
}
|
||||
}
|
||||
}
|
||||
#if MC_VER >= MC_1_18_2
|
||||
private static BlendingData readBlendingData(CompoundTag chunkData)
|
||||
{
|
||||
BlendingData blendingData = null;
|
||||
if (chunkData.contains("blending_data", 10))
|
||||
{
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data"));
|
||||
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial(LOGGER::error).orElse(null);
|
||||
}
|
||||
return blendingData;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
//=====================//
|
||||
// read chunk lighting //
|
||||
//=====================//
|
||||
|
||||
/**
|
||||
* https://minecraft.wiki/w/Chunk_format
|
||||
*/
|
||||
public static CombinedChunkLightStorage readLight(ChunkAccess chunk, CompoundTag chunkData)
|
||||
{
|
||||
#if MC_VER <= MC_1_17_1
|
||||
// MC 1.16 and 1.17 doesn't have the necessary NBT info
|
||||
return null;
|
||||
#else
|
||||
|
||||
CombinedChunkLightStorage combinedStorage = new CombinedChunkLightStorage(ChunkWrapper.getMinBuildHeight(chunk), ChunkWrapper.getMaxBuildHeight(chunk));
|
||||
ChunkLightStorage blockLightStorage = combinedStorage.blockLightStorage;
|
||||
ChunkLightStorage skyLightStorage = combinedStorage.skyLightStorage;
|
||||
|
||||
boolean foundSkyLight = false;
|
||||
|
||||
|
||||
|
||||
//===================//
|
||||
// get NBT tags info //
|
||||
//===================//
|
||||
|
||||
Tag chunkSectionTags = chunkData.get("sections");
|
||||
if (chunkSectionTags == null)
|
||||
{
|
||||
if (!lightingSectionErrorLogged)
|
||||
{
|
||||
lightingSectionErrorLogged = true;
|
||||
LOGGER.error("No sections found for chunk at pos ["+chunk.getPos()+"] chunk data may be out of date.");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
else if (!(chunkSectionTags instanceof ListTag))
|
||||
{
|
||||
if (!lightingSectionErrorLogged)
|
||||
{
|
||||
lightingSectionErrorLogged = true;
|
||||
LOGGER.error("Chunk section tag list have unexpected type ["+chunkSectionTags.getClass().getName()+"], expected ["+ListTag.class.getName()+"].");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
ListTag chunkSectionListTag = (ListTag) chunkSectionTags;
|
||||
|
||||
|
||||
|
||||
//===================//
|
||||
// get lighting info //
|
||||
//===================//
|
||||
|
||||
for (int sectionIndex = 0; sectionIndex < chunkSectionListTag.size(); sectionIndex++)
|
||||
{
|
||||
Tag chunkSectionTag = chunkSectionListTag.get(sectionIndex);
|
||||
if (!(chunkSectionTag instanceof CompoundTag))
|
||||
{
|
||||
if (!lightingSectionErrorLogged)
|
||||
{
|
||||
lightingSectionErrorLogged = true;
|
||||
LOGGER.error("Chunk section tag has an unexpected type ["+chunkSectionTag.getClass().getName()+"], expected ["+CompoundTag.class.getName()+"].");
|
||||
}
|
||||
return null;
|
||||
}
|
||||
CompoundTag chunkSectionCompoundTag = (CompoundTag) chunkSectionTag;
|
||||
|
||||
|
||||
// if null all lights = 0
|
||||
byte[] blockLightNibbleArray = chunkSectionCompoundTag.getByteArray("BlockLight");
|
||||
byte[] skyLightNibbleArray = chunkSectionCompoundTag.getByteArray("SkyLight");
|
||||
|
||||
// if any sky light was found then all lights above will be max brightness
|
||||
if (skyLightNibbleArray.length != 0)
|
||||
{
|
||||
foundSkyLight = true;
|
||||
}
|
||||
|
||||
for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++)
|
||||
{
|
||||
for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++)
|
||||
{
|
||||
// chunk sections are also 16 blocks tall
|
||||
for (int relY = 0; relY < LodUtil.CHUNK_WIDTH; relY++)
|
||||
{
|
||||
int blockPosIndex = relY*16*16 + relZ*16 + relX;
|
||||
byte blockLight = (blockLightNibbleArray.length == 0) ? 0 : getNibbleAtIndex(blockLightNibbleArray, blockPosIndex);
|
||||
byte skyLight = (skyLightNibbleArray.length == 0) ? 0 : getNibbleAtIndex(skyLightNibbleArray, blockPosIndex);
|
||||
if (skyLightNibbleArray.length == 0 && foundSkyLight)
|
||||
{
|
||||
skyLight = LodUtil.MAX_MC_LIGHT;
|
||||
}
|
||||
|
||||
int y = relY + (sectionIndex * LodUtil.CHUNK_WIDTH) + ChunkWrapper.getMinBuildHeight(chunk);
|
||||
blockLightStorage.set(relX, y, relZ, blockLight);
|
||||
skyLightStorage.set(relX, y, relZ, skyLight);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return combinedStorage;
|
||||
#endif
|
||||
}
|
||||
/** source: https://minecraft.wiki/w/Chunk_format#Block_Format */
|
||||
private static byte getNibbleAtIndex(byte[] arr, int index)
|
||||
{
|
||||
if (index % 2 == 0)
|
||||
{
|
||||
return (byte)(arr[index/2] & 0x0F);
|
||||
}
|
||||
else
|
||||
{
|
||||
return (byte)((arr[index/2]>>4) & 0x0F);
|
||||
}
|
||||
}
|
||||
|
||||
private static void logBlockDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
|
||||
{
|
||||
LOGGER.warn("Unable to deserialize blocks for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+message+"]. This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
||||
}
|
||||
private static void logBiomeDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
|
||||
{
|
||||
LOGGER.warn("Unable to deserialize biomes for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+message+"]. This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//================//
|
||||
// helper classes //
|
||||
//================//
|
||||
|
||||
public static class CombinedChunkLightStorage
|
||||
{
|
||||
public ChunkLightStorage blockLightStorage;
|
||||
public ChunkLightStorage skyLightStorage;
|
||||
|
||||
public CombinedChunkLightStorage(int minY, int maxY)
|
||||
{
|
||||
this.blockLightStorage = ChunkLightStorage.createBlockLightStorage(minY, maxY);
|
||||
this.skyLightStorage = ChunkLightStorage.createSkyLightStorage(minY, maxY);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-9
@@ -5,24 +5,19 @@ package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
|
||||
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
|
||||
+19
-60
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -28,7 +28,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.LodUtil;
|
||||
import net.minecraft.world.level.block.EntityBlock;
|
||||
import net.minecraft.world.level.block.SpawnerBlock;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -68,24 +68,16 @@ 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
|
||||
{
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
|
||||
|
||||
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;
|
||||
|
||||
@@ -130,7 +122,7 @@ 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);
|
||||
@@ -147,10 +139,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
|
||||
|
||||
#endif
|
||||
this.firstPos = chunkList.get(0).getPos();
|
||||
this.serverLevel = serverLevel;
|
||||
this.generator = generator;
|
||||
this.lightEngine = lightEngine;
|
||||
this.writeRadius = writeRadius;
|
||||
@@ -179,18 +170,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 +180,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,
|
||||
@@ -229,16 +193,8 @@ public class DhLitWorldGenRegion extends WorldGenRegion
|
||||
{
|
||||
ChunkAccess chunkAccess = this.getChunk(blockPos);
|
||||
if (chunkAccess instanceof LevelChunk)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_21_5
|
||||
chunkAccess.setBlockState(blockPos, blockState, /*isBlockMoving*/false);
|
||||
#else
|
||||
chunkAccess.setBlockState(blockPos, blockState, /*flags*/0);
|
||||
#endif
|
||||
|
||||
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))
|
||||
@@ -352,7 +308,7 @@ public class DhLitWorldGenRegion extends WorldGenRegion
|
||||
ChunkAccess chunk = this.getChunkAccess(chunkX, chunkZ, chunkStatus, returnNonNull);
|
||||
if (chunk instanceof LevelChunk)
|
||||
{
|
||||
chunk = new ImposterProtoChunk((LevelChunk) chunk #if MC_VER >= MC_1_18_2 ,/* allow writes */ false #endif );
|
||||
chunk = new ImposterProtoChunk((LevelChunk) chunk #if MC_VER >= MC_1_18_2 , true #endif );
|
||||
}
|
||||
return chunk;
|
||||
}
|
||||
@@ -383,7 +339,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!");
|
||||
@@ -392,11 +348,10 @@ public class DhLitWorldGenRegion extends WorldGenRegion
|
||||
}
|
||||
}
|
||||
|
||||
if (chunkStatus != ChunkStatus.EMPTY
|
||||
&& chunkStatus != debugTriggeredForStatus)
|
||||
if (chunkStatus != ChunkStatus.EMPTY && chunkStatus != debugTriggeredForStatus)
|
||||
{
|
||||
LOGGER.info("WorldGen requiring [" + chunkStatus + "]"
|
||||
+ " is outside the expected range. Returning EMPTY chunk.");
|
||||
LOGGER.info("WorldGen requiring " + chunkStatus
|
||||
+ " outside expected range detected. Force passing EMPTY chunk and seeing if it works.");
|
||||
debugTriggeredForStatus = chunkStatus;
|
||||
}
|
||||
|
||||
@@ -435,10 +390,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)
|
||||
{
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
|
||||
+35
-42
@@ -1,13 +1,13 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling.ChunkFileReader;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import net.minecraft.nbt.CompoundTag;
|
||||
import net.minecraft.nbt.NbtIo;
|
||||
import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.chunk.storage.RegionFile;
|
||||
import net.minecraft.world.level.chunk.storage.RegionFileStorage;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import java.io.DataInputStream;
|
||||
@@ -22,14 +22,14 @@ 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 DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
/** Can be null due to the C2ME mod */
|
||||
@Nullable
|
||||
@@ -46,16 +46,27 @@ 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; }
|
||||
|
||||
@Nullable
|
||||
public RegionFile getRegionFile(ChunkPos chunkPos) throws IOException
|
||||
public RegionFile getRegionFile(ChunkPos pos) throws IOException
|
||||
{
|
||||
if (this.storage == null)
|
||||
{
|
||||
@@ -70,8 +81,8 @@ public class RegionFileStorageExternalCache implements AutoCloseable
|
||||
|
||||
|
||||
|
||||
long chunkPosLong = ChunkPos.asLong(chunkPos.getRegionX(), chunkPos.getRegionZ());
|
||||
RegionFile regionFile = null;
|
||||
long posLong = ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ());
|
||||
RegionFile rFile = null;
|
||||
|
||||
// Check vanilla cache
|
||||
int retryCount = 0;
|
||||
@@ -85,7 +96,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
|
||||
this.getRegionFileLock.lock();
|
||||
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
regionFile = this.storage.getRegionFile(chunkPos);
|
||||
rFile = this.storage.getRegionFile(pos);
|
||||
|
||||
// keeping the region cache size low helps prevent concurrency issues
|
||||
if (this.storage.regionCache.size() > 150) // max 256
|
||||
@@ -97,7 +108,7 @@ public class RegionFileStorageExternalCache implements AutoCloseable
|
||||
}
|
||||
}
|
||||
#else
|
||||
regionFile = this.storage.regionCache.getOrDefault(chunkPosLong, null);
|
||||
rFile = this.storage.regionCache.getOrDefault(posLong, null);
|
||||
#endif
|
||||
|
||||
break;
|
||||
@@ -139,19 +150,19 @@ public class RegionFileStorageExternalCache implements AutoCloseable
|
||||
|
||||
if (retryCount >= maxRetryCount)
|
||||
{
|
||||
ChunkFileReader.CHUNK_LOAD_LOGGER.warn("Concurrency issue detected when getting region file for chunk at [" + chunkPos + "].");
|
||||
BatchGenerationEnvironment.LOAD_LOGGER.warn("Concurrency issue detected when getting region file for chunk at " + pos + ".");
|
||||
}
|
||||
|
||||
|
||||
if (regionFile != null)
|
||||
if (rFile != null)
|
||||
{
|
||||
return regionFile;
|
||||
return rFile;
|
||||
}
|
||||
|
||||
// Then check our custom cache
|
||||
for (RegionFileCache cache : this.regionFileCache)
|
||||
{
|
||||
if (cache.pos == chunkPosLong)
|
||||
if (cache.pos == posLong)
|
||||
{
|
||||
return cache.file;
|
||||
}
|
||||
@@ -170,22 +181,22 @@ public class RegionFileStorageExternalCache implements AutoCloseable
|
||||
return null;
|
||||
}
|
||||
|
||||
Path regionFilePath = storageFolderPath.resolve("r." + chunkPos.getRegionX() + "." + chunkPos.getRegionZ() + ".mca");
|
||||
Path regionFilePath = storageFolderPath.resolve("r." + pos.getRegionX() + "." + pos.getRegionZ() + ".mca");
|
||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||
regionFile = new RegionFile(regionFilePath.toFile(), storageFolderPath.toFile(), false);
|
||||
rFile = new RegionFile(regionFilePath.toFile(), storageFolderPath.toFile(), false);
|
||||
#elif MC_VER <= MC_1_20_4
|
||||
regionFile = new RegionFile(regionFilePath, storageFolderPath, false);
|
||||
rFile = new RegionFile(regionFilePath, storageFolderPath, false);
|
||||
#else
|
||||
regionFile = new RegionFile(new RegionStorageInfo("level", null, "level type"), regionFilePath, storageFolderPath, false);
|
||||
rFile = new RegionFile(new RegionStorageInfo("level", null, "level type"), regionFilePath, storageFolderPath, false);
|
||||
#endif
|
||||
|
||||
this.regionFileCache.add(new RegionFileCache(ChunkPos.asLong(chunkPos.getRegionX(), chunkPos.getRegionZ()), regionFile));
|
||||
this.regionFileCache.add(new RegionFileCache(ChunkPos.asLong(pos.getRegionX(), pos.getRegionZ()), rFile));
|
||||
while (this.regionFileCache.size() > MAX_CACHE_SIZE)
|
||||
{
|
||||
this.regionFileCache.poll().file.close();
|
||||
}
|
||||
|
||||
return regionFile;
|
||||
return rFile;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
|
||||
-124
@@ -1,124 +0,0 @@
|
||||
/*
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, version 3.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.params;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.WorldGenStructFeatManager;
|
||||
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
import net.minecraft.world.level.WorldGenLevel;
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.world.level.levelgen.structure.StructureCheck;
|
||||
#endif
|
||||
|
||||
public final class ThreadWorldGenParams
|
||||
{
|
||||
private static final ThreadLocal<ThreadWorldGenParams> LOCAL_PARAM_REF = new ThreadLocal<>();
|
||||
|
||||
|
||||
final ServerLevel level;
|
||||
public WorldGenStructFeatManager structFeatManager = null;
|
||||
|
||||
#if MC_VER >= MC_1_18_2
|
||||
public StructureCheck structCheck;
|
||||
#endif
|
||||
|
||||
boolean isValid = true;
|
||||
|
||||
// used for some older MC versions
|
||||
private static GlobalWorldGenParams previousGlobalWorldGenParams = null;
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public static ThreadWorldGenParams getOrMake(GlobalWorldGenParams globalParams)
|
||||
{
|
||||
ThreadWorldGenParams threadParam = LOCAL_PARAM_REF.get();
|
||||
if (threadParam != null
|
||||
&& threadParam.isValid
|
||||
&& threadParam.level == globalParams.mcServerLevel)
|
||||
{
|
||||
return threadParam;
|
||||
}
|
||||
|
||||
threadParam = new ThreadWorldGenParams(globalParams);
|
||||
LOCAL_PARAM_REF.set(threadParam);
|
||||
return threadParam;
|
||||
}
|
||||
|
||||
private ThreadWorldGenParams(GlobalWorldGenParams param)
|
||||
{
|
||||
previousGlobalWorldGenParams = param;
|
||||
|
||||
this.level = param.mcServerLevel;
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
this.structFeatManager = new WorldGenStructFeatManager(param.worldGenSettings, this.level);
|
||||
#elif MC_VER < MC_1_19_2
|
||||
this.structCheck = this.createStructureCheck(param);
|
||||
#else
|
||||
this.structCheck = new StructureCheck(param.chunkScanner, param.registry, param.structures,
|
||||
param.mcServerLevel.dimension(), param.generator, param.randomState, this.level, param.generator.getBiomeSource(), param.worldSeed,
|
||||
param.dataFixer);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
//==========//
|
||||
// builders //
|
||||
//==========//
|
||||
|
||||
public void makeStructFeatManager(WorldGenLevel genLevel, GlobalWorldGenParams param)
|
||||
{
|
||||
#if MC_VER < MC_1_18_2
|
||||
this.structFeatManager = new WorldGenStructFeatManager(param.worldGenSettings, genLevel);
|
||||
#elif MC_VER < MC_1_19_4
|
||||
this.structFeatManager = new WorldGenStructFeatManager(param.worldGenSettings, genLevel, this.structCheck);
|
||||
#else
|
||||
this.structFeatManager = new WorldGenStructFeatManager(param.worldOptions, genLevel, this.structCheck);
|
||||
#endif
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_18_2
|
||||
#elif MC_VER < MC_1_19_2
|
||||
public void recreateStructureCheck()
|
||||
{
|
||||
if (previousGlobalWorldGenParams != null)
|
||||
{
|
||||
this.structCheck = this.createStructureCheck(previousGlobalWorldGenParams);
|
||||
}
|
||||
}
|
||||
private StructureCheck createStructureCheck(GlobalWorldGenParams param)
|
||||
{
|
||||
return new StructureCheck(param.chunkScanner, param.registry, param.structures,
|
||||
param.mcServerLevel.dimension(), param.generator, this.level, param.generator.getBiomeSource(), param.worldSeed,
|
||||
param.dataFixer);
|
||||
}
|
||||
#else
|
||||
public void recreateStructureCheck() { /* do nothing */ }
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
}
|
||||
-54
@@ -1,54 +0,0 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
|
||||
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
#else
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
public abstract class AbstractWorldGenStep
|
||||
{
|
||||
public abstract void generateGroup(
|
||||
ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
|
||||
ArrayGridList<ChunkWrapper> chunkWrappers);
|
||||
|
||||
public abstract ChunkStatus getChunkStatus();
|
||||
|
||||
|
||||
|
||||
/** @return the list of chunks that have an earlier status and can be generated */
|
||||
protected ArrayList<ChunkWrapper> getChunkWrappersToGenerate(List<ChunkWrapper> chunkWrappers)
|
||||
{
|
||||
ArrayList<ChunkWrapper> chunkWrappersToGenerate = new ArrayList<>(chunkWrappers.size());
|
||||
|
||||
for (ChunkWrapper chunkWrapper : chunkWrappers)
|
||||
{
|
||||
ChunkAccess chunk = chunkWrapper.getChunk();
|
||||
if (chunkWrapper.getStatus().isOrAfter(this.getChunkStatus()))
|
||||
{
|
||||
// this chunk has already been generated up to this step
|
||||
continue;
|
||||
}
|
||||
else if (chunk instanceof ProtoChunk)
|
||||
{
|
||||
chunkWrapper.trySetStatus(this.getChunkStatus());
|
||||
chunkWrappersToGenerate.add(chunkWrapper);
|
||||
}
|
||||
}
|
||||
|
||||
return chunkWrappersToGenerate;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
+42
-57
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -20,14 +20,15 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
|
||||
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
|
||||
import net.minecraft.server.level.WorldGenRegion;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||
@@ -39,77 +40,61 @@ import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
public final class StepBiomes extends AbstractWorldGenStep
|
||||
public final class StepBiomes
|
||||
{
|
||||
private final BatchGenerationEnvironment environment;
|
||||
|
||||
public static final ChunkStatus STATUS = ChunkStatus.BIOMES;
|
||||
|
||||
private final BatchGenerationEnvironment environment;
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public StepBiomes(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// abstract methods //
|
||||
//==================//
|
||||
|
||||
@Override
|
||||
public ChunkStatus getChunkStatus() { return STATUS; }
|
||||
|
||||
@Override
|
||||
public void generateGroup(
|
||||
ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
|
||||
ArrayGridList<ChunkWrapper> chunkWrappers)
|
||||
ThreadedParameters tParams, WorldGenRegion worldGenRegion,
|
||||
List<ChunkWrapper> chunkWrappers)
|
||||
{
|
||||
ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
|
||||
for (ChunkWrapper chunkWrapper : chunksToDo)
|
||||
|
||||
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
|
||||
|
||||
for (ChunkWrapper chunkWrapper : chunkWrappers)
|
||||
{
|
||||
ChunkAccess chunk = chunkWrapper.getChunk();
|
||||
|
||||
|
||||
if (chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
{
|
||||
// this chunk has already generated this step
|
||||
continue;
|
||||
}
|
||||
else if (chunk instanceof ProtoChunk)
|
||||
{
|
||||
#if MC_VER < MC_1_21_1
|
||||
((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.globalParams.generator.createBiomes(this.environment.globalParams.biomes, chunk);
|
||||
this.environment.params.generator.createBiomes(this.environment.params.biomes, chunk);
|
||||
#elif MC_VER < MC_1_19_2
|
||||
chunk = this.environment.confirmFutureWasRunSynchronously(
|
||||
this.environment.globalParams.generator.createBiomes(
|
||||
this.environment.globalParams.biomes,
|
||||
Runnable::run,
|
||||
Blender.of(worldGenRegion),
|
||||
tParams.structFeatManager.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.globalParams.generator.createBiomes(
|
||||
this.environment.globalParams.biomes,
|
||||
Runnable::run,
|
||||
this.environment.globalParams.randomState, Blender.of(worldGenRegion),
|
||||
tParams.structFeatManager.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_1
|
||||
chunk = this.environment.confirmFutureWasRunSynchronously(
|
||||
this.environment.globalParams.generator.createBiomes(
|
||||
Runnable::run,
|
||||
this.environment.globalParams.randomState,
|
||||
Blender.of(worldGenRegion),
|
||||
tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
|
||||
chunk)
|
||||
);
|
||||
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.globalParams.generator.createBiomes(
|
||||
this.environment.globalParams.randomState,
|
||||
Blender.of(worldGenRegion),
|
||||
tParams.structFeatManager.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
|
||||
}
|
||||
}
|
||||
|
||||
+24
-29
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -21,14 +21,15 @@ package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
|
||||
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import net.minecraft.world.level.levelgen.Heightmap;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
@@ -36,13 +37,10 @@ import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.ConcurrentModificationException;
|
||||
|
||||
|
||||
public final class StepFeatures extends AbstractWorldGenStep
|
||||
public final class StepFeatures
|
||||
{
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
|
||||
public static final ChunkStatus STATUS = ChunkStatus.FEATURES;
|
||||
|
||||
@@ -50,41 +48,41 @@ public final class StepFeatures extends AbstractWorldGenStep
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public StepFeatures(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// abstract methods //
|
||||
//==================//
|
||||
|
||||
@Override
|
||||
public ChunkStatus getChunkStatus() { return STATUS; }
|
||||
|
||||
@Override
|
||||
public void generateGroup(
|
||||
ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
|
||||
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion,
|
||||
ArrayGridList<ChunkWrapper> chunkWrappers)
|
||||
{
|
||||
ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
|
||||
for (ChunkWrapper chunkWrapper : chunksToDo)
|
||||
for (ChunkWrapper chunkWrapper : chunkWrappers)
|
||||
{
|
||||
ChunkAccess chunk = chunkWrapper.getChunk();
|
||||
if (chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
{
|
||||
// this chunk has already generated this step
|
||||
continue;
|
||||
}
|
||||
else if (chunk instanceof ProtoChunk)
|
||||
{
|
||||
#if MC_VER < MC_1_21_1
|
||||
((ProtoChunk) chunk).setStatus(STATUS);
|
||||
#else
|
||||
((ProtoChunk) chunk).setPersistedStatus(STATUS);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
#if MC_VER < MC_1_18_2
|
||||
worldGenRegion.setOverrideCenter(chunk.getPos());
|
||||
environment.globalParams.generator.applyBiomeDecoration(worldGenRegion, tParams.structFeatManager);
|
||||
environment.params.generator.applyBiomeDecoration(worldGenRegion, tParams.structFeat);
|
||||
#else
|
||||
if (worldGenRegion.hasChunk(chunkWrapper.getChunkPos().getX(), chunkWrapper.getChunkPos().getZ()))
|
||||
{
|
||||
this.environment.globalParams.generator.applyBiomeDecoration(worldGenRegion, chunk, tParams.structFeatManager.forWorldGenRegion(worldGenRegion));
|
||||
this.environment.params.generator.applyBiomeDecoration(worldGenRegion, chunk, tParams.structFeat.forWorldGenRegion(worldGenRegion));
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -93,10 +91,7 @@ public final class StepFeatures extends AbstractWorldGenStep
|
||||
#endif
|
||||
|
||||
Heightmap.primeHeightmaps(chunk, STATUS.heightmapsAfter());
|
||||
}
|
||||
catch (ConcurrentModificationException e) // ReportedException
|
||||
{
|
||||
// TODO
|
||||
BatchGenerationEnvironment.clearDistantGenerationMixinData();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
+42
-50
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -20,14 +20,16 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
|
||||
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
|
||||
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
|
||||
import net.minecraft.server.level.WorldGenRegion;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
|
||||
#if MC_VER >= MC_1_18_2
|
||||
import net.minecraft.world.level.levelgen.blending.Blender;
|
||||
@@ -39,7 +41,7 @@ import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
public final class StepNoise extends AbstractWorldGenStep
|
||||
public final class StepNoise
|
||||
{
|
||||
private static final ChunkStatus STATUS = ChunkStatus.NOISE;
|
||||
|
||||
@@ -47,62 +49,52 @@ public final class StepNoise extends AbstractWorldGenStep
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public StepNoise(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// abstract methods //
|
||||
//==================//
|
||||
|
||||
@Override
|
||||
public ChunkStatus getChunkStatus() { return STATUS; }
|
||||
|
||||
@Override
|
||||
public void generateGroup(
|
||||
ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
|
||||
ArrayGridList<ChunkWrapper> chunkWrappers)
|
||||
ThreadedParameters tParams, WorldGenRegion worldGenRegion,
|
||||
List<ChunkWrapper> chunkWrappers)
|
||||
{
|
||||
ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
|
||||
for (ChunkWrapper chunkWrapper : chunksToDo)
|
||||
|
||||
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
|
||||
|
||||
for (ChunkWrapper chunkWrapper : chunkWrappers)
|
||||
{
|
||||
ChunkAccess chunk = chunkWrapper.getChunk();
|
||||
if (chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_17_1
|
||||
this.environment.globalParams.generator.fillFromNoise(worldGenRegion, tParams.structFeatManager, chunk);
|
||||
#elif MC_VER < MC_1_18_2
|
||||
chunk = this.environment.confirmFutureWasRunSynchronously(
|
||||
this.environment.globalParams.generator.fillFromNoise(
|
||||
Runnable::run,
|
||||
tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
|
||||
chunk));
|
||||
#elif MC_VER < MC_1_19_2
|
||||
chunk = this.environment.confirmFutureWasRunSynchronously(
|
||||
this.environment.globalParams.generator.fillFromNoise(
|
||||
Runnable::run,
|
||||
Blender.of(worldGenRegion),
|
||||
tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
|
||||
chunk));
|
||||
#elif MC_VER < MC_1_21_1
|
||||
chunk = this.environment.confirmFutureWasRunSynchronously(
|
||||
this.environment.globalParams.generator.fillFromNoise(
|
||||
Runnable::run,
|
||||
Blender.of(worldGenRegion),
|
||||
this.environment.globalParams.randomState,
|
||||
tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
|
||||
chunk));
|
||||
#if MC_VER < MC_1_21_1
|
||||
((ProtoChunk) chunk).setStatus(STATUS);
|
||||
#else
|
||||
chunk = this.environment.confirmFutureWasRunSynchronously(
|
||||
this.environment.globalParams.generator.fillFromNoise(
|
||||
Blender.of(worldGenRegion),
|
||||
this.environment.globalParams.randomState,
|
||||
tParams.structFeatManager.forWorldGenRegion(worldGenRegion),
|
||||
chunk));
|
||||
((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.joinSync(this.environment.params.generator.fillFromNoise(Runnable::run,
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
|
||||
#elif MC_VER < MC_1_19_2
|
||||
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_1
|
||||
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.joinSync(this.environment.params.generator.fillFromNoise(Blender.of(worldGenRegion), this.environment.params.randomState,
|
||||
tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk));
|
||||
#endif
|
||||
UncheckedInterruptedException.throwIfInterrupted();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+32
-22
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -20,14 +20,15 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
|
||||
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
|
||||
import net.minecraft.server.level.WorldGenRegion;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
@@ -36,7 +37,7 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
|
||||
public final class StepStructureReference extends AbstractWorldGenStep
|
||||
public final class StepStructureReference
|
||||
{
|
||||
private static final ChunkStatus STATUS = ChunkStatus.STRUCTURE_REFERENCES;
|
||||
|
||||
@@ -44,31 +45,40 @@ public final class StepStructureReference extends AbstractWorldGenStep
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public StepStructureReference(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// abstract methods //
|
||||
//==================//
|
||||
|
||||
@Override
|
||||
public ChunkStatus getChunkStatus() { return STATUS; }
|
||||
|
||||
@Override
|
||||
public void generateGroup(
|
||||
ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
|
||||
ArrayGridList<ChunkWrapper> chunkWrappers)
|
||||
ThreadedParameters tParams, WorldGenRegion worldGenRegion,
|
||||
List<ChunkWrapper> chunkWrappers)
|
||||
{
|
||||
ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
|
||||
for (ChunkWrapper chunkWrapper : chunksToDo)
|
||||
|
||||
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
|
||||
|
||||
for (ChunkWrapper chunkWrapper : chunkWrappers)
|
||||
{
|
||||
ChunkAccess chunk = chunkWrapper.getChunk();
|
||||
this.environment.globalParams.generator.createReferences(worldGenRegion, tParams.structFeatManager.forWorldGenRegion(worldGenRegion), chunk);
|
||||
if (chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
{
|
||||
// this chunk has already generated this step
|
||||
continue;
|
||||
}
|
||||
else if (chunk instanceof ProtoChunk)
|
||||
{
|
||||
#if MC_VER < MC_1_21_1
|
||||
((ProtoChunk) chunk).setStatus(STATUS);
|
||||
#else
|
||||
((ProtoChunk) chunk).setPersistedStatus(STATUS);
|
||||
#endif
|
||||
chunksToDo.add(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
for (ChunkAccess chunk : chunksToDo)
|
||||
{
|
||||
// System.out.println("StepStructureReference: "+chunk.getPos());
|
||||
this.environment.params.generator.createReferences(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+91
-74
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -20,17 +20,18 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.locks.ReentrantLock;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
|
||||
import net.minecraft.server.level.WorldGenRegion;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import com.seibel.distanthorizons.core.logging.DhLogger;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
@@ -39,9 +40,9 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
|
||||
public final class StepStructureStart extends AbstractWorldGenStep
|
||||
public final class StepStructureStart
|
||||
{
|
||||
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
|
||||
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
|
||||
private static final ChunkStatus STATUS = ChunkStatus.STRUCTURE_STARTS;
|
||||
private static final ReentrantLock STRUCTURE_PLACEMENT_LOCK = new ReentrantLock();
|
||||
|
||||
@@ -49,97 +50,113 @@ public final class StepStructureStart extends AbstractWorldGenStep
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public StepStructureStart(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// abstract methods //
|
||||
//==================//
|
||||
|
||||
@Override
|
||||
public ChunkStatus getChunkStatus() { return STATUS; }
|
||||
|
||||
@Override
|
||||
public void generateGroup(
|
||||
ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
|
||||
ArrayGridList<ChunkWrapper> chunkWrappers)
|
||||
public static class StructStartCorruptedException extends RuntimeException
|
||||
{
|
||||
ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
|
||||
private static final long serialVersionUID = -8987434342051563358L;
|
||||
|
||||
// TODO should be put in wrapped environment so we can skip some other world gen steps
|
||||
// SURFACE wouldn't need structure generation either
|
||||
#if MC_VER < MC_1_19_2
|
||||
if (!this.environment.globalParams.worldGenSettings.generateFeatures())
|
||||
#elif MC_VER < MC_1_19_4
|
||||
if (!this.environment.globalParams.worldGenSettings.generateStructures())
|
||||
#else
|
||||
if (!this.environment.globalParams.worldOptions.generateStructures())
|
||||
#endif
|
||||
public StructStartCorruptedException(ArrayIndexOutOfBoundsException e)
|
||||
{
|
||||
return;
|
||||
super("StructStartCorruptedException");
|
||||
super.initCause(e);
|
||||
fillInStackTrace();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void generateGroup(
|
||||
ThreadedParameters tParams, WorldGenRegion worldGenRegion,
|
||||
List<ChunkWrapper> chunkWrappers) throws InterruptedException
|
||||
{
|
||||
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
|
||||
|
||||
|
||||
for (ChunkWrapper chunkWrapper : chunksToDo)
|
||||
for (ChunkWrapper chunkWrapper : chunkWrappers)
|
||||
{
|
||||
ChunkAccess chunk = chunkWrapper.getChunk();
|
||||
|
||||
// hopefully this shouldn't cause any performance issues (this step is generally quite quick so hopefully it should be fine)
|
||||
// and should prevent some concurrency issues
|
||||
STRUCTURE_PLACEMENT_LOCK.lock();
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
this.environment.globalParams.generator.createStructures(this.environment.globalParams.registry, tParams.structFeatManager, chunk, this.environment.globalParams.structures,
|
||||
this.environment.globalParams.worldSeed);
|
||||
#elif MC_VER < MC_1_19_4
|
||||
this.environment.globalParams.generator.createStructures(this.environment.globalParams.registry, this.environment.globalParams.randomState, tParams.structFeatManager, chunk, this.environment.globalParams.structures,
|
||||
this.environment.globalParams.worldSeed);
|
||||
#elif MC_VER <= MC_1_21_3
|
||||
this.environment.globalParams.generator.createStructures(this.environment.globalParams.registry,
|
||||
this.environment.globalParams.mcServerLevel.getChunkSource().getGeneratorState(),
|
||||
tParams.structFeatManager, chunk, this.environment.globalParams.structures);
|
||||
#else
|
||||
this.environment.globalParams.generator.createStructures(this.environment.globalParams.registry,
|
||||
this.environment.globalParams.mcServerLevel.getChunkSource().getGeneratorState(),
|
||||
tParams.structFeatManager, chunk, this.environment.globalParams.structures,
|
||||
this.environment.globalParams.mcServerLevel.dimension());
|
||||
#endif
|
||||
|
||||
#if MC_VER >= MC_1_18_2
|
||||
try
|
||||
if (chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
{
|
||||
tParams.structCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts());
|
||||
// this chunk has already generated this step
|
||||
continue;
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException firstEx)
|
||||
else if (chunk instanceof ProtoChunk)
|
||||
{
|
||||
// There's a rare issue with StructStart where it throws ArrayIndexOutOfBounds
|
||||
// This means the structFeat is corrupted (For some reason) and I need to reset it.
|
||||
// TODO: Figure out in the future why this happens even though I am using new structFeat - OLD
|
||||
#if MC_VER < MC_1_21_1
|
||||
((ProtoChunk) chunk).setStatus(STATUS);
|
||||
#else
|
||||
((ProtoChunk) chunk).setPersistedStatus(STATUS);
|
||||
#endif
|
||||
chunksToDo.add(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
if (this.environment.params.worldGenSettings.generateFeatures())
|
||||
{
|
||||
#elif MC_VER < MC_1_19_4
|
||||
if (this.environment.params.worldGenSettings.generateStructures()) {
|
||||
#else
|
||||
if (this.environment.params.worldOptions.generateStructures())
|
||||
{
|
||||
#endif
|
||||
for (ChunkAccess chunk : chunksToDo)
|
||||
{
|
||||
// System.out.println("StepStructureStart: "+chunk.getPos());
|
||||
|
||||
// reset the structureStart
|
||||
tParams.recreateStructureCheck();
|
||||
// there are a few cases where the structure generator call may lock up (either due to teleporting or leaving the world).
|
||||
// hopefully allowing interrupts here will prevent that from happening.
|
||||
BatchGenerationEnvironment.throwIfThreadInterrupted();
|
||||
|
||||
// hopefully this shouldn't cause any performance issues (this step is generally quite quick so hopefully it should be fine)
|
||||
// and should prevent some concurrency issues
|
||||
STRUCTURE_PLACEMENT_LOCK.lock();
|
||||
|
||||
#if MC_VER < MC_1_19_2
|
||||
environment.params.generator.createStructures(environment.params.registry, tParams.structFeat, chunk, environment.params.structures,
|
||||
environment.params.worldSeed);
|
||||
#elif MC_VER < MC_1_19_4
|
||||
environment.params.generator.createStructures(environment.params.registry, environment.params.randomState, tParams.structFeat, chunk, environment.params.structures,
|
||||
environment.params.worldSeed);
|
||||
#else
|
||||
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
|
||||
try
|
||||
{
|
||||
// try running the structure logic again
|
||||
tParams.structCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts());
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException secondEx)
|
||||
catch (ArrayIndexOutOfBoundsException firstEx)
|
||||
{
|
||||
// the structure logic failed again, log it and move on
|
||||
LOGGER.error("Unable to create structure starts for " + chunk.getPos() + ". This is an error with MC's world generation. Ignoring and continuing generation. Error: " + secondEx.getMessage()); // don't log the full stack trace since it is long and will generally end up in MC's code
|
||||
// There's a rare issue with StructStart where it throws ArrayIndexOutOfBounds
|
||||
// This means the structFeat is corrupted (For some reason) and I need to reset it.
|
||||
// TODO: Figure out in the future why this happens even though I am using new structFeat - OLD
|
||||
|
||||
// reset the structureStart
|
||||
tParams.recreateStructureCheck();
|
||||
|
||||
try
|
||||
{
|
||||
// try running the structure logic again
|
||||
tParams.structCheck.onStructureLoad(chunk.getPos(), chunk.getAllStarts());
|
||||
}
|
||||
catch (ArrayIndexOutOfBoundsException secondEx)
|
||||
{
|
||||
// the structure logic failed again, log it and move on
|
||||
LOGGER.error("Unable to create structure starts for " + chunk.getPos() + ". This is an error with MC's world generation. Ignoring and continuing generation. Error: " + secondEx.getMessage()); // don't log the full stack trace since it is long and will generally end up in MC's code
|
||||
|
||||
//throw new StepStructureStart.StructStartCorruptedException(secondEx);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
STRUCTURE_PLACEMENT_LOCK.unlock();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
STRUCTURE_PLACEMENT_LOCK.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+34
-27
@@ -2,7 +2,7 @@
|
||||
* This file is part of the Distant Horizons mod
|
||||
* licensed under the GNU LGPL v3 License.
|
||||
*
|
||||
* Copyright (C) 2020 James Seibel
|
||||
* 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
|
||||
@@ -20,14 +20,15 @@
|
||||
package com.seibel.distanthorizons.common.wrappers.worldGeneration.step;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.ThreadWorldGenParams;
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.ThreadedParameters;
|
||||
|
||||
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.DhLitWorldGenRegion;
|
||||
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
|
||||
import net.minecraft.server.level.WorldGenRegion;
|
||||
import net.minecraft.world.level.chunk.ChunkAccess;
|
||||
import net.minecraft.world.level.chunk.ProtoChunk;
|
||||
|
||||
#if MC_VER <= MC_1_20_4
|
||||
import net.minecraft.world.level.chunk.ChunkStatus;
|
||||
@@ -36,7 +37,7 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
|
||||
#endif
|
||||
|
||||
|
||||
public final class StepSurface extends AbstractWorldGenStep
|
||||
public final class StepSurface
|
||||
{
|
||||
private static final ChunkStatus STATUS = ChunkStatus.SURFACE;
|
||||
|
||||
@@ -44,41 +45,47 @@ public final class StepSurface extends AbstractWorldGenStep
|
||||
|
||||
|
||||
|
||||
//=============//
|
||||
// constructor //
|
||||
//=============//
|
||||
|
||||
public StepSurface(BatchGenerationEnvironment batchGenerationEnvironment) { this.environment = batchGenerationEnvironment; }
|
||||
|
||||
|
||||
|
||||
//==================//
|
||||
// abstract methods //
|
||||
//==================//
|
||||
|
||||
@Override
|
||||
public ChunkStatus getChunkStatus() { return STATUS; }
|
||||
|
||||
@Override
|
||||
public void generateGroup(
|
||||
ThreadWorldGenParams tParams, DhLitWorldGenRegion worldGenRegion,
|
||||
ArrayGridList<ChunkWrapper> chunkWrappers)
|
||||
ThreadedParameters tParams, WorldGenRegion worldGenRegion,
|
||||
List<ChunkWrapper> chunkWrappers)
|
||||
{
|
||||
ArrayList<ChunkWrapper> chunksToDo = this.getChunkWrappersToGenerate(chunkWrappers);
|
||||
for (ChunkWrapper chunkWrapper : chunksToDo)
|
||||
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
|
||||
|
||||
for (ChunkWrapper chunkWrapper : chunkWrappers)
|
||||
{
|
||||
ChunkAccess chunk = chunkWrapper.getChunk();
|
||||
|
||||
if (chunkWrapper.getStatus().isOrAfter(STATUS))
|
||||
{
|
||||
// this chunk has already generated this step
|
||||
continue;
|
||||
}
|
||||
else if (chunk instanceof ProtoChunk)
|
||||
{
|
||||
#if MC_VER < MC_1_21_1
|
||||
((ProtoChunk) chunk).setStatus(STATUS);
|
||||
#else
|
||||
((ProtoChunk) chunk).setPersistedStatus(STATUS);
|
||||
#endif
|
||||
|
||||
chunksToDo.add(chunk);
|
||||
}
|
||||
}
|
||||
|
||||
for (ChunkAccess chunk : chunksToDo)
|
||||
{
|
||||
// System.out.println("StepSurface: "+chunk.getPos());
|
||||
#if MC_VER < MC_1_18_2
|
||||
this.environment.globalParams.generator.buildSurfaceAndBedrock(worldGenRegion, chunk);
|
||||
environment.params.generator.buildSurfaceAndBedrock(worldGenRegion, chunk);
|
||||
#elif MC_VER < MC_1_19_2
|
||||
this.environment.globalParams.generator.buildSurface(worldGenRegion, tParams.structFeatManager.forWorldGenRegion(worldGenRegion), chunk);
|
||||
environment.params.generator.buildSurface(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), chunk);
|
||||
#else
|
||||
this.environment.globalParams.generator.buildSurface(worldGenRegion, tParams.structFeatManager.forWorldGenRegion(worldGenRegion), this.environment.globalParams.randomState, chunk);
|
||||
environment.params.generator.buildSurface(worldGenRegion, tParams.structFeat.forWorldGenRegion(worldGenRegion), environment.params.randomState, chunk);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -18,6 +16,7 @@ accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chu
|
||||
# used for grabbing vanilla rendered chunks
|
||||
accessible class net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo
|
||||
accessible field net/minecraft/client/renderer/LevelRenderer$RenderChunkInfo chunk Lnet/minecraft/client/renderer/chunk/ChunkRenderDispatcher$RenderChunk;
|
||||
#accessible field net/minecraft/world/entity/Entity blockPosition Lnet/minecraft/core/BlockPos;
|
||||
|
||||
# lighting
|
||||
accessible field net/minecraft/client/renderer/LightTexture lightPixels Lcom/mojang/blaze3d/platform/NativeImage;
|
||||
@@ -29,10 +28,9 @@ accessible field net/minecraft/world/level/lighting/LevelLightEngine skyEngine L
|
||||
accessible method net/minecraft/world/level/levelgen/Heightmap setHeight (III)V
|
||||
accessible field net/minecraft/world/level/biome/Biome generationSettings Lnet/minecraft/world/level/biome/BiomeGenerationSettings;
|
||||
accessible field net/minecraft/world/level/biome/Biome biomeCategory Lnet/minecraft/world/level/biome/Biome$BiomeCategory;
|
||||
#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;
|
||||
|
||||
@@ -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
|
||||
@@ -29,12 +28,9 @@ accessible field net/minecraft/world/level/lighting/LevelLightEngine skyEngine L
|
||||
accessible method net/minecraft/world/level/levelgen/Heightmap setHeight (III)V
|
||||
accessible field net/minecraft/world/level/biome/Biome generationSettings Lnet/minecraft/world/level/biome/BiomeGenerationSettings;
|
||||
accessible field net/minecraft/world/level/biome/Biome biomeCategory Lnet/minecraft/world/level/biome/Biome$BiomeCategory;
|
||||
# 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;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user