Compare commits

..

1 Commits

Author SHA1 Message Date
s809 a442a1f3ca Test build 2025-01-19 21:23:01 +05:00
141 changed files with 2064 additions and 4719 deletions
+2 -1
View File
@@ -5,6 +5,7 @@ root = true
[*]
charset = utf-8
end_of_line = crlf
indent_size = 4
indent_style = space
insert_final_newline = false
@@ -687,7 +688,7 @@ ij_markdown_wrap_text_inside_blockquotes = true
ij_toml_keep_indents_on_empty_lines = false
[{*.yaml,*.yml}]
indent_size = 4
indent_size = 2
ij_yaml_align_values_properties = do_not_align
ij_yaml_autoinsert_sequence_marker = true
ij_yaml_block_mapping_on_new_line = false
-4
View File
@@ -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
+25 -19
View File
@@ -35,28 +35,34 @@ build:
stage: build
parallel:
matrix:
- MC_VER: [
"1.21.10", "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", "1.21.3", "1.21.4"]
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
+3 -3
View File
@@ -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 -1
View File
@@ -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**:
+62 -82
View File
@@ -1,11 +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
plugins {
id "java"
@@ -13,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.11-SNAPSHOT" apply false
id "dev.architectury.loom" version "1.7-SNAPSHOT" apply false
}
@@ -42,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");
}
}
@@ -69,64 +59,41 @@ 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 {
String loaderHyphenSeparatedList = ((String)gradle.builds_for).replaceAll(",", "-");
group = "com.seibel.distanthorizons"
mergedJarName = "DistantHorizons-${rootProject.versionStr}-${loaderHyphenSeparatedList}.jar"
class NativeTransformer implements Transformer {
private final HashMap<String, String> replacements = new HashMap()
private final HashMap<String, byte[]> rewrittenFiles = new HashMap()
private var nativeRelocator
public File rootDir
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 replacements.keySet().stream().anyMatch {
element.name.startsWith(it as String)
}
}
@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 {
Map.Entry<String, String> pathReplacement = replacements.entrySet().stream().filter {
context.path.startsWith(it.key as String)
}.findFirst().orElseThrow()
String path = context.path.replace(pathReplacement.key as String, pathReplacement.value as String)
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 ->
@@ -247,8 +214,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:1.5.7-4")
// Compression
forgeShadowMe("org.lz4:lz4-java:${rootProject.lz4_version}") // LZ4
@@ -340,20 +306,26 @@ subprojects { p ->
// 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
relocateNative "org/sqlite", "dh_sqlite"
relocateNative "org_sqlite", "dh_1sqlite"
}
// // Sqlite Database
// // James can't determine how to relocate the library correctly so this is commented out
// relocate ("org.sqlite", "${librariesLocation}.sqlite") {
// exclude("org/sqlite/core/NativeDB/**")
//
// exclude("org/sqlite/native/FreeBSD/**")
// exclude("org/sqlite/native/Linux-Android/**")
// exclude("org/sqlite/native/Linux-Musl/**")
// exclude("org/sqlite/native/Linux/arm/**")
// exclude("org/sqlite/native/Linux/aarch64/**")
// exclude("org/sqlite/native/Linux/armv6/**")
// exclude("org/sqlite/native/Linux/x86/**")
// exclude("org/sqlite/native/Linux/armv7/**")
// exclude("org/sqlite/native/Linux/ppc64/**")
// exclude("org/sqlite/native/Linux/riscv64/**")
// exclude("org/sqlite/native/Windows/armv7/**")
// exclude("org/sqlite/native/Windows/aarch64/**")
// exclude("org/sqlite/native/Windows/armv7/**")
// }
// JOML
@@ -448,8 +420,6 @@ subprojects { p ->
fabric_incompatibility_list : fabric_incompatibility_list,
fabric_recommend_list : fabric_recommend_list,
neoforge_version_range : neoforge_version_range,
]
// replace any properties in the sub-projects with the values defined here
@@ -530,9 +500,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
@@ -697,3 +666,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)
+2 -2
View File
@@ -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
View File
@@ -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
-214
View File
@@ -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);
}
}
@@ -5,7 +5,6 @@ import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiAfterDh
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.core.api.internal.SharedApi;
@@ -67,7 +66,7 @@ public abstract class AbstractModInitializer
{
DependencySetup.createClientBindings();
LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " client, firing DhApiBeforeDhInitEvent...");
LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " client.");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
this.startup();
@@ -78,18 +77,13 @@ public abstract class AbstractModInitializer
this.initializeModCompat();
LOGGER.info(ModInfo.READABLE_NAME + " client 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_10
// debug screen rendering handled via a mixin
#else
DhDebugScreenEntry.register();
#endif
this.subscribeClientStartedEvent(this::postInit);
}
@@ -97,7 +91,7 @@ public abstract class AbstractModInitializer
{
DependencySetup.createServerBindings();
LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " server, firing DhApiBeforeDhInitEvent event...");
LOGGER.info("Initializing " + ModInfo.READABLE_NAME + " server.");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiBeforeDhInitEvent.class, null);
this.startup();
@@ -112,7 +106,8 @@ public abstract class AbstractModInitializer
this.initializeModCompat();
LOGGER.info(ModInfo.READABLE_NAME + " server Initialized, adding event subscribers...");
LOGGER.info(ModInfo.READABLE_NAME + " server Initialized.");
ApiEventInjector.INSTANCE.fireAllEvents(DhApiAfterDhInitEvent.class, null);
this.subscribeRegisterCommandsEvent(dispatcher -> { this.commandInitializer = new CommandInitializer(dispatcher); });
@@ -126,7 +121,7 @@ public abstract class AbstractModInitializer
this.checkForUpdates();
LOGGER.info(ModInfo.READABLE_NAME + " server Initialized at " + server.getServerDirectory());
LOGGER.info("Dedicated server initialized at " + server.getServerDirectory());
});
}
@@ -168,7 +163,7 @@ public abstract class AbstractModInitializer
private void initConfig()
{
ConfigBase.RunFirstTimeSetup();
ConfigBase.INSTANCE = new ConfigBase(ModInfo.ID, ModInfo.NAME, Config.class, ModInfo.CONFIG_FILE_VERSION);
Config.completeDelayedSetup();
}
@@ -188,12 +183,9 @@ public abstract class AbstractModInitializer
private void postInit()
{
LOGGER.info("Running Delayed setup...");
LOGGER.info("Post-Initializing Mod");
this.runDelayedSetup();
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");
}
@@ -1,7 +1,6 @@
package com.seibel.distanthorizons.common;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
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;
@@ -31,16 +30,6 @@ public abstract class AbstractPluginPacketSender implements IPluginPacketSender
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)
@@ -52,7 +41,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;
@@ -60,7 +49,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);
@@ -93,19 +82,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()); }
}
@@ -59,9 +59,13 @@ public class DependencySetup
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);
@@ -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)
{
@@ -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,55 +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_10
return "1.21.10";
#if MC_VER < MC_1_19_2
return Minecraft.getInstance().getGame().getVersion().getId();
#else
ERROR MC version constant missing
return SharedConstants.getCurrentVersion().getId();
#endif
}
}
@@ -1,135 +0,0 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
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 net.minecraft.core.BlockPos;
import net.minecraft.world.level.BlockAndTintGetter;
import net.minecraft.world.level.ColorResolver;
import net.minecraft.world.level.biome.Biome;
#if MC_VER >= MC_1_18_2
import net.minecraft.core.Holder;
#endif
public abstract class AbstractDhTintGetter implements BlockAndTintGetter
{
protected final BiomeWrapper biomeWrapper;
protected final int smoothingRadiusInBlocks;
protected final FullDataSourceV2 fullDataSource;
//=============//
// constructor //
//=============//
public AbstractDhTintGetter(BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource)
{
this.biomeWrapper = biomeWrapper;
this.fullDataSource = fullDataSource;
this.smoothingRadiusInBlocks = Config.Client.Advanced.Graphics.Quality.lodBiomeBlending.get();
}
//================//
// shared methods //
//================//
@Override
public int getBlockTint(BlockPos blockPos, 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 colorResolver.getColor(this.unwrapBiome(this.biomeWrapper.biome), blockPos.getX(), blockPos.getZ());
}
// use a rolling average to calculate the color
int dataPointCount = 0;
int rollingRed = 0;
int rollingGreen = 0;
int rollingBlue = 0;
int xMin = blockPos.getX() - this.smoothingRadiusInBlocks;
int xMax = blockPos.getX() + this.smoothingRadiusInBlocks;
int zMin = blockPos.getZ() - this.smoothingRadiusInBlocks;
int zMax = blockPos.getZ() + this.smoothingRadiusInBlocks;
DhBlockPosMutable mutableBlockPos = new DhBlockPosMutable(0, blockPos.getY(), 0);
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.getAtBlockPos(mutableBlockPos);
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 = colorResolver.getColor(this.unwrapBiome(biomeWrapper.biome), mutableBlockPos.getX(), mutableBlockPos.getZ());
// 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 colorResolver.getColor(this.unwrapBiome(this.biomeWrapper.biome), blockPos.getX(), blockPos.getZ());
}
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;
}
protected 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
}
}
@@ -47,7 +47,6 @@ import net.minecraft.core.registries.Registries;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.biome.Biome;
#if MC_VER >= MC_1_18_2
import net.minecraft.world.level.biome.Biomes;
#endif
@@ -131,6 +130,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);
}
//=========//
@@ -279,16 +286,64 @@ 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);
#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
if (!deserializeResult.success)
if (!success)
{
if (!brokenResourceLocationStrings.contains(resourceLocationString))
{
@@ -299,7 +354,7 @@ public class BiomeWrapper implements IBiomeWrapper
}
foundWrapper = (BiomeWrapper) getBiomeWrapper(deserializeResult.biome, levelWrapper);
foundWrapper = (BiomeWrapper) getBiomeWrapper(biome, levelWrapper);
return foundWrapper;
}
catch (Exception e)
@@ -313,82 +368,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 + "].");
}
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() + "].");
}
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;
}
}
}
@@ -43,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;
@@ -81,7 +79,6 @@ 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;
@@ -93,19 +90,20 @@ public class BlockStateWrapper implements IBlockStateWrapper
// 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;
@@ -162,12 +160,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++)
{
@@ -203,39 +198,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)
{
@@ -379,13 +341,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
@@ -394,20 +356,19 @@ 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)
#if MC_VER < MC_1_21_3
else if (this.blockState.propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO))
#else
else if (this.blockState.propagatesSkylightDown())
#endif
{
// 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
@@ -417,37 +378,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; }
@@ -520,8 +453,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; }
@@ -20,8 +20,6 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.core.config.Config;
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.util.ColorUtil;
@@ -42,18 +40,11 @@ import java.util.Random;
#endif
import net.minecraft.world.level.block.state.BlockState;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
#if MC_VER < MC_1_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
@@ -65,8 +56,6 @@ public class ClientBlockStateColorCache
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
// TODO it isn't that we need the level, but that we need the adjacent data
// maybe we can pass in the full data source?
private static final HashSet<BlockState> BLOCK_STATES_THAT_NEED_LEVEL = new HashSet<>();
private static final HashSet<BlockState> BROKEN_BLOCK_STATES = new HashSet<>();
@@ -95,9 +84,7 @@ public class ClientBlockStateColorCache
private static final RandomSource RANDOM = RandomSource.create();
#endif
private final IClientLevelWrapper clientLevelWrapper;
private final BlockStateWrapper blockStateWrapper;
private final IClientLevelWrapper levelWrapper;
private final BlockState blockState;
private final LevelReader level;
@@ -180,10 +167,8 @@ public class ClientBlockStateColorCache
public ClientBlockStateColorCache(BlockState blockState, IClientLevelWrapper samplingLevel)
{
this.blockState = blockState;
this.clientLevelWrapper = samplingLevel;
this.levelWrapper = samplingLevel;
this.level = (LevelReader) samplingLevel.getWrappedMcObject();
this.blockStateWrapper = BlockStateWrapper.fromBlockState(this.blockState, this.clientLevelWrapper);
this.resolveColors();
}
@@ -211,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
@@ -225,37 +212,19 @@ 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.needShade = firstQuad.isShade();
this.tintIndex = firstQuad.getTintIndex();
#else
this.needShade = firstQuad.shade();
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,
ColorMode.getColorMode(this.blockState.getBlock()));
#elif MC_VER < MC_1_21_5
this.baseColor = calculateColorFromTexture(
firstQuad.getSprite(),
ColorMode.getColorMode(this.blockState.getBlock()));
#else
this.baseColor = calculateColorFromTexture(
firstQuad.sprite(),
ColorMode.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
{
@@ -263,7 +232,8 @@ public class ClientBlockStateColorCache
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
@@ -272,17 +242,10 @@ public class ClientBlockStateColorCache
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()));
}
//String serialString = this.blockStateWrapper.getSerialString();
//if (serialString.contains("minecraft:water")
// || serialString.contains("grass"))
//{
// BLOCK_STATES_THAT_NEED_LEVEL.add(this.blockState);
//}
this.isColorResolved = true;
}
finally
@@ -290,35 +253,6 @@ 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, ColorMode colorMode)
{
@@ -430,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;
@@ -451,20 +383,13 @@ public class ClientBlockStateColorCache
return (bias + (scale * t)) >>> 16;
}
private int getParticleIconColor()
{
return calculateColorFromTexture(
Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
ColorMode.getColorMode(this.blockState.getBlock()));
}
//===============//
// public getter //
//===============//
public int getColor(BiomeWrapper biome, FullDataSourceV2 fullDataSource, DhBlockPos pos)
public int getColor(BiomeWrapper biome, DhBlockPos pos)
{
// only get the tint if the block needs to be tinted
if (!this.needPostTinting)
@@ -489,15 +414,12 @@ public class ClientBlockStateColorCache
try
{
tintColor = Minecraft.getInstance().getBlockColors()
.getColor(this.blockState,
new TintWithoutLevelSmoothOverrider(biome, fullDataSource), // TODO can this object be cached?
McObjectConverter.Convert(pos),
this.tintIndex);
.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 ["+TintWithoutLevelSmoothOverrider.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);
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);
}
}
@@ -508,10 +430,7 @@ public class ClientBlockStateColorCache
// this logic can't be used all the time due to it breaking some blocks tinting
// specifically oceans don't render correctly
tintColor = Minecraft.getInstance().getBlockColors()
.getColor(this.blockState,
new TintGetterOverrideSmooth(this.level, biome, fullDataSource), // TODO can this object be cached?
McObjectConverter.Convert(pos),
this.tintIndex);
.getColor(this.blockState, new TintGetterOverrideFast(this.level), McObjectConverter.Convert(pos), this.tintIndex);
}
}
catch (Exception e)
@@ -0,0 +1,194 @@
/*
* 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.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;
//=============//
// constructor //
//=============//
public TintGetterOverrideFast(LevelReader parent) { this.parent = parent; }
//=========//
// methods //
//=========//
private Biome _getBiome(BlockPos pos)
{
#if MC_VER >= MC_1_18_2
return this.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 this.parent.getLightEngine(); }
@Override
public int getBrightness(LightLayer lightLayer, BlockPos blockPos) { return this.parent.getBrightness(lightLayer, blockPos); }
@Override
public int getRawBrightness(BlockPos blockPos, int i) { return this.parent.getRawBrightness(blockPos, i); }
@Override
public boolean canSeeSky(BlockPos blockPos) { return this.parent.canSeeSky(blockPos); }
@Override
@Nullable
public BlockEntity getBlockEntity(BlockPos blockPos) { return this.parent.getBlockEntity(blockPos); }
@Override
public BlockState getBlockState(BlockPos blockPos) { return this.parent.getBlockState(blockPos); }
@Override
public FluidState getFluidState(BlockPos blockPos) { return this.parent.getFluidState(blockPos); }
@Override
public int getLightEmission(BlockPos blockPos) { return this.parent.getLightEmission(blockPos); }
#if MC_VER < MC_1_21_3
@Override
public int getMaxLightLevel() { return parent.getMaxLightLevel(); }
#else
#endif
@Override
public Stream<BlockState> getBlockStates(AABB aABB)
{ return this.parent.getBlockStates(aABB); }
@Override
public BlockHitResult clip(ClipContext clipContext)
{ return this.parent.clip(clipContext); }
@Override
@Nullable
public BlockHitResult clipWithInteractionOverride(Vec3 vec3, Vec3 vec32, BlockPos blockPos, VoxelShape voxelShape, BlockState blockState)
{ return this.parent.clipWithInteractionOverride(vec3, vec32, blockPos, voxelShape, blockState); }
@Override
public double getBlockFloorHeight(VoxelShape voxelShape, Supplier<VoxelShape> supplier)
{ return this.parent.getBlockFloorHeight(voxelShape, supplier); }
@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
//==============//
// post MC 1.17 //
//==============//
#if MC_VER >= MC_1_17_1
@Override
public <T extends BlockEntity> Optional<T> getBlockEntity(BlockPos blockPos, BlockEntityType<T> blockEntityType)
{ return this.parent.getBlockEntity(blockPos, blockEntityType); }
@Override
public BlockHitResult isBlockInLine(ClipBlockStateContext clipBlockStateContext)
{ return this.parent.isBlockInLine(clipBlockStateContext); }
@Override
public int getHeight() { return this.parent.getHeight(); }
#if MC_VER < MC_1_21_3
@Override
public int getMinBuildHeight() { return this.parent.getMinBuildHeight(); }
#else
@Override
public int getMinY() { return this.parent.getMinY(); }
#endif
@Override
public int getSectionsCount() { return this.parent.getSectionsCount(); }
#if MC_VER < MC_1_21_3
@Override
public int getMinSection() { return this.parent.getMinSection(); }
#else
@Override
public int getMinSectionY() { return BlockAndTintGetter.super.getMinSectionY(); }
#endif
#if MC_VER < MC_1_21_3
@Override
public int getMaxSection() { return this.parent.getMaxSection(); }
#else
@Override
public int getMaxSectionY() { return this.parent.getMaxSectionY(); }
#endif
@Override
public boolean isOutsideBuildHeight(BlockPos blockPos) { return this.parent.isOutsideBuildHeight(blockPos); }
@Override
public boolean isOutsideBuildHeight(int i) { return this.parent.isOutsideBuildHeight(i); }
@Override
public int getSectionIndex(int i) { return this.parent.getSectionIndex(i); }
@Override
public int getSectionIndexFromSectionY(int i) { return this.parent.getSectionIndexFromSectionY(i); }
@Override
public int getSectionYFromSectionIndex(int i) { return this.parent.getSectionYFromSectionIndex(i); }
#endif
}
@@ -19,10 +19,12 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
import com.seibel.distanthorizons.core.util.LodUtil;
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;
@@ -38,9 +40,10 @@ import java.util.Optional;
import java.util.function.Supplier;
import java.util.stream.Stream;
public class TintGetterOverrideSmooth extends AbstractDhTintGetter
public class TintGetterOverrideSmooth implements BlockAndTintGetter
{
private final LevelReader parent;
LevelReader parent;
public int smoothingRange;
@@ -48,10 +51,10 @@ public class TintGetterOverrideSmooth extends AbstractDhTintGetter
// constructor //
//=============//
public TintGetterOverrideSmooth(LevelReader parent, BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource)
{
super(biomeWrapper, fullDataSource);
public TintGetterOverrideSmooth(LevelReader parent, int smoothingRange)
{
this.parent = parent;
this.smoothingRange = smoothingRange;
}
@@ -60,6 +63,41 @@ public class TintGetterOverrideSmooth extends AbstractDhTintGetter
// methods //
//=========//
private Biome _getBiome(BlockPos pos)
{
#if MC_VER >= MC_1_18_2
return this.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;
}
@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); }
@@ -147,7 +185,7 @@ public class TintGetterOverrideSmooth extends AbstractDhTintGetter
public int getMinSection() { return this.parent.getMinSection(); }
#else
@Override
public int getMinSectionY() { return super.getMinSectionY(); }
public int getMinSectionY() { return BlockAndTintGetter.super.getMinSectionY(); }
#endif
#if MC_VER < MC_1_21_3
@@ -173,7 +211,4 @@ public class TintGetterOverrideSmooth extends AbstractDhTintGetter
@Override
public int getSectionYFromSectionIndex(int i) { return this.parent.getSectionYFromSectionIndex(i); }
#endif
}
@@ -0,0 +1,158 @@
/*
* 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.block;
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.*;
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;
#if MC_VER >= MC_1_18_2
import net.minecraft.core.Holder;
#endif
public class TintWithoutLevelOverrider implements BlockAndTintGetter
{
/**
* 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;
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 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 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(@NotNull BlockPos pos)
{
throw new UnsupportedOperationException("ERROR: getBlockEntity() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
public @NotNull BlockState getBlockState(@NotNull BlockPos pos)
{
throw new UnsupportedOperationException("ERROR: getBlockState() called on TintWithoutLevelOverrider. Object is for tinting only.");
}
@Override
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
@Override
public int getMinBuildHeight()
{ throw new UnsupportedOperationException("ERROR: getMinBuildHeight() called on TintWithoutLevelOverrider. Object is for tinting only."); }
#else
@Override
public int getMinY()
{ throw new UnsupportedOperationException("ERROR: getMinY() called on TintWithoutLevelOverrider. Object is for tinting only."); }
#endif
#endif
}
@@ -19,24 +19,37 @@
package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2;
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;
public class TintWithoutLevelSmoothOverrider extends AbstractDhTintGetter
#if MC_VER >= MC_1_18_2
import net.minecraft.core.Holder;
#endif
public class TintWithoutLevelSmoothOverrider implements BlockAndTintGetter
{
final BiomeWrapper biome;
public int smoothingRange;
//=============//
// constructor //
//=============//
public TintWithoutLevelSmoothOverrider(BiomeWrapper biomeWrapper, FullDataSourceV2 fullDataSource)
{ super(biomeWrapper, fullDataSource); }
public TintWithoutLevelSmoothOverrider(BiomeWrapper biome, int smoothingRange)
{
this.biome = biome;
this.smoothingRange = smoothingRange;
}
@@ -44,6 +57,49 @@ public class TintWithoutLevelSmoothOverrider extends AbstractDhTintGetter
// methods //
//=========//
@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 TintWithoutLevelSmoothOverrider. Object is for tinting only."); }
@@ -278,14 +278,14 @@ public class ChunkWrapper implements IChunkWrapper
{
// is this block solid?
if (solidHeight == minInclusiveBuildHeight
&& block.isSolid())
&& block.isSolid())
{
solidHeight = y;
}
// is this block light blocking?
if (lightBlockingHeight == minInclusiveBuildHeight
&& block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT)
&& block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT)
{
lightBlockingHeight = y;
}
@@ -1,89 +0,0 @@
package com.seibel.distanthorizons.common.wrappers.gui;
#if MC_VER < MC_1_21_10
// 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.resources.ResourceLocation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.chunk.LevelChunk;
#endif
#if MC_VER < MC_1_21_10
// 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
ResourceLocation.fromNamespaceAndPath(ModInfo.RESOURCE_NAMESPACE, "distant_horizons"),
// 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
}
@@ -1,54 +1,40 @@
package com.seibel.distanthorizons.common.wrappers.gui;
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 org.apache.logging.log4j.Logger;
import java.lang.invoke.MethodHandles;
public class GetConfigScreen
{
protected static final Logger LOGGER = DhLoggerBuilder.getLogger();
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 = ConfigBase.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(ConfigBase.INSTANCE, 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");
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+"].");
}
@@ -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_10
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,17 +78,14 @@ 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)
}
@@ -100,39 +95,41 @@ public class MinecraftScreen
{
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;
}
}
@@ -39,13 +39,9 @@ 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;
#else
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.RenderPipelines;
#endif
/**
@@ -176,15 +172,9 @@ public class TexturedButtonWidget extends Button
{
#if MC_VER < MC_1_21_3
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,
RenderType::guiTextured,
SPRITES.get(this.active, this.isHoveredOrFocused()),
this.getX(), this.getY(),
this.getWidth(), this.getHeight());
@@ -206,17 +196,9 @@ public class TexturedButtonWidget extends Button
#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,
RenderType::guiTextured,
this.textureResourceLocation,
this.getX(), this.getY(),
this.u, this.v + (this.hoveredVOffset * i),
@@ -74,7 +74,6 @@ public class ChangelogScreen extends DhScreen
{
return;
}
try
{
this.setupChangelog(versionID);
@@ -176,12 +175,9 @@ public class ChangelogScreen 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
if (!this.usable)
{
return;
@@ -253,35 +249,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_10
{
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, this.text, 12, y + 5, 0xFFFFFF);
}
#endif
@Override
public List<? extends GuiEventListener> children() { return this.children; }
public List<? extends GuiEventListener> children()
{
return this.children;
}
#if MC_VER >= MC_1_17_1
@Override
public List<? extends NarratableEntry> narratables() { return this.children; }
public List<? extends NarratableEntry> narratables()
{
return this.children;
}
#endif
}
}
@@ -169,10 +169,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
@@ -180,30 +178,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
}
}
@@ -33,7 +33,6 @@ 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;
@@ -215,29 +214,17 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@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);
}
@@ -307,22 +294,10 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
return;
}
if (!GLProxy.hasInstance())
{
// rendering setup hasn't finished
return;
}
#if MC_VER < MC_1_19_2
player.sendMessage(new TextComponent(string), getPlayer().getUUID());
#elif MC_VER < MC_1_21_10
#else
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false);
#else
GLProxy.getInstance().queueRunningOnRenderThread(() ->
{
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false);
});
#endif
}
@@ -19,11 +19,7 @@
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;
@@ -152,10 +148,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper
public void glBlendFunc(int sfactor, int dfactor)
{
GL32.glBlendFunc(sfactor, dfactor);
#if MC_VER < MC_1_21_5
GlStateManager._blendFunc(sfactor, dfactor);
#endif
GlStateManager._blendFunc(sfactor, dfactor);
}
/** @see GL32#glBlendFuncSeparate */
@Override
@@ -187,15 +180,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper
/** @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);
}
{ GlStateManager._glDeleteBuffers(buffer); }
// culling //
@@ -233,7 +218,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper
GlStateManager._activeTexture(textureId);
}
@Override
public int getActiveTexture() { return GL32.glGetInteger(GL32.GL_TEXTURE_BINDING_2D); }
public int getActiveTexture() { return GL32.glGetInteger(GL32.GL_ACTIVE_TEXTURE); }
/**
* Always binds to {@link GL32#GL_TEXTURE_2D}
@@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentHashMap;
import com.mojang.blaze3d.pipeline.RenderTarget;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.common.wrappers.WrapperFactory;
import com.seibel.distanthorizons.common.wrappers.misc.LightMapWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
@@ -32,12 +33,9 @@ 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
@@ -45,6 +43,8 @@ import org.joml.Matrix4f;
import org.joml.Vector3f;
#else
#endif
#if MC_VER >= MC_1_20_2
#endif
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
@@ -71,21 +71,22 @@ import net.minecraft.world.phys.Vec3;
import org.apache.logging.log4j.Logger;
import org.joml.Vector4f;
#if MC_VER >= MC_1_21_5
import com.mojang.blaze3d.opengl.GlTexture;
#else
#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 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);
@@ -101,19 +102,7 @@ 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()
@@ -126,22 +115,11 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
/** 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
@@ -174,33 +152,8 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
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
);
#else
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);
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
@@ -295,27 +248,7 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return height;
}
protected RenderTarget getRenderTarget() { return MC.getMainRenderTarget(); }
@Override
public boolean mcRendersToFrameBuffer()
{
#if MC_VER < MC_1_21_5
return true;
#else
return false;
#endif
}
@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()
@@ -326,96 +259,27 @@ 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()
{
#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
}
public int getColorTextureId() { return this.getRenderTarget().getColorTextureId(); }
@Override
public int getTargetFrameBufferViewportWidth()
{
#if MC_VER < MC_1_21_10
return this.getRenderTarget().viewWidth;
#else
return this.getRenderTarget().width;
#endif
return getRenderTarget().viewWidth;
}
@Override
public int getTargetFrameBufferViewportHeight()
{
#if MC_VER < MC_1_21_10
return this.getRenderTarget().viewHeight;
#else
return this.getRenderTarget().height;
#endif
return getRenderTarget().viewHeight;
}
@Override
@@ -21,10 +21,8 @@ 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 org.apache.logging.log4j.Logger;
import org.lwjgl.opengl.GL32;
import java.nio.ByteBuffer;
@@ -32,7 +30,6 @@ import java.nio.ByteBuffer;
public class LightMapWrapper implements ILightMapWrapper
{
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private int textureId = 0;
@@ -52,7 +49,6 @@ public class LightMapWrapper implements ILightMapWrapper
public void uploadLightmap(NativeImage image)
{
#if MC_VER < MC_1_21_5
int currentTexture = GLMC.getActiveTexture();
if (this.textureId == 0)
{
@@ -63,27 +59,14 @@ public class LightMapWrapper implements ILightMapWrapper
GLMC.glBindTexture(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
GLMC.glBindTexture(currentTexture);
}
private void createLightmap(NativeImage image)
{
#if MC_VER < MC_1_21_5
this.textureId = GLMC.glGenTextures();
GLMC.glBindTexture(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)
@@ -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
}
//================//
@@ -7,7 +7,6 @@ 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;
@@ -26,6 +25,7 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.phys.Vec3;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -33,12 +33,7 @@ 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;
@@ -47,7 +42,6 @@ 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
@@ -55,12 +49,7 @@ import com.seibel.distanthorizons.core.util.ColorUtil;
public class ClientLevelWrapper implements IClientLevelWrapper
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger(ClientLevelWrapper.class.getSimpleName());
/**
* 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 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();
@@ -68,12 +57,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
private final ClientLevel level;
private final ConcurrentHashMap<BlockState, ClientBlockStateColorCache> blockCache = new ConcurrentHashMap<>();
/** cached method reference to reduce GC overhead */
private final Function<BlockState, ClientBlockStateColorCache> cachedBlockColorCacheFunction = (blockState) -> this.createBlockColorCache(blockState);
private BlockStateWrapper dirtBlockWrapper;
private BlockStateWrapper waterBlockWrapper;
private BiomeWrapper plainsBiomeWrapper;
@Deprecated // TODO circular references are bad
private IDhLevel parentDhLevel;
@@ -88,28 +72,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)
{
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
@@ -130,31 +96,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
@@ -195,17 +137,14 @@ public class ClientLevelWrapper implements IClientLevelWrapper
//====================//
@Override
public int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, FullDataSourceV2 fullDataSource, IBlockStateWrapper blockWrapper)
public int getBlockColor(DhBlockPos pos, IBiomeWrapper biome, IBlockStateWrapper blockWrapper)
{
ClientBlockStateColorCache blockColorCache = this.blockCache.computeIfAbsent(
((BlockStateWrapper) blockWrapper).blockState,
this.cachedBlockColorCacheFunction);
(block) -> new ClientBlockStateColorCache(block, this));
return blockColorCache.getColor((BiomeWrapper) biome, fullDataSource, pos);
return blockColorCache.getColor((BiomeWrapper) biome, pos);
}
/** used by {@link ClientLevelWrapper#cachedBlockColorCacheFunction} */
private ClientBlockStateColorCache createBlockColorCache(BlockState block) { return new ClientBlockStateColorCache(block, this); }
@Override
public int getDirtBlockColor()
@@ -224,27 +163,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
}
}
return this.getBlockColor(DhBlockPos.ZERO, BiomeWrapper.EMPTY_WRAPPER, null, this.dirtBlockWrapper);
}
@Override
public int getWaterBlockColor()
{
if (this.waterBlockWrapper == null)
{
try
{
this.waterBlockWrapper = (BlockStateWrapper) BlockStateWrapper.deserialize(BlockStateWrapper.WATER_RESOURCE_LOCATION_STRING, this);
}
catch (IOException e)
{
// shouldn't happen, but just in case
LOGGER.warn("Unable to get water color with resource location ["+BlockStateWrapper.WATER_RESOURCE_LOCATION_STRING+"] with level ["+this+"].", e);
return -1;
}
}
return this.getBlockColor(DhBlockPos.ZERO, BiomeWrapper.EMPTY_WRAPPER, null, this.waterBlockWrapper);
return this.getBlockColor(DhBlockPos.ZERO,BiomeWrapper.EMPTY_WRAPPER, this.dirtBlockWrapper);
}
@Override
@@ -346,7 +265,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
@Override
public void onUnload()
{
LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.remove(this.level);
LEVEL_WRAPPER_BY_CLIENT_LEVEL.remove(this.level);
this.parentDhLevel = null;
}
@@ -28,80 +28,72 @@ 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;
//=============//
// Constructor //
//=============//
public DimensionTypeWrapper(DimensionType dimensionType) { this.dimensionType = dimensionType; }
public DimensionTypeWrapper(DimensionType dimensionType)
{
this.dimensionType = dimensionType;
}
public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType)
{
String dimName = getName(dimensionType);
// 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 it hasn't been created yet, we create it and save it in the map
DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType);
DIMENSION_WRAPPER_BY_NAME.put(dimName, dimensionTypeWrapper);
dimensionTypeWrapperMap.put(dimensionType, dimensionTypeWrapper);
//we return the newly created wrapper
return dimensionTypeWrapper;
}
public static void clearMap() { DIMENSION_WRAPPER_BY_NAME.clear(); }
public static void clearMap()
{
dimensionTypeWrapperMap.clear();
}
//=================//
// wrapper methods //
//=================//
@Override
public String getName() { return getName(this.dimensionType); }
public static String getName(DimensionType dimensionType)
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();
#else
return dimensionType.effectsLocation().getPath();
return this.dimensionType.effectsLocation().getPath();
#endif
}
@Override
public boolean hasCeiling() { return this.dimensionType.hasCeiling(); }
public boolean hasCeiling()
{
return this.dimensionType.hasCeiling();
}
@Override
public boolean hasSkyLight() { return this.dimensionType.hasSkyLight(); }
public boolean hasSkyLight()
{
return this.dimensionType.hasSkyLight();
}
@Override
public Object getWrappedMcObject() { return this.dimensionType; }
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 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)
@@ -113,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());
}
}
}
@@ -20,10 +20,6 @@
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;
@@ -42,7 +38,6 @@ 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;
@@ -62,11 +57,7 @@ import org.apache.logging.log4j.Logger;
public class ServerLevelWrapper implements IServerLevelWrapper
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
/**
* 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 ConcurrentHashMap<ServerLevel, ServerLevelWrapper> LEVEL_WRAPPER_BY_SERVER_LEVEL = new ConcurrentHashMap<>();
private final ServerLevel level;
@Deprecated // TODO circular references are bad
@@ -79,29 +70,15 @@ public class ServerLevelWrapper implements IServerLevelWrapper
//==============//
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();
}
{ return LEVEL_WRAPPER_BY_SERVER_LEVEL.computeIfAbsent(level, ServerLevelWrapper::new); }
public ServerLevelWrapper(ServerLevel level) { this.level = level; }
//==================//
// instance methods //
//==================//
//=========//
// methods //
//=========//
@Override
public File getMcSaveFolder()
@@ -116,12 +93,10 @@ public class ServerLevelWrapper implements IServerLevelWrapper
@Override
public String getWorldFolderName()
{
// Need specifically overworld since it's the only dimension that is stored in a 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();
#if MC_VER >= MC_1_17_1
return this.level.getServer().getWorldScreenshotFile().get().getParent().getFileName().toString();
#else // <= 1.16.5
return this.level.getServer().getWorldScreenshotFile().getParentFile().getName();
#endif
}
@@ -204,7 +179,7 @@ public class ServerLevelWrapper implements IServerLevelWrapper
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
@@ -50,9 +50,6 @@ import java.util.function.Function;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepBiomes;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepFeatures;
@@ -63,7 +60,10 @@ import com.seibel.distanthorizons.common.wrappers.worldGeneration.step.StepSurfa
import net.minecraft.server.level.*;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.chunk.*;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkGenerator;
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 net.minecraft.world.level.levelgen.DebugLevelSource;
@@ -73,17 +73,19 @@ import net.minecraft.world.level.levelgen.NoiseBasedChunkGenerator;
import net.minecraft.nbt.CompoundTag;
import org.apache.logging.log4j.LogManager;
#if MC_VER <= MC_1_17_1
#elif MC_VER <= MC_1_19_2
import net.minecraft.core.Registry;
#else
#if MC_VER >= MC_1_19_4
import net.minecraft.core.registries.Registries;
#else
import net.minecraft.core.Registry;
#endif
#if MC_VER <= MC_1_20_4
import net.minecraft.world.level.chunk.ChunkStatus;
import org.jetbrains.annotations.Nullable;
#else
import net.minecraft.world.level.chunk.status.ChunkStatus;
import javax.annotation.Nullable;
#endif
/*
@@ -111,13 +113,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
new ConfigBasedLogger(LogManager.getLogger("LodWorldGen"),
() -> Config.Common.Logging.logWorldGenLoadEvent.get());
#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_10
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 */0);
#endif
private static final IModChecker MOD_CHECKER = SingletonInjector.INSTANCE.get(IModChecker.class);
@@ -129,7 +125,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
* pull chunks using their async method), or if there
* was an issue with the sync pulling method.
*/
private boolean pullExistingChunkUsingMcAsyncMethod = false;
private boolean pullExistingChunkAsync = false;
@@ -174,7 +170,6 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
// constructors //
//==============//
@NotNull
public static final ImmutableMap<EDhApiWorldGenerationStep, Integer> WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP;
public static final int MAX_WORLD_GEN_CHUNK_BORDER_NEEDED;
@@ -206,12 +201,11 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
builder.put(EDhApiWorldGenerationStep.LIGHT, 0);
WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP = builder.build();
// in James' testing as of 2025-09-13 a border here of 2
// and a getChunkPosToGenerateStream() radius of 14 provided more accurate
// structure generation, however it also caused extreme server lag
// a border of 0 here and a getChunkPosToGenerateStream() radius of 8 provided
// good-enough structure generation while not lagging the server
// TODO this is a test to see if the additional boarder is actually necessary or not.
// If world generators end up having infinite loops or other unexplained issues,
// this should be set back to the commented out logic below
MAX_WORLD_GEN_CHUNK_BORDER_NEEDED = 0;
//MAX_WORLD_GEN_CHUNK_BORDER_NEEDED = WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP.values().stream().mapToInt(Integer::intValue).max().getAsInt();
}
public BatchGenerationEnvironment(IDhServerLevel serverlevel)
@@ -247,10 +241,19 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (MOD_CHECKER.isModLoaded("c2me"))
{
EVENT_LOGGER.info("C2ME detected: DH's pre-existing chunk accessing will use methods handled by C2ME.");
this.pullExistingChunkUsingMcAsyncMethod = true;
EVENT_LOGGER.info("C2ME detected: DH's pre-existing chunk accessing will use async methods handled by C2ME.");
this.pullExistingChunkAsync = true;
}
//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 (ioWorker.storage != null)
this.params = new GlobalParameters(serverlevel);
}
@@ -339,8 +342,6 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
// world generation //
//==================//
// TODO this is already being run on a generator thread,
// why are we passing in an executor?
/** @throws RejectedExecutionException if the given {@link Executor} is cancelled. */
public CompletableFuture<Void> generateLodFromListAsync(GenerationEvent genEvent, Executor executor) throws RejectedExecutionException, InterruptedException
{
@@ -384,14 +385,10 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
.map((chunkPos) -> this.createEmptyOrPreExistingChunkAsync(chunkPos.x, chunkPos.z, chunkSkyLightingByDhPos, chunkBlockLightingByDhPos, generatedChunkByDhPos))
.toArray(CompletableFuture[]::new);
// join to prevent an issue where DH queues too many tasks or something(?)
// also allows file IO to run in parallel so no one thread is waiting on disk IO (this is only an issue when C2ME is present)
CompletableFuture.allOf(readFutures).join();
// future chain for generation
return CompletableFuture.runAsync(() ->
{
try
return CompletableFuture.allOf(readFutures)
.thenRunAsync(() ->
{
// offset 1 chunk in both X and Z direction so we can generate an even number of chunks wide
// while still submitting an odd number width to MC's internal generators
@@ -414,10 +411,10 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
int centerZ = refPosZ + radius + zOffset;
// get/create the list of chunks we're going to generate
IEmptyChunkRetrievalFunc fallbackFunc =
IEmptyChunkRetrievalFunc fallbackFunc =
(chunkPosX, chunkPosZ) -> Objects.requireNonNull(
generatedChunkByDhPos.get(new DhChunkPos(chunkPosX, chunkPosZ)),
() -> String.format("Requested chunk [%d, %d] unavailable during world generation", chunkPosX, chunkPosZ));
generatedChunkByDhPos.get(new DhChunkPos(chunkPosX, chunkPosZ)),
() -> String.format("Requested chunk [%d, %d] unavailable during world generation", chunkPosX, chunkPosZ));
ArrayGridList<ChunkAccess> regionChunks = new ArrayGridList<>(
refSize,
@@ -439,7 +436,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
// this method shouldn't be necessary since we're passing in a pre-populated
// list of chunks, but just in case
fallbackFunc
);
);
lightGetterAdaptor.setRegion(region);
genEvent.threadedParam.makeStructFeat(region, this.params);
@@ -528,12 +525,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
genEvent.threadedParam.perf.recordEvent(genEvent.timer);
PREF_LOGGER.debugInc(genEvent.timer.toString());
}
}
catch (Exception e)
{
EVENT_LOGGER.error("Unexpected error during world gen for min chunk pos ["+genEvent.minPos+"], error: ["+e.getMessage()+"].", e);
}
}, executor);
}, executor);
}
/** @param extraRadius in both the positive and negative directions */
private static Stream<ChunkPos> getChunkPosToGenerateStream(int genMinX, int genMinZ, int width, int extraRadius)
@@ -572,7 +564,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
if (Config.Common.LodBuilding.pullLightingForPregeneratedChunks.get())
{
// attempt to get chunk lighting
ChunkFileReader.CombinedChunkLightStorage combinedLights = ChunkFileReader.readLight(newChunk, chunkData);
ChunkLoader.CombinedChunkLightStorage combinedLights = ChunkLoader.readLight(newChunk, chunkData);
if (combinedLights != null)
{
chunkSkyLightingByDhPos.put(dhChunkPos, combinedLights.skyLightStorage);
@@ -600,7 +592,6 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
return newChunk;
});
}
// TODO FIXME this method can be called up to 25 times for the same chunk position, why?
private CompletableFuture<CompoundTag> getChunkNbtDataAsync(ChunkPos chunkPos)
{
ServerLevel level = this.params.level;
@@ -619,7 +610,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
#else
// storage will be null if C2ME is installed
if (!this.pullExistingChunkUsingMcAsyncMethod && ioWorker.storage != null)
if (!this.pullExistingChunkAsync && ioWorker.storage != null)
{
try
{
@@ -633,7 +624,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
// ioWorker.storage
// but just in case
EVENT_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;
this.pullExistingChunkAsync = true;
// try again now using the async method
return this.getChunkNbtDataAsync(chunkPos);
@@ -642,42 +633,32 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
else
{
// log if we unexpectedly weren't able to run the sync chunk pulling
if (!this.pullExistingChunkUsingMcAsyncMethod)
if (!this.pullExistingChunkAsync)
{
// this shouldn't happen, but just in case
EVENT_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;
this.pullExistingChunkAsync = 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 ->
.thenApply(optional -> optional.orElse(null))
.exceptionally((throwable) ->
{
// unwrap the CompletionException if necessary
Throwable actualThrowable = throwable;
while (actualThrowable instanceof CompletionException completionException)
{
// 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();
}
LOAD_LOGGER.warn("DistantHorizons: Couldn't load or make chunk ["+chunkPos+"], error: ["+actualThrowable.getMessage()+"].", actualThrowable);
return null;
});
actualThrowable = completionException.getCause();
}
LOAD_LOGGER.warn("DistantHorizons: Couldn't load or make chunk ["+chunkPos+"], error: ["+actualThrowable.getMessage()+"].", actualThrowable);
return null;
});
}
#endif
}
@@ -702,7 +683,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
LOAD_LOGGER.debug("DistantHorizons: Loading chunk [" + chunkPos + "] from disk.");
@Nullable
ChunkAccess chunk = ChunkFileReader.read(level, chunkPos, chunkData);
ChunkAccess chunk = ChunkLoader.read(level, chunkPos, chunkData);
if (chunk != null)
{
if (Config.Common.LodBuilding.assumePreExistingChunksAreFinished.get())
@@ -744,10 +725,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
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_10
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);
return new ProtoChunk(chunkPos, UpgradeData.EMPTY, level, level.registryAccess().lookupOrThrow(Registries.BIOME), null);
#endif
}
@@ -893,11 +872,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
chunkLevel = generateUpToFeatures ? ChunkLevel.byStatus(ChunkStatus.FEATURES) : 33;
#endif
#if MC_VER < MC_1_21_5
level.getChunkSource().distanceManager.addTicket(DH_SERVER_GEN_TICKET, pos, chunkLevel, pos);
#else
level.getChunkSource().addTicketWithRadius(DH_SERVER_GEN_TICKET, pos, 0);
#endif
level.getChunkSource().distanceManager.runAllUpdates(level.getChunkSource().chunkMap); // probably not the most optimal to run updates here, but fast enough
ChunkHolder holder = level.getChunkSource().chunkMap.getUpdatingChunkIfPresent(pos.toLong());
if (holder == null)
@@ -934,11 +909,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
chunkLevel = chunkWasGeneratedUpToFeatures ? ChunkLevel.byStatus(ChunkStatus.FEATURES) : 33;
#endif
#if MC_VER < MC_1_21_5
level.getChunkSource().distanceManager.removeTicket(DH_SERVER_GEN_TICKET, pos, chunkLevel, pos);
#else
level.getChunkSource().removeTicketWithRadius(DH_SERVER_GEN_TICKET, pos, 0);
#endif
// mitigate OOM issues in vanilla chunk system: see https://github.com/pop4959/Chunky/pull/383
level.getChunkSource().chunkMap.tick(() -> false);
@@ -1089,8 +1060,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
}
}
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, int border) { return new ArrayGridList<>(total, border, total.gridSize - border); }
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP.get(step)); }
//private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, 0); }
//private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, MaxBorderNeeded - WORLD_GEN_CHUNK_BORDER_NEEDED_BY_GEN_STEP.get(step)); }
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, EDhApiWorldGenerationStep step) { return GetCutoutFrom(total, 0); }
@Override
@@ -29,9 +29,7 @@ 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 it.unimi.dsi.fastutil.shorts.ShortList;
@@ -86,23 +84,17 @@ import net.minecraft.world.level.chunk.status.ChunkType;
#endif
import net.minecraft.world.level.material.Fluid;
import org.jetbrains.annotations.Nullable;
public class ChunkFileReader
public class ChunkLoader
{
private static final AtomicBoolean ZERO_CHUNK_POS_ERROR_LOGGED_REF = new AtomicBoolean(false);
private static boolean zeroChunkPosErrorLogged = false;
#if MC_VER >= MC_1_21_10
// BLOCK_STATE_CODEC can no longer be statically created since
// it needs a level reference
#elif MC_VER >= MC_1_19_2
#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";
@@ -128,16 +120,19 @@ public class ChunkFileReader
CompoundTag tagLevel = chunkData;
#endif
int chunkX = tagGetInt(tagLevel,"xPos");
int chunkZ = tagGetInt(tagLevel, "zPos");
ChunkPos actualPos = new ChunkPos(chunkX, chunkZ);
ChunkPos actualPos = new ChunkPos(tagLevel.getInt("xPos"), tagLevel.getInt("zPos"));
if (!Objects.equals(chunkPos, actualPos))
{
if (chunkX == 0 && chunkZ == 0)
#if MC_VER >= MC_1_18_2
if (actualPos.equals(ChunkPos.ZERO))
#else
if (actualPos.equals(ChunkPos.INVALID_CHUNK_POS))
#endif
{
if (!ZERO_CHUNK_POS_ERROR_LOGGED_REF.getAndSet(true))
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" +
@@ -166,7 +161,7 @@ public class ChunkFileReader
#if MC_VER < MC_1_18_2
if (chunkType != ChunkStatus.ChunkType.LEVELCHUNK)
return null;
#elif MC_VER < MC_1_21_6
#else
BlendingData blendingData = readBlendingData(tagLevel);
#if MC_VER < MC_1_19_2
@@ -176,42 +171,17 @@ public class ChunkFileReader
if (chunkType == #if MC_VER < MC_1_20_6 ChunkStatus.ChunkType.PROTOCHUNK #else ChunkType.PROTOCHUNK #endif && blendingData == null)
return null;
#endif
#else
// ignore blending data, there appears to be an issue with parsing it in 1.21.6
BlendingData blendingData = null;
if (chunkType == ChunkType.PROTOCHUNK)
{
return null;
}
#endif
long inhabitedTime = tagGetLong(tagLevel, "InhabitedTime");
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;
UpgradeData upgradeData = UpgradeData.EMPTY;
// commented out 2025-06-04 as a test to see if the upgrade data
// is actually necessary for DH or if it can be ignored
// (if it can't be ignored we'll need to handle null responses from tagGetCompoundTag())
//
//#if MC_VER < MC_1_17_1
//upgradeData = tagLevel.contains(TAG_UPGRADE_DATA, 10)
// ? new UpgradeData(tagGetCompoundTag(tagLevel, TAG_UPGRADE_DATA))
// : UpgradeData.EMPTY;
//#elif MC_VER < MC_1_21_5
//upgradeData = tagLevel.contains(TAG_UPGRADE_DATA, 10)
// ? new UpgradeData(tagGetCompoundTag(tagLevel, TAG_UPGRADE_DATA), level)
// : UpgradeData.EMPTY;
//#else
//upgradeData = tagLevel.contains(TAG_UPGRADE_DATA)
// ? new UpgradeData(tagGetCompoundTag(tagLevel, TAG_UPGRADE_DATA), level)
// : UpgradeData.EMPTY;
//#endif
boolean isLightOn = tagGetBoolean(tagLevel, "isLightOn");
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 ,
@@ -228,21 +198,17 @@ public class ChunkFileReader
: 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);
#elif MC_VER < MC_1_21_4
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);
#else
// do we need the ticks for what we're doing?
LevelChunkTicks<Block> blockTicks = new LevelChunkTicks<>();
LevelChunkTicks<Fluid> fluidTicks = new LevelChunkTicks<>();
#endif
#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);
@@ -252,25 +218,18 @@ public class ChunkFileReader
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);
readPostPocessings(chunk, chunkData);
return chunk;
}
private static LevelChunkSection[] readSections(LevelAccessor level, ChunkPos chunkPos, CompoundTag chunkData)
{
#if MC_VER < MC_1_21_10
// BLOCK_STATE_CODEC is created statically
// TODO clean up this code separation
#else
final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainerFactory.create(level.registryAccess()).blockStatesContainerCodec();
#endif
#if MC_VER >= MC_1_18_2
#if MC_VER < MC_1_19_4
Registry<Biome> biomes = level.registryAccess().registryOrThrow(Registry.BIOME_REGISTRY);
@@ -288,142 +247,95 @@ public class ChunkFileReader
#elif MC_VER < MC_1_21_3
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getHolderOrThrow(Biomes.PLAINS));
#elif MC_VER < MC_1_21_10
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
#else
Codec<PalettedContainer<Holder<Biome>>> biomeCodec = PalettedContainer.codecRW(
biomes.holderByNameCodec(), PalettedContainerFactory.create(level.registryAccess()).biomeStrategy(), biomes.getOrThrow(Biomes.PLAINS));
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
#endif
#endif
int sectionYIndex = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif
LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYIndex];
ListTag tagSections = tagGetListTag(chunkData, "Sections", 10);
if (tagSections == null || tagSections.isEmpty())
{
tagSections = tagGetListTag(chunkData, "sections", 10);
}
boolean isLightOn = chunkData.getBoolean("isLightOn");
boolean hasSkyLight = level.dimensionType().hasSkyLight();
ListTag tagSections = chunkData.getList("Sections", 10);
if (tagSections.isEmpty()) tagSections = chunkData.getList("sections", 10);
if (tagSections != null)
for (int j = 0; j < tagSections.size(); ++j)
{
for (int j = 0; j < tagSections.size(); ++j)
{
CompoundTag tagSection = tagGetCompoundTag(tagSections, j);
if (tagSection == null)
{
continue;
}
final int sectionYPos = tagGetByte(tagSection, "Y");
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
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;
}
PalettedContainer<Biome> biomeContainer;
#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
boolean containsBlockStates;
#if MC_VER < MC_1_21_5
containsBlockStates = tagSection.contains("block_states", 10);
#else
containsBlockStates = tagSection.contains("block_states");
#endif
if (containsBlockStates)
{
#if MC_VER < MC_1_20_6
blockStateContainer = BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagGetCompoundTag(tagSection, "block_states"))
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
.getOrThrow(false, (message) -> logParsingWarningOnce(message));
#else
blockStateContainer = BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagGetCompoundTag(tagSection, "block_states"))
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
.getOrThrow((message) -> logErrorAndReturnException(message));
#endif
}
else
{
#if MC_VER < MC_1_21_10
blockStateContainer = new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
#else
blockStateContainer = PalettedContainerFactory.create(level.registryAccess()).createForBlockStates();
#endif
}
#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, (message) -> logWarningOnce(message))
: new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#else
boolean containsBiomes;
#if MC_VER < MC_1_21_5
containsBiomes = tagSection.contains("biomes", 10);
#else
containsBiomes = tagSection.contains("biomes");
#endif
if (containsBiomes)
{
#if MC_VER < MC_1_20_6
biomeContainer = biomeCodec.parse(NbtOps.INSTANCE, tagGetCompoundTag(tagSection, "biomes"))
.promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
.getOrThrow(false, (message) -> logParsingWarningOnce(message));
#else
biomeContainer = biomeCodec.parse(NbtOps.INSTANCE, tagGetCompoundTag(tagSection, "biomes"))
.promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
.getOrThrow((message) -> logErrorAndReturnException(message));
#endif
}
else
{
#if MC_VER < MC_1_21_3
biomeContainer = new PalettedContainer<Holder<Biome>>(
biomes.asHolderIdMap(),
biomes.getHolderOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#elif MC_VER < MC_1_21_10
biomeContainer = new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(),
biomes.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
}
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, (message) -> logWarningOnce(message))
#else
.getOrThrow((message) -> logErrorAndReturnException(message))
#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, (message) -> logWarningOnce(message))
: new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
#else
if (tagSection.contains("biomes", 10))
{
biomeContainer =
biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes"))
.promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
#if MC_VER < MC_1_20_6
.getOrThrow(false, (message) -> logWarningOnce(message));
#else
.getOrThrow((message) -> logErrorAndReturnException(message));
#endif
}
else
{
biomeContainer = new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(),
#if MC_VER < MC_1_21_3
biomes.getHolderOrThrow(Biomes.PLAINS),
#else
biomes.getOrThrow(Biomes.PLAINS),
#endif
PalettedContainer.Strategy.SECTION_BIOMES);
}
#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;
}
@@ -433,108 +345,57 @@ public class ChunkFileReader
#else ChunkType #endif
readChunkType(CompoundTag tagLevel)
{
String statusString = tagGetString(tagLevel,"Status");
if (statusString != null)
ChunkStatus chunkStatus = ChunkStatus.byName(tagLevel.getString("Status"));
if (chunkStatus != null)
{
ChunkStatus chunkStatus = ChunkStatus.byName(statusString);
if (chunkStatus != null)
{
return chunkStatus.getChunkType();
}
return chunkStatus.getChunkType();
}
#if MC_VER <= MC_1_20_4
return ChunkStatus.ChunkType.PROTOCHUNK;
#else
return ChunkType.PROTOCHUNK;
#endif
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 = tagGetCompoundTag(chunkData, "Heightmaps");
if (tagHeightmaps != null)
CompoundTag tagHeightmaps = chunkData.getCompound("Heightmaps");
for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter())
{
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 i = 0; i < tagPostProcessings.size(); ++i)
{
ListTag listTag3 = tagPostProcessings.getList(i);
for (int j = 0; j < listTag3.size(); ++j)
{
String heightmap = type.getSerializationKey();
#if MC_VER < MC_1_21_5
if (tagHeightmaps.contains(heightmap, 12))
{
chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmap));
}
#if MC_VER < MC_1_21_3
chunk.addPackedPostProcess(listTag3.getShort(j), i);
#else
if (tagHeightmaps.contains(heightmap))
{
Optional<long[]> optionalHeightmap = tagHeightmaps.getLongArray(heightmap);
if (optionalHeightmap.isPresent())
{
chunk.setHeightmap(type, optionalHeightmap.get());
}
}
chunk.addPackedPostProcess(ShortList.of(listTag3.getShort(j)), i);
#endif
}
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
}
}
// commented out as a test as of 2025-06-04 to see if this is actually necessary for DH
// DH probably doesn't need any chunk post-processing data
//private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData)
//{
// ListTag tagPostProcessings = tagGetListTag(chunkData,"PostProcessing", 9);
// if (tagPostProcessings != null)
// {
// for (int i = 0; i < tagPostProcessings.size(); ++i)
// {
// ListTag listTag3 = tagGetListTag(tagPostProcessings, i);
// for (int j = 0; j < listTag3.size(); ++j)
// {
// #if MC_VER < MC_1_21_3
// chunk.addPackedPostProcess(listTag3.getShort(j), i);
// #else
// chunk.addPackedPostProcess(ShortList.of(tagGetShort(listTag3, j)), i);
// #endif
// }
// }
// }
//}
#if MC_VER >= MC_1_18_2
private static BlendingData readBlendingData(CompoundTag chunkData)
{
BlendingData blendingData = null;
boolean containsBlendingData;
#if MC_VER < MC_1_21_5
containsBlendingData = chunkData.contains("blending_data", 10);
#else
containsBlendingData = chunkData.contains("blending_data");
#endif
if (containsBlendingData)
if (chunkData.contains("blending_data", 10))
{
@SuppressWarnings({"unchecked", "rawtypes"})
Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data"));
try
{
#if MC_VER < MC_1_21_3
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial((message) -> logParsingWarningOnce(message)).orElse(null);
#else
// blending data appears to have changed as of 1.21.6 causing a class cast exception here due to it being wrapped in a Java.Optional
blendingData = BlendingData.unpack(BlendingData.Packed.CODEC.parse(blendingDataTag).resultOrPartial((message) -> logParsingWarningOnce(message)).orElse(null));
#endif
}
catch (Exception e)
{
String message = e.getMessage();
if (message == null || message.trim().isEmpty())
{
message = "Failed to parse blending data";
}
logParsingWarningOnce(message, e);
}
#if MC_VER < MC_1_21_3
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial((message) -> logWarningOnce(message)).orElse(null);
#else
blendingData = BlendingData.unpack(BlendingData.Packed.CODEC.parse(blendingDataTag).resultOrPartial((message) -> logWarningOnce(message)).orElse(null));
#endif
}
return blendingData;
}
@@ -611,37 +472,33 @@ public class ChunkFileReader
// if null all lights = 0
byte[] blockLightNibbleArray = tagGetByteArray(chunkSectionCompoundTag, "BlockLight");
byte[] skyLightNibbleArray = tagGetByteArray(chunkSectionCompoundTag, "SkyLight");
byte[] blockLightNibbleArray = chunkSectionCompoundTag.getByteArray("BlockLight");
byte[] skyLightNibbleArray = chunkSectionCompoundTag.getByteArray("SkyLight");
if (blockLightNibbleArray != null
&& skyLightNibbleArray != null)
// if any sky light was found then all lights above will be max brightness
if (skyLightNibbleArray.length != 0)
{
// 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++)
{
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++)
{
// 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)
{
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);
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);
}
}
}
@@ -690,14 +547,9 @@ public class ChunkFileReader
});
}
private static void logParsingWarningOnce(String message) { logParsingWarningOnce(message, null); }
private static void logParsingWarningOnce(String message, Exception e)
private static void logWarningOnce(String message) { logWarningOnce(message, null); }
private static void logWarningOnce(String message, Exception e)
{
if (message == null)
{
return;
}
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
@@ -724,136 +576,6 @@ public class ChunkFileReader
//====================//
// tag helper methods //
//====================//
// TODO move into separate file (this file is getting long)
// these tag helpers are to simplify tag accessing between MC versions
/** defaults to "false" if the tag isn't present */
private static boolean tagGetBoolean(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 */
private static byte tagGetByte(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 */
private static short tagGetShort(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 */
private static int tagGetInt(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 */
private static long tagGetLong(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
private static String tagGetString(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
private static byte[] tagGetByteArray(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
private static CompoundTag tagGetCompoundTag(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
private static CompoundTag tagGetCompoundTag(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
private static ListTag tagGetListTag(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
private static ListTag tagGetListTag(ListTag tag, int index)
{
#if MC_VER < MC_1_21_5
return tag.getList(index);
#else
return tag.getList(index).orElse(null);
#endif
}
//================//
// helper classes //
@@ -229,16 +229,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
// This is for post ticking for water on gen and stuff like that. Not enabled
// for now.
// if (blockState.hasPostProcess(this, blockPos))
@@ -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.ThreadedParameters;
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(
ThreadedParameters 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<ChunkAccess> getChunksToGenerate(List<ChunkWrapper> chunkWrappers)
{
ArrayList<ChunkAccess> chunksToGenerate = new ArrayList<>();
for (ChunkWrapper chunkWrapper : chunkWrappers)
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (chunkWrapper.getStatus().isOrAfter(this.getChunkStatus()))
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
chunkWrapper.trySetStatus(this.getChunkStatus());
chunksToGenerate.add(chunk);
}
}
return chunksToGenerate;
}
}
@@ -20,13 +20,12 @@
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.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;
@@ -41,35 +40,39 @@ 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(
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers)
ThreadedParameters tParams, WorldGenRegion worldGenRegion,
List<ChunkWrapper> chunkWrappers)
{
ArrayList<ChunkAccess> chunksToDo = this.getChunksToGenerate(chunkWrappers);
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)
{
chunkWrapper.trySetStatus(STATUS);
chunksToDo.add(chunk);
}
}
for (ChunkAccess chunk : chunksToDo)
{
#if MC_VER < MC_1_18_2
@@ -37,10 +37,8 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
import java.util.ConcurrentModificationException;
public final class StepFeatures extends AbstractWorldGenStep
public final class StepFeatures
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
@@ -50,22 +48,10 @@ 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(
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers)
@@ -102,10 +88,6 @@ public final class StepFeatures extends AbstractWorldGenStep
Heightmap.primeHeightmaps(chunk, STATUS.heightmapsAfter());
}
catch (ConcurrentModificationException e) // ReportedException
{
// TODO
}
catch (Exception e)
{
LOGGER.warn("Unexpected issue when generating features for chunk at pos ["+chunkWrapper.getChunkPos()+"], error: ["+e.getMessage()+"].", e);
@@ -26,8 +26,6 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
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;
@@ -43,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;
@@ -51,26 +49,15 @@ 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(
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers)
ThreadedParameters tParams, WorldGenRegion worldGenRegion,
List<ChunkWrapper> chunkWrappers)
{
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
for (ChunkWrapper chunkWrapper : chunkWrappers)
@@ -26,8 +26,6 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
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;
@@ -39,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;
@@ -47,26 +45,15 @@ 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(
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers)
ThreadedParameters tParams, WorldGenRegion worldGenRegion,
List<ChunkWrapper> chunkWrappers)
{
ArrayList<ChunkAccess> chunksToDo = new ArrayList<ChunkAccess>();
for (ChunkWrapper chunkWrapper : chunkWrappers)
@@ -80,7 +67,6 @@ public final class StepStructureReference extends AbstractWorldGenStep
else if (chunk instanceof ProtoChunk)
{
chunkWrapper.trySetStatus(STATUS);
chunksToDo.add(chunk);
}
}
@@ -27,9 +27,7 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
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.resources.ResourceKey;
import net.minecraft.server.level.WorldGenRegion;
import net.minecraft.world.level.Level;
@@ -44,7 +42,7 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
public final class StepStructureStart extends AbstractWorldGenStep
public final class StepStructureStart
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final ChunkStatus STATUS = ChunkStatus.STRUCTURE_STARTS;
@@ -54,27 +52,42 @@ 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(
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers)
public static class StructStartCorruptedException extends RuntimeException
{
ArrayList<ChunkAccess> chunksToDo = this.getChunksToGenerate(chunkWrappers);
private static final long serialVersionUID = -8987434342051563358L;
public StructStartCorruptedException(ArrayIndexOutOfBoundsException e)
{
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 : chunkWrappers)
{
ChunkAccess chunk = chunkWrapper.getChunk();
if (chunkWrapper.getStatus().isOrAfter(STATUS))
{
// this chunk has already generated this step
continue;
}
else if (chunk instanceof ProtoChunk)
{
chunkWrapper.trySetStatus(STATUS);
}
}
#if MC_VER < MC_1_19_2
if (this.environment.params.worldGenSettings.generateFeatures())
@@ -88,6 +101,12 @@ public final class StepStructureStart extends AbstractWorldGenStep
#endif
for (ChunkAccess chunk : chunksToDo)
{
// System.out.println("StepStructureStart: "+chunk.getPos());
// 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();
@@ -132,6 +151,8 @@ public final class StepStructureStart extends AbstractWorldGenStep
{
// 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);
}
}
@@ -26,8 +26,6 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
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;
@@ -39,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;
@@ -47,25 +45,13 @@ 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(
ThreadedParameters tParams, DhLitWorldGenRegion worldGenRegion,
ArrayGridList<ChunkWrapper> chunkWrappers)
ThreadedParameters tParams, WorldGenRegion worldGenRegion,
List<ChunkWrapper> chunkWrappers)
{
ArrayList<ChunkAccess> chunksToDo = new ArrayList<>();
@@ -1,54 +0,0 @@
accessWidener v1 named
# used when determining where to save files to
accessible field net/minecraft/world/level/storage/DimensionDataStorage dataFolder Ljava/nio/file/Path;
# used to help determine what folder a clientLevel is
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J
# used when rendering
accessible method net/minecraft/client/renderer/GameRenderer getFov (Lnet/minecraft/client/Camera;FZ)F
accessible field net/minecraft/client/Minecraft deltaTracker Lnet/minecraft/client/DeltaTracker$Timer;
accessible field net/minecraft/client/renderer/LevelRenderer level Lnet/minecraft/client/multiplayer/ClientLevel;
# used for grabbing vanilla rendered chunks
accessible field net/minecraft/client/renderer/LevelRenderer visibleSections Lit/unimi/dsi/fastutil/objects/ObjectArrayList;
# world generation
# accessible method net/minecraft/world/level/lighting/LayerLightEngine queueSectionData (JLnet/minecraft/world/level/chunk/DataLayer;Z)V
accessible field net/minecraft/world/level/chunk/LevelChunk loaded Z
accessible field net/minecraft/world/level/lighting/LightEngine storage Lnet/minecraft/world/level/lighting/LayerLightSectionStorage;
accessible method net/minecraft/world/level/lighting/LayerLightSectionStorage lightOnInSection (J)Z
accessible field net/minecraft/server/level/ServerChunkCache distanceManager Lnet/minecraft/server/level/DistanceManager;
accessible method net/minecraft/server/level/ChunkMap getUpdatingChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/BooleanSupplier;)V
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# lod generation from save file
accessible field net/minecraft/world/level/chunk/storage/ChunkStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
accessible field net/minecraft/world/level/chunk/storage/IOWorker storage Lnet/minecraft/world/level/chunk/storage/RegionFileStorage;
accessible field net/minecraft/world/level/chunk/storage/RegionFileStorage regionCache Lit/unimi/dsi/fastutil/longs/Long2ObjectLinkedOpenHashMap;
accessible field net/minecraft/world/level/chunk/storage/RegionFileStorage folder Ljava/nio/file/Path;
# grabbing textures
accessible class net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture
accessible method net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture getFrameX (I)I
accessible method net/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture getFrameY (I)I
accessible field net/minecraft/client/renderer/texture/SpriteContents animatedTexture Lnet/minecraft/client/renderer/texture/SpriteContents$AnimatedTexture;
accessible field net/minecraft/client/renderer/texture/SpriteContents originalImage Lcom/mojang/blaze3d/platform/NativeImage;
# UI stuff
accessible field net/minecraft/client/gui/components/AbstractButton SPRITES Lnet/minecraft/client/gui/components/WidgetSprites;
# Handles inserting the config button
accessible field net/minecraft/client/gui/layouts/HeaderAndFooterLayout headerFrame Lnet/minecraft/client/gui/layouts/FrameLayout;
accessible field net/minecraft/client/gui/layouts/FrameLayout children Ljava/util/List;
accessible class net/minecraft/client/gui/layouts/FrameLayout$ChildContainer
accessible field net/minecraft/client/gui/layouts/LinearLayout wrapped Lnet/minecraft/client/gui/layouts/GridLayout;
accessible method net/minecraft/client/gui/components/debug/DebugScreenEntries register (Lnet/minecraft/resources/ResourceLocation;Lnet/minecraft/client/gui/components/debug/DebugScreenEntry;)Lnet/minecraft/resources/ResourceLocation;
# hacky stuff
accessible field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
mutable field net/minecraft/util/ThreadingDetector lock Ljava/util/concurrent/Semaphore;
+7 -49
View File
@@ -1,5 +1,5 @@
plugins {
id "fabric-loom" version "1.10-SNAPSHOT"
id "fabric-loom" version "1.8-SNAPSHOT"
}
loom {
@@ -72,27 +72,19 @@ dependencies {
// runtimeOnly "javax.annotation:javax.annotation-api:1.3.2"
// compileOnly "javax.annotation:javax.annotation-api:1.3.2"
// modImplementation "javax.annotation:javax.annotation-api:1.3.2"
// Fabric API
addModJar(fabricApi.module("fabric-api-base", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-lifecycle-events-v1", rootProject.fabric_api_version))
if (buildVersionBefore(minecraft_version, "1.21.10"))
{
addModJar(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version))
}
else // > 1.21.10
{
addModJar(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-resource-loader-v1", rootProject.fabric_api_version))
}
addModJar(fabricApi.module("fabric-resource-loader-v0", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-events-interaction-v0", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-rendering-v1", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-rendering-v1", rootProject.fabric_api_version)) // TODO: Remove this as it is only needed in 1 line (FabricClientProxy)
addModJar(fabricApi.module("fabric-networking-api-v1", rootProject.fabric_api_version))
addModJar(fabricApi.module("fabric-entity-events-v1", rootProject.fabric_api_version))
if (buildVersionBefore(minecraft_version, "1.19.2"))
addModJar(fabricApi.module("fabric-command-api-v1", rootProject.fabric_api_version))
else
if (minecraft_version >= "1.19.2")
addModJar(fabricApi.module("fabric-command-api-v2", rootProject.fabric_api_version))
else // < 1.19.2
addModJar(fabricApi.module("fabric-command-api-v1", rootProject.fabric_api_version))
// used by mod menu in MC 1.20.6+
addModJar(fabricApi.module("fabric-screen-api-v1", rootProject.fabric_api_version))
@@ -151,40 +143,6 @@ dependencies {
}
}
private static boolean buildVersionBefore(String minecraft_version, String compareVersion)
{
int sortValue = sortSemanticVersionOldestToNewest(minecraft_version, compareVersion);
return sortValue == -1;
}
/**
* input format: "major.minor.patch"
* needed so we can sort versions with different length strings
* IE: 1.21.1 should come before 1.21.10
*/
private static int sortSemanticVersionOldestToNewest(String version1, String version2)
{
String[] parts1 = version1.split("\\.");
String[] parts2 = version2.split("\\.");
int major1 = Integer.parseInt(parts1[0]);
int major2 = Integer.parseInt(parts2[0]);
if (major1 != major2)
{
return Integer.compare(major1, major2);
}
int minor1 = Integer.parseInt(parts1[1]);
int minor2 = Integer.parseInt(parts2[1]);
if (minor1 != minor2)
{
return Integer.compare(minor1, minor2);
}
int patch1 = Integer.parseInt(parts1[2]);
int patch2 = Integer.parseInt(parts2[2]);
return Integer.compare(patch1, patch2);
}
task deleteResources(type: Delete) {
delete file("build/resources/main")
@@ -24,24 +24,25 @@ import com.seibel.distanthorizons.common.AbstractPluginPacketSender;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.mojang.blaze3d.platform.InputConstants;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.ISodiumAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.fabric.wrappers.modAccessor.SodiumAccessor;
import net.fabricmc.api.EnvType;
import net.fabricmc.api.Environment;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientChunkEvents;
import net.fabricmc.fabric.api.client.event.lifecycle.v1.ClientTickEvents;
import net.fabricmc.fabric.api.client.networking.v1.ClientPlayNetworking;
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
import net.fabricmc.fabric.api.event.player.AttackBlockCallback;
import net.fabricmc.fabric.api.event.player.UseBlockCallback;
import net.minecraft.client.Minecraft;
@@ -59,10 +60,7 @@ import java.nio.FloatBuffer;
#endif
import java.util.HashSet;
import java.util.concurrent.AbstractExecutorService;
#if MC_VER < MC_1_21_10
import net.fabricmc.fabric.api.client.rendering.v1.WorldRenderEvents;
#endif
import java.util.concurrent.ThreadPoolExecutor;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.world.InteractionResult;
@@ -84,7 +82,6 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
{
private final ClientApi clientApi = ClientApi.INSTANCE;
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final AbstractPluginPacketSender PACKET_SENDER = (AbstractPluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
// TODO we shouldn't be filtering keys on the Forge/Fabric side, only in ClientApi
@@ -104,7 +101,6 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
LOGGER.info("Registering Fabric Client Events");
//========================//
// register mod accessors //
//========================//
@@ -219,83 +215,76 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// render event //
//==============//
// TODO wait for fabric to re-add their rendering API
#if MC_VER < MC_1_21_10
WorldRenderEvents.AFTER_SETUP.register((renderContext) ->
{
ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix());
Mat4f projectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix());
Mat4f modelViewMatrix;
#if MC_VER < MC_1_20_6
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(renderContext.matrixStack().last().pose());
modelViewMatrix = McObjectConverter.Convert(renderContext.matrixStack().last().pose());
#else
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(renderContext.positionMatrix());
modelViewMatrix = McObjectConverter.Convert(renderContext.positionMatrix());
#endif
#if MC_VER < MC_1_21_1
ClientApi.RENDER_STATE.frameTime = renderContext.tickDelta();
#else
ClientApi.RENDER_STATE.frameTime = renderContext.tickCounter().getGameTimeDeltaTicks();
#endif
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, renderContext.world());
this.clientApi.renderLods();
this.clientApi.renderLods(ClientLevelWrapper.getWrapper(renderContext.world()),
modelViewMatrix,
projectionMatrix,
#if MC_VER < MC_1_21_1
renderContext.tickDelta()
#else
renderContext.tickCounter().getGameTimeDeltaTicks()
#endif
);
});
// TODO add to forge and neo
WorldRenderEvents.AFTER_ENTITIES.register((renderContext) ->
{
ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix());
Mat4f projectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix());
Mat4f modelViewMatrix;
#if MC_VER < MC_1_20_6
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(renderContext.matrixStack().last().pose());
modelViewMatrix = McObjectConverter.Convert(renderContext.matrixStack().last().pose());
#else
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(renderContext.positionMatrix());
modelViewMatrix = McObjectConverter.Convert(renderContext.positionMatrix());
#endif
#if MC_VER < MC_1_21_1
ClientApi.RENDER_STATE.frameTime = renderContext.tickDelta();
#else
ClientApi.RENDER_STATE.frameTime = renderContext.tickCounter().getGameTimeDeltaTicks();
#endif
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, renderContext.world());
this.clientApi.renderFadeOpaque();
this.clientApi.renderFadeOpaque(
modelViewMatrix,
projectionMatrix,
#if MC_VER < MC_1_21_1
renderContext.tickDelta(),
#else
renderContext.tickCounter().getGameTimeDeltaTicks(),
#endif
ClientLevelWrapper.getWrapper(renderContext.world())
);
});
// TODO add to forge and neo
WorldRenderEvents.AFTER_TRANSLUCENT.register((renderContext) ->
{
ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix());
Mat4f projectionMatrix = McObjectConverter.Convert(renderContext.projectionMatrix());
Mat4f modelViewMatrix;
#if MC_VER < MC_1_20_6
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(renderContext.matrixStack().last().pose());
modelViewMatrix = McObjectConverter.Convert(renderContext.matrixStack().last().pose());
#else
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(renderContext.positionMatrix());
modelViewMatrix = McObjectConverter.Convert(renderContext.positionMatrix());
#endif
#if MC_VER < MC_1_21_1
ClientApi.RENDER_STATE.frameTime = renderContext.tickDelta();
#else
ClientApi.RENDER_STATE.frameTime = renderContext.tickCounter().getGameTimeDeltaTicks();
#endif
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, renderContext.world());
#if MC_VER < MC_1_21_6
// rendered in MixinLevelRenderer
#else
ClientApi.INSTANCE.renderDeferredLodsForShaders();
#endif
this.clientApi.renderFadeTransparent();
this.clientApi.renderFade(
modelViewMatrix,
projectionMatrix,
#if MC_VER < MC_1_21_1
renderContext.tickDelta(),
#else
renderContext.tickCounter().getGameTimeDeltaTicks(),
#endif
ClientLevelWrapper.getWrapper(renderContext.world())
);
});
#endif
// Debug keyboard event
@@ -327,7 +316,9 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
#else
ClientPlayNetworking.registerGlobalReceiver(AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE, (client, handler, buffer, packetSender) ->
{
AbstractNetworkMessage message = PACKET_SENDER.decodeMessage(buffer);
// Forge packet ID
buffer.readByte();
AbstractNetworkMessage message = AbstractPluginPacketSender.decodeMessage(buffer);
if (message != null)
{
ClientApi.INSTANCE.pluginMessageReceived(message);
@@ -345,18 +336,18 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
// Check all keys we need
for (int keyCode = GLFW.GLFW_KEY_A; keyCode <= GLFW.GLFW_KEY_Z; keyCode++)
{
//if (InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), keyCode))
//{
// currentKeyDown.add(keyCode);
//}
if (InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), keyCode))
{
currentKeyDown.add(keyCode);
}
}
for (int keyCode : KEY_TO_CHECK_FOR)
{
//if (InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), keyCode))
//{
// currentKeyDown.add(keyCode);
//}
if (InputConstants.isKeyDown(Minecraft.getInstance().getWindow().getWindow(), keyCode))
{
currentKeyDown.add(keyCode);
}
}
// Diff and trigger events
@@ -20,7 +20,6 @@
package com.seibel.distanthorizons.fabric;
import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode;
import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.ConfigBase;
@@ -91,8 +90,8 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
{
ModAccessorInjector.INSTANCE.bind(ISodiumAccessor.class, new SodiumAccessor());
// If sodium is installed Indium is also necessary for versions 0.5 and less in order to use the Fabric rendering API
if (!modChecker.isModLoaded("indium") && SodiumAccessor.isSodiumV5OrLess)
// If sodium is installed Indium is also necessary in order to use the Fabric rendering API
if (!modChecker.isModLoaded("indium"))
{
String indiumMissingMessage = ModInfo.READABLE_NAME + " needs Indium to work with Sodium.\nPlease download Indium from https://modrinth.com/mod/indium";
LOGGER.fatal(indiumMissingMessage);
@@ -127,8 +126,7 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
}
@Override
protected void subscribeClientStartedEvent(Runnable eventHandler)
{ ClientLifecycleEvents.CLIENT_STARTED.register((mc) -> eventHandler.run()); }
protected void subscribeClientStartedEvent(Runnable eventHandler) { ClientLifecycleEvents.CLIENT_STARTED.register((mc) -> eventHandler.run()); }
@Override
protected void subscribeServerStartingEvent(Consumer<MinecraftServer> eventHandler)
@@ -22,7 +22,9 @@ public class FabricPluginPacketSender extends AbstractPluginPacketSender
ClientPlayNetworking.send(new CommonPacketPayload(message));
#else // < 1.20.6
FriendlyByteBuf buffer = PacketByteBufs.create();
this.encodeMessage(buffer, message);
// Forge packet ID
buffer.writeByte(0);
AbstractPluginPacketSender.encodeMessage(buffer, message);
ClientPlayNetworking.send(WRAPPER_PACKET_RESOURCE, buffer);
#endif
}
@@ -34,7 +36,9 @@ public class FabricPluginPacketSender extends AbstractPluginPacketSender
ServerPlayNetworking.send(serverPlayer, new CommonPacketPayload(message));
#else // < 1.20.6
FriendlyByteBuf buffer = PacketByteBufs.create();
this.encodeMessage(buffer, message);
// Forge packet ID
buffer.writeByte(0);
AbstractPluginPacketSender.encodeMessage(buffer, message);
ServerPlayNetworking.send(serverPlayer, WRAPPER_PACKET_RESOURCE, buffer);
#endif
}
@@ -1,7 +1,5 @@
package com.seibel.distanthorizons.fabric;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkProcessingEvent;
import com.seibel.distanthorizons.api.methods.events.DhApiEventRegister;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent;
import com.seibel.distanthorizons.common.AbstractModInitializer;
@@ -9,14 +7,11 @@ import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.common.AbstractPluginPacketSender;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.fabric.testing.TestChunkInputReplacerEvent;
import com.seibel.distanthorizons.fabric.testing.TestWorldGenBindingEvent;
import net.fabricmc.fabric.api.entity.event.v1.ServerEntityWorldChangeEvents;
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerChunkEvents;
@@ -37,8 +32,11 @@ import com.seibel.distanthorizons.common.CommonPacketPayload;
import net.fabricmc.fabric.api.networking.v1.PayloadTypeRegistry;
#else
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import com.seibel.distanthorizons.common.AbstractPluginPacketSender;
#endif
import java.util.function.Supplier;
/**
* This handles all events sent to the server,
* and is the starting point for most of the mod.
@@ -50,8 +48,6 @@ import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
public class FabricServerProxy implements AbstractModInitializer.IEventProxy
{
private static final ServerApi SERVER_API = ServerApi.INSTANCE;
@SuppressWarnings("unused")
private static final AbstractPluginPacketSender PACKET_SENDER = (AbstractPluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private final boolean isDedicatedServer;
@@ -97,11 +93,10 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
ServerTickEvents.END_SERVER_TICK.register((server) -> SERVER_API.serverTickEvent());
// can be enabled to test overrides/events without having to build a separate API project
// can be enabled to test world gen overrides without having to build a separate API project
if (false)
{
DhApiEventRegister.on(DhApiLevelLoadEvent.class, new TestWorldGenBindingEvent());
DhApi.events.bind(DhApiChunkProcessingEvent.class, new TestChunkInputReplacerEvent());
}
@@ -197,7 +192,9 @@ public class FabricServerProxy implements AbstractModInitializer.IEventProxy
#else
ServerPlayNetworking.registerGlobalReceiver(AbstractPluginPacketSender.WRAPPER_PACKET_RESOURCE, (server, serverPlayer, handler, buffer, packetSender) ->
{
AbstractNetworkMessage message = PACKET_SENDER.decodeMessage(buffer);
// Forge packet ID
buffer.readByte();
AbstractNetworkMessage message = AbstractPluginPacketSender.decodeMessage(buffer);
if (message != null)
{
ServerApi.INSTANCE.pluginMessageReceived(ServerPlayerWrapper.getWrapper(serverPlayer), message);
@@ -1,70 +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.fabric.mixins.client;
#if MC_VER < MC_1_21_10
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(Entity.class)
public class MixinChunkSectionsToRender
{ /* rendering before was handled via Fabric API events */ }
#else
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.chunk.ChunkSectionLayerGroup;
import net.minecraft.client.renderer.chunk.ChunkSectionsToRender;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(ChunkSectionsToRender.class)
public class MixinChunkSectionsToRender
{
// needs to fire at HEAD with a lower than normal order (less than 1000)
// otherwise it will be canceled by Sodium
@Inject(at = @At("HEAD"), method = "renderGroup", order = 800)
private void renderDeferredLayer(ChunkSectionLayerGroup chunkSectionLayerGroup, CallbackInfo ci)
{
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, Minecraft.getInstance().levelRenderer.level);
if (chunkSectionLayerGroup == ChunkSectionLayerGroup.TRANSLUCENT)
{
ClientApi.INSTANCE.renderFadeTransparent();
ClientApi.INSTANCE.renderDeferredLodsForShaders();
}
else if (chunkSectionLayerGroup == ChunkSectionLayerGroup.TRIPWIRE)
{
ClientApi.INSTANCE.renderFadeOpaque();
}
}
}
#endif
@@ -12,15 +12,12 @@ import java.util.List;
@Mixin(DebugScreenOverlay.class)
public class MixinDebugScreenOverlay
{
#if MC_VER < MC_1_21_10
@Inject(method = "getSystemInformation", at = @At("RETURN"))
private void addCustomF3(CallbackInfoReturnable<List<String>> cir)
{
List<String> messages = cir.getReturnValue();
F3Screen.addStringToDisplay(messages);
}
#else
// handled by DhDebugScreenEntry for MC versions after 1.21.10
#endif
}
@@ -22,51 +22,31 @@ package com.seibel.distanthorizons.fabric.mixins.client;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import net.minecraft.client.Minecraft;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
#if MC_VER < MC_1_17_1
import net.minecraft.world.level.material.FluidState;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode;
import com.mojang.blaze3d.systems.RenderSystem;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#elif MC_VER < MC_1_21_3
import net.minecraft.world.level.material.FogType;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode;
import com.mojang.blaze3d.systems.RenderSystem;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#elif MC_VER < MC_1_21_6
#else
import net.minecraft.world.level.material.FogType;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.mojang.blaze3d.shaders.FogShape;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode;
import net.minecraft.client.renderer.FogParameters;
import org.joml.Vector4f;
import com.mojang.blaze3d.systems.RenderSystem;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#else
import net.minecraft.world.level.material.FogType;
import net.minecraft.client.renderer.fog.FogRenderer;
import net.minecraft.client.renderer.fog.FogData;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
#endif
@Mixin(FogRenderer.class)
@@ -86,23 +66,18 @@ public class MixinFogRenderer
#elif MC_VER < MC_1_21_3
@Inject(at = @At("RETURN"), method = "setupFog")
private static void disableSetupFog(Camera camera, FogMode fogMode, float f, boolean bl, float g, CallbackInfo callback)
#elif MC_VER < MC_1_21_6
#else
@Inject(at = @At("RETURN"), method = "setupFog", cancellable = true)
private static void disableSetupFog(Camera camera, FogMode fogMode, Vector4f vector4f, float f, boolean bl, float g, CallbackInfoReturnable<FogParameters> callback)
#else
@Unique
private static void unused()
#endif
{
#if MC_VER < MC_1_21_6
boolean cancelFog = cancelFog(camera, fogMode);
#elif MC_VER < MC_1_21_6
boolean cancelFog = cancelFog(camera);
#else
boolean cancelFog = cancelFog();
#endif
boolean cameraNotInFluid = cameraNotInFluid(camera);
if (cancelFog)
Entity entity = camera.getEntity();
boolean isSpecialFog = (entity instanceof LivingEntity) && ((LivingEntity) entity).hasEffect(MobEffects.BLINDNESS);
if (!isSpecialFog && cameraNotInFluid && fogMode == FogMode.FOG_TERRAIN
&& !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial()
&& !Config.Client.Advanced.Graphics.Fog.enableVanillaFog.get())
{
#if MC_VER < MC_1_17_1
RenderSystem.fogStart(A_REALLY_REALLY_BIG_VALUE);
@@ -110,74 +85,10 @@ public class MixinFogRenderer
#elif MC_VER < MC_1_21_3
RenderSystem.setShaderFogStart(A_REALLY_REALLY_BIG_VALUE);
RenderSystem.setShaderFogEnd(A_EVEN_LARGER_VALUE);
#elif MC_VER < MC_1_21_6
callback.setReturnValue(FogParameters.NO_FOG);
#else
callback.setReturnValue(FogParameters.NO_FOG);
#endif
}
}
#if MC_VER < MC_1_21_6
#else
// In MC's FogRenderer they clamp the "renderDistanceEnd" fog field to the render distance,
// which prevents us from disabling the vanilla fog.
// This mixin fires after they set the "renderDistanceEnd" so we can change it.
@WrapOperation(
method = "setupFog",
at = @At(
value = "FIELD",
target = "Lnet/minecraft/client/renderer/fog/FogData;renderDistanceEnd:F",
opcode = org.objectweb.asm.Opcodes.PUTFIELD
)
)
private void onSetRenderDistanceEnd(FogData instance, float value, Operation<Void> original)
{
if (cancelFog())
{
instance.environmentalStart = A_REALLY_REALLY_BIG_VALUE;
instance.environmentalEnd = A_EVEN_LARGER_VALUE;
instance.renderDistanceStart = A_REALLY_REALLY_BIG_VALUE;
instance.renderDistanceEnd = A_EVEN_LARGER_VALUE;
}
// Always call the original with the modified or original value
original.call(instance, value);
}
#endif
@Unique
#if MC_VER < MC_1_21_6
private static boolean cancelFog(Camera camera, FogMode fogMode)
#else
private static boolean cancelFog()
#endif
{
#if MC_VER < MC_1_21_6
Entity entity = camera.getEntity();
#else
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
Entity entity = camera.getEntity();
#endif
boolean cameraNotInFluid = cameraNotInFluid(camera);
boolean isSpecialFog = (entity instanceof LivingEntity) && ((LivingEntity) entity).hasEffect(MobEffects.BLINDNESS);
boolean cancelFog = !isSpecialFog;
cancelFog = cancelFog && cameraNotInFluid;
#if MC_VER < MC_1_21_6
cancelFog = cancelFog && (fogMode == FogMode.FOG_TERRAIN);
#endif
cancelFog = cancelFog && !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial();
cancelFog = cancelFog && !Config.Client.Advanced.Graphics.Fog.enableVanillaFog.get();
return cancelFog;
}
@Unique
@@ -194,6 +105,4 @@ public class MixinFogRenderer
return cameraNotInFluid;
}
}
@@ -19,78 +19,51 @@
package com.seibel.distanthorizons.fabric.mixins.client;
#if MC_VER < MC_1_19_4
import net.minecraft.client.renderer.RenderType;
import com.mojang.blaze3d.vertex.PoseStack;
#if MC_VER < MC_1_19_4
import com.mojang.math.Matrix4f;
import org.lwjgl.opengl.GL32;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#elif MC_VER < MC_1_21_6
import net.minecraft.client.renderer.RenderType;
import com.mojang.blaze3d.vertex.PoseStack;
import org.joml.Matrix4f;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#elif MC_VER < MC_1_21_10
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.framegraph.FrameGraphBuilder;
import com.mojang.blaze3d.resource.GraphicsResourceAllocator;
import net.minecraft.client.Camera;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.renderer.chunk.ChunkSectionsToRender;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.util.profiling.ProfilerFiller;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.joml.Vector4f;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
#else
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.framegraph.FrameGraphBuilder;
import com.mojang.blaze3d.resource.GraphicsResourceAllocator;
import net.minecraft.client.Camera;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.renderer.chunk.ChunkSectionsToRender;
import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.client.renderer.state.LevelRenderState;
import net.minecraft.util.profiling.ProfilerFiller;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.joml.Vector4f;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
#endif
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LightTexture;
import com.seibel.distanthorizons.core.config.Config;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.RenderType;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import org.apache.logging.log4j.Logger;
/**
* This class is used to mix in my rendering code
* before Minecraft starts rendering blocks.
* If this wasn't done, and we used Forge's
* render last event, the LODs would render on top
* of the normal terrain.
*
* This is also the mixin for rendering the clouds
*
* @author coolGi
* @author James Seibel
* @version 12-31-2021
*/
@Mixin(LevelRenderer.class)
public class MixinLevelRenderer
{
@Shadow
private ClientLevel level;
@Unique
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
#if MC_VER < MC_1_17_1
@Inject(at = @At("HEAD"),
method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDD)V",
@@ -111,94 +84,57 @@ public class MixinLevelRenderer
method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double camX, double camY, double camZ, Matrix4f projectionMatrix, CallbackInfo callback)
#elif MC_VER < MC_1_21_6
#else
@Inject(at = @At("HEAD"),
method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;DDDLorg/joml/Matrix4f;Lorg/joml/Matrix4f;)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, double x, double y, double z, Matrix4f projectionMatrix, Matrix4f frustumMatrix, CallbackInfo callback)
#elif MC_VER < MC_1_21_10
@Inject(at = @At("HEAD"), method = "prepareChunkRenders", cancellable = true)
private void prepareChunkRenders(Matrix4fc projectionMatrix, double d, double e, double f, CallbackInfoReturnable<ChunkSectionsToRender> callback)
#else
@Inject(at = @At("HEAD"), method = "renderLevel")
private void renderLevel(
GraphicsResourceAllocator resourceAllocator, DeltaTracker deltaTracker,
boolean renderBlockOutline, Camera camera,
Matrix4f positionMatrix, Matrix4f projectionMatrix, Matrix4f idkMatrix, GpuBufferSlice gpuBufferSlice,
Vector4f skyColor, boolean thinFog, CallbackInfo callback)
#endif
{
#if MC_VER == MC_1_16_5
// get the matrices from the OpenGL fixed pipeline
float[] mcProjMatrixRaw = new float[16];
GL32.glGetFloatv(GL32.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
ClientApi.RENDER_STATE.mcProjectionMatrix = new Mat4f(mcProjMatrixRaw);
ClientApi.RENDER_STATE.mcProjectionMatrix.transpose();
Mat4f mcProjectionMatrix = new Mat4f(mcProjMatrixRaw);
mcProjectionMatrix.transpose();
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(matrixStackIn.last().pose());
Mat4f mcModelViewMatrix = McObjectConverter.Convert(matrixStackIn.last().pose());
#elif MC_VER <= MC_1_20_4
// get the matrices directly from MC
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose());
ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
#elif MC_VER < MC_1_21_10
// MC combined the model view and projection matricies
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(projectionMatrix);
ClientApi.RENDER_STATE.mcProjectionMatrix = new Mat4f();
ClientApi.RENDER_STATE.mcProjectionMatrix.setIdentity();
Mat4f mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose());
Mat4f mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
#else
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(positionMatrix);
ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
// MC combined the model view and projection matricies
Mat4f mcModelViewMatrix = McObjectConverter.Convert(projectionMatrix);
Mat4f mcProjectionMatrix = new Mat4f();
mcProjectionMatrix.setIdentity();
#endif
// TODO move this into a common place
float frameTime;
#if MC_VER < MC_1_21_1
ClientApi.RENDER_STATE.frameTime = Minecraft.getInstance().getFrameTime();
frameTime = Minecraft.getInstance().getFrameTime();
#elif MC_VER < MC_1_21_3
ClientApi.RENDER_STATE.frameTime = Minecraft.getInstance().getTimer().getRealtimeDeltaTicks();
frameTime = Minecraft.getInstance().getTimer().getRealtimeDeltaTicks();
#else
ClientApi.RENDER_STATE.frameTime = Minecraft.getInstance().deltaTracker.getRealtimeDeltaTicks();
frameTime = Minecraft.getInstance().deltaTracker.getRealtimeDeltaTicks();
#endif
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, this.level);
#if MC_VER < MC_1_21_6
if (renderType.equals(RenderType.translucent()))
{
ClientApi.INSTANCE.renderDeferredLodsForShaders();
ClientApi.INSTANCE.renderDeferredLods(ClientLevelWrapper.getWrapper(this.level),
mcModelViewMatrix,
mcProjectionMatrix,
frameTime
);
}
#elif MC_VER < MC_1_21_10
// rendering handled via Fabric Api render event
#else
// handled here and in MixinChunkSectionsToRender
#endif
// FIXME completely disables rendering when sodium is installed
if (Config.Client.Advanced.Debugging.lodOnlyMode.get())
{
callback.cancel();
}
}
#if MC_VER < MC_1_21_10
// rendering handled via Fabric Api render event
#else
@Inject(at = @At("HEAD"), method = "prepareChunkRenders")
private void prepareChunkRenders(Matrix4fc modelViewMatrix, double d, double e, double f, CallbackInfoReturnable<ChunkSectionsToRender> callback)
{
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrix);
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, this.level);
// only crash during development
if (ModInfo.IS_DEV_BUILD)
{
ClientApi.RENDER_STATE.canRenderOrThrow();
}
ClientApi.INSTANCE.renderLods();
}
#endif
}
@@ -22,7 +22,6 @@ package com.seibel.distanthorizons.fabric.mixins.client;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.renderer.LightTexture;
@@ -35,34 +34,21 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#if MC_VER < MC_1_21_3
import com.mojang.blaze3d.platform.NativeImage;
#elif MC_VER < MC_1_21_5
import com.mojang.blaze3d.pipeline.TextureTarget;
#else
import com.mojang.blaze3d.opengl.GlTexture;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.GpuTexture;
import com.mojang.blaze3d.pipeline.TextureTarget;
#endif
@Mixin(LightTexture.class)
public class MixinLightTexture
{
#if MC_VER < MC_1_21_3
@Shadow
@Final
@Final
#if MC_VER < MC_1_21_3
private NativeImage lightPixels;
#elif MC_VER < MC_1_21_5
@Shadow
@Final
private TextureTarget target;
#else
@Shadow
@Final
private GpuTexture texture;
private TextureTarget target;
#endif
@Inject(method = "updateLightTexture(F)V", at = @At("RETURN"))
public void updateLightTexture(float partialTicks, CallbackInfo ci)
{
@@ -74,15 +60,11 @@ public class MixinLightTexture
IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
MinecraftRenderWrapper renderWrapper = (MinecraftRenderWrapper)SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
#if MC_VER < MC_1_21_3
renderWrapper.updateLightmap(this.lightPixels, clientLevel);
#elif MC_VER < MC_1_21_5
renderWrapper.setLightmapId(this.target.getColorTextureId(), clientLevel);
MinecraftRenderWrapper.INSTANCE.updateLightmap(this.lightPixels, clientLevel);
#else
GlTexture glTexture = (GlTexture) this.texture;
renderWrapper.setLightmapId(glTexture.glId(), clientLevel);
MinecraftRenderWrapper.INSTANCE.setLightmapId(this.target.getColorTextureId(), clientLevel);
#endif
}
@@ -76,25 +76,15 @@ public abstract class MixinMinecraft
if (SelfUpdater.onStart() || DEBUG_ALWAYS_SHOW_UPDATER)
{
try
{
instance.setScreen(new UpdateModScreen(
new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
(Config.Client.Advanced.AutoUpdater.updateBranch.get() == EDhApiUpdateBranch.STABLE ? ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()): GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha"))
));
return;
}
catch (Exception e)
{
// info instead of error since this can be ignored and probably just means
// there isn't a new DH version available
LOGGER.info("Unable to show DH update screen, reason: ["+e.getMessage()+"].");
}
instance.setScreen(new UpdateModScreen(
new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
(Config.Client.Advanced.AutoUpdater.updateBranch.get() == EDhApiUpdateBranch.STABLE ? ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()): GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha"))
));
}
else
{
instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened
}
// Sets the screen back to the vanilla screen as if nothing ever happened
// if not done the game will crash
instance.setScreen(guiScreen);
}
#endif
@@ -134,13 +124,14 @@ public abstract class MixinMinecraft
{
try
{
Minecraft.getInstance().setScreen(new UpdateModScreen(
// TODO: Change to runnable, instead of tittle screen
new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
versionId
));
}
catch (Exception e)
catch (IllegalArgumentException e)
{
// info instead of error since this can be ignored and probably just means
// there isn't a new DH version available
@@ -1,15 +1,12 @@
package com.seibel.distanthorizons.fabric.mixins.client;
#if MC_VER < MC_1_21_5
import com.mojang.blaze3d.platform.GlStateManager;
#endif
import com.mojang.blaze3d.platform.TextureUtil;
import com.seibel.distanthorizons.core.config.Config;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
/**
* Sets Minecraft's LOD Bias (looks similar to mipmaps)
*
@@ -18,9 +15,6 @@ import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(TextureUtil.class)
public class MixinTextureUtil
{
// TODO fix for MC 1.21.5+
#if MC_VER < MC_1_21_5
@Redirect(method = "Lcom/mojang/blaze3d/platform/TextureUtil;prepareImage(Lcom/mojang/blaze3d/platform/NativeImage$InternalGlFormat;IIII)V",
at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/GlStateManager;_texParameter(IIF)V", #if MC_VER == MC_1_16_5 remap = true #else remap = false #endif))
private static void setLodBias(int target, int pname, float param)
@@ -33,6 +27,5 @@ public class MixinTextureUtil
GlStateManager._texParameter(target, pname, biasValue);
}
}
#endif
}
@@ -1,16 +1,24 @@
package com.seibel.distanthorizons.fabric.mixins.server;
#if MC_VER < MC_1_21_4
#if MC_VER < MC_1_21_3
import net.minecraft.Util;
import net.minecraft.world.entity.Entity;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(Entity.class)
public class MixinLevelTicks<T>
/**
* {@link MixinUtilBackgroundThread} was used for versions before 1.21.3
* This is just a dummy class/mixin to make the compiler happy.
*
* @see MixinUtilBackgroundThread
*/
//@Mixin(net.minecraft.minecraft.class) // TODO we should allow version specific mixins so we don't have to create dummy mixins that exist for all MC versions
@Mixin(Util.class)
public class MixinLevelTicks
{
// dummy mixin to make the loader happy
}
#else
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck;
@@ -23,7 +31,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LevelTicks.class) // available in 1.18.2+, but only needed in 1.21.4+
@Mixin(LevelTicks.class)
public class MixinLevelTicks<T>
{
// TODO put in a common location
@@ -33,7 +41,7 @@ public class MixinLevelTicks<T>
@Inject(method = "schedule", at = @At(value = "HEAD"), cancellable = true)
private void onChunkSave(ScheduledTick<T> tick, CallbackInfo ci)
private void schedule(ScheduledTick<T> tick, CallbackInfo ci)
{
// In MC 1.21.4 an error check was added to log attempting to schedule ticks for unloaded chunks
// this caused a lot of unnecessary errors when generating sand (FallingBlock.class).
@@ -44,5 +52,4 @@ public class MixinLevelTicks<T>
}
}
#endif
@@ -1,64 +0,0 @@
package com.seibel.distanthorizons.fabric.testing;
import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiChunkProcessingEvent;
import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiEventParam;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import org.apache.logging.log4j.Logger;
import java.io.IOException;
public class TestChunkInputReplacerEvent extends DhApiChunkProcessingEvent
{
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private static final String REPLACEMENT_BLOCK_STATE_NAMESPACE = "minecraft:stone";
private IDhApiBlockStateWrapper stoneBlockWrapper = null;
private boolean initialBlockSetupComplete = false;
@Override
public void blockOrBiomeChangedDuringChunkProcessing(DhApiEventParam<EventParam> event)
{
if (!this.initialBlockSetupComplete)
{
// this method can be called on multiple threads
synchronized (this)
{
this.initialBlockSetupComplete = true;
try
{
this.stoneBlockWrapper = DhApi.Delayed.wrapperFactory.getDefaultBlockStateWrapper(REPLACEMENT_BLOCK_STATE_NAMESPACE, event.value.levelWrapper);
}
catch (IOException e)
{
LOGGER.error("Unable to get ["+REPLACEMENT_BLOCK_STATE_NAMESPACE+"] block replacement cannot continue and is now disabled, error: ["+e.getMessage()+"].", e);
DhApi.events.unbind(DhApiChunkProcessingEvent.class, this.getClass());
}
}
}
// will happen if the initial setup fails until the unbind call is processed
// which likely won't happen until the current chunk has finished processing
if (this.stoneBlockWrapper == null)
{
return;
}
// replace any dirt or grass block with stone
IDhApiBlockStateWrapper block = event.value.currentBlock;
if (block.getSerialString().contains("grass_block")
|| block.getSerialString().contains("dirt"))
{
event.value.setBlockOverride(this.stoneBlockWrapper);
}
}
}
@@ -10,9 +10,11 @@ import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper;
import com.seibel.distanthorizons.api.objects.data.DhApiChunk;
import com.seibel.distanthorizons.api.objects.data.DhApiTerrainDataPoint;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.chunk.ChunkAccess;
import org.apache.logging.log4j.Logger;
import java.util.ArrayList;
@@ -46,8 +46,8 @@ public class TestGenericWorldGenerator implements IDhApiWorldGenerator
public byte getSmallestDataDetailLevel() { return (byte) (EDhApiDetailLevel.BLOCK.detailLevel); }
@Override
public byte getLargestDataDetailLevel()
{ return (byte) (EDhApiDetailLevel.BLOCK.detailLevel + 12); }
//{ return (byte) (EDhApiDetailLevel.BLOCK.detailLevel); }
//{ return (byte) (EDhApiDetailLevel.BLOCK.detailLevel + 12); }
{ return (byte) (EDhApiDetailLevel.BLOCK.detailLevel); }
@Override
@@ -105,43 +105,43 @@ public class TestGenericWorldGenerator implements IDhApiWorldGenerator
{
case 0:
blockResourceLocation = "minecraft:red_wool";
maxHeight = 20;
maxHeight = 60;
break;
case 1:
blockResourceLocation = "minecraft:orange_wool";
maxHeight = 30;
maxHeight = 70;
break;
case 2:
blockResourceLocation = "minecraft:yellow_wool";
maxHeight = 40;
maxHeight = 80;
break;
case 3:
blockResourceLocation = "minecraft:lime_wool";
maxHeight = 50;
maxHeight = 90;
break;
case 4:
blockResourceLocation = "minecraft:cyan_wool";
maxHeight = 60;
maxHeight = 100;
break;
case 5:
blockResourceLocation = "minecraft:blue_wool";
maxHeight = 70;
maxHeight = 100;
break;
case 6:
blockResourceLocation = "minecraft:magenta_wool";
maxHeight = 80;
maxHeight = 110;
break;
case 7:
blockResourceLocation = "minecraft:white_wool";
maxHeight = 90;
maxHeight = 120;
break;
case 8:
blockResourceLocation = "minecraft:gray_wool";
maxHeight = 100;
maxHeight = 120;
break;
default:
blockResourceLocation = "minecraft:black_wool";
maxHeight = 110;
maxHeight = 140;
break;
}
@@ -25,7 +25,7 @@ public class TestWorldGenBindingEvent extends DhApiLevelLoadEvent
ServerLevel level = (ServerLevel) event.value.levelWrapper.getWrappedMcObject();
// override the core DH world generator for this level
//IDhApiWorldGenerator exampleWorldGen = new TestChunkWorldGenerator(level); // TODO biomes are broken for some reason
//IDhApiWorldGenerator exampleWorldGen = new TestChunkWorldGenerator(level);
IDhApiWorldGenerator exampleWorldGen = new TestGenericWorldGenerator(event.value.levelWrapper);
DhApi.worldGenOverrides.registerWorldGeneratorOverride(event.value.levelWrapper, exampleWorldGen);
}
@@ -43,22 +43,11 @@ import net.minecraft.world.phys.AABB;
public class SodiumAccessor implements ISodiumAccessor
{
/**
* True if sodium 0.5 or less is present. <br>
* This field is public because it's also used to check if we need Indium to be present. <br>
* We need Indium if Sodium 0.5 or less is present.
*/
public static final boolean isSodiumV5OrLess;
#if MC_VER >= MC_1_20_1
private static MethodHandle setFogOcclusionMethod;
private static Object sodiumPerformanceOptions;
#endif
static {
isSodiumV5OrLess = !classPresent("net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer");
}
//======================//
@@ -83,7 +72,8 @@ public class SodiumAccessor implements ISodiumAccessor
{
if (sodiumPerformanceOptions == null)
{
if (isSodiumV5OrLess)
boolean sodiumV6 = classPresent("net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer");
if (!sodiumV6)
{
// sodium 0.5
@@ -8,7 +8,7 @@
"server.MixinEntity",
"server.MixinServerPlayer",
"server.MixinTracingExecutor",
"server.MixinUtilBackgroundThread",
"server.MixinUtilBackgroundThread",
"server.MixinLevelTicks"
],
"client": [
@@ -17,7 +17,6 @@
"client.MixinDebugScreenOverlay",
"client.MixinFogRenderer",
"client.MixinLevelRenderer",
"client.MixinChunkSectionsToRender",
"client.MixinLightTexture",
"client.MixinMinecraft",
"client.MixinOptionsScreen",
@@ -24,17 +24,13 @@ import com.seibel.distanthorizons.common.util.ProxyUtil;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.world.level.LevelAccessor;
@@ -56,7 +52,6 @@ import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraftforge.common.MinecraftForge;
import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
@@ -80,7 +75,6 @@ import java.util.concurrent.ThreadPoolExecutor;
public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
{
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final ForgePluginPacketSender PACKET_SENDER = (ForgePluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
@@ -96,13 +90,7 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
public void registerEvents()
{
MinecraftForge.EVENT_BUS.register(this);
// handles singleplayer, LAN, and connecting to a server
PACKET_SENDER.setPacketHandler((IServerPlayerWrapper player, @NotNull AbstractNetworkMessage message) ->
{
ClientApi.INSTANCE.pluginMessageReceived(message);
ServerApi.INSTANCE.pluginMessageReceived(player, message);
});
ForgePluginPacketSender.setPacketHandler(ClientApi.INSTANCE::pluginMessageReceived);
}
@@ -118,14 +118,14 @@ public class ForgeMain extends AbstractModInitializer
@Override
protected void subscribeRegisterCommandsEvent(Consumer<CommandDispatcher<CommandSourceStack>> eventHandler)
{ MinecraftForge.EVENT_BUS.addListener((RegisterCommandsEvent e) -> { eventHandler.accept(e.getDispatcher()); }); }
{
MinecraftForge.EVENT_BUS.addListener((RegisterCommandsEvent e) -> { eventHandler.accept(e.getDispatcher()); });
}
@Override
protected void subscribeClientStartedEvent(Runnable eventHandler)
{
// Just run the event handler, since there are no proper ClientLifecycleEvent for the client
// to signify readiness other than FmlClientSetupEvent
eventHandler.run();
// FIXME What event is this?
}
@Override
@@ -45,18 +45,16 @@ public class ForgePluginPacketSender extends AbstractPluginPacketSender
);
#endif
public ForgePluginPacketSender() { super(true); }
public void setPacketHandler(Consumer<AbstractNetworkMessage> consumer)
public static void setPacketHandler(Consumer<AbstractNetworkMessage> consumer)
{
this.setPacketHandler((player, message) -> consumer.accept(message));
setPacketHandler((player, message) -> consumer.accept(message));
}
public void setPacketHandler(BiConsumer<IServerPlayerWrapper, AbstractNetworkMessage> consumer)
public static void setPacketHandler(BiConsumer<IServerPlayerWrapper, AbstractNetworkMessage> consumer)
{
#if MC_VER >= MC_1_20_2
PLUGIN_CHANNEL.messageBuilder(MessageWrapper.class, 0)
.encoder((wrapper, out) -> this.encodeMessage(out, wrapper.message))
.decoder(in -> new MessageWrapper(this.decodeMessage(in)))
.encoder((wrapper, out) -> AbstractPluginPacketSender.encodeMessage(out, wrapper.message))
.decoder(in -> new MessageWrapper(AbstractPluginPacketSender.decodeMessage(in)))
.consumerNetworkThread((wrapper, context) ->
{
if (wrapper.message != null)
@@ -75,8 +73,8 @@ public class ForgePluginPacketSender extends AbstractPluginPacketSender
.add();
#else // < 1.20.2
PLUGIN_CHANNEL.registerMessage(0, MessageWrapper.class,
(wrapper, out) -> this.encodeMessage(out, wrapper.message),
in -> new MessageWrapper(this.decodeMessage(in)),
(wrapper, out) -> AbstractPluginPacketSender.encodeMessage(out, wrapper.message),
in -> new MessageWrapper(AbstractPluginPacketSender.decodeMessage(in)),
(wrapper, context) ->
{
if (wrapper.message != null)
@@ -7,9 +7,7 @@ import com.seibel.distanthorizons.common.wrappers.misc.ServerPlayerWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.BatchGenerationEnvironment;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.level.ServerLevel;
@@ -53,8 +51,6 @@ import java.util.function.Supplier;
public class ForgeServerProxy implements AbstractModInitializer.IEventProxy
{
private static final ForgePluginPacketSender PACKET_SENDER = (ForgePluginPacketSender) SingletonInjector.INSTANCE.get(IPluginPacketSender.class);
#if MC_VER < MC_1_19_2
private static LevelAccessor GetEventLevel(WorldEvent e) { return e.getWorld(); }
#else
@@ -72,7 +68,7 @@ public class ForgeServerProxy implements AbstractModInitializer.IEventProxy
MinecraftForge.EVENT_BUS.register(this);
if (this.isDedicated)
{
PACKET_SENDER.setPacketHandler(ServerApi.INSTANCE::pluginMessageReceived);
ForgePluginPacketSender.setPacketHandler(ServerApi.INSTANCE::pluginMessageReceived);
}
}
@@ -82,7 +78,10 @@ public class ForgeServerProxy implements AbstractModInitializer.IEventProxy
// constructor //
//=============//
public ForgeServerProxy(boolean isDedicated) { this.isDedicated = isDedicated; }
public ForgeServerProxy(boolean isDedicated)
{
this.isDedicated = isDedicated;
}
@@ -129,35 +129,34 @@ public class MixinLevelRenderer
// get the matrices from the OpenGL fixed pipeline
float[] mcProjMatrixRaw = new float[16];
GL15.glGetFloatv(GL15.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
ClientApi.RENDER_STATE.mcProjectionMatrix = new Mat4f(mcProjMatrixRaw);
ClientApi.RENDER_STATE.mcProjectionMatrix.transpose();
Mat4f mcProjectionMatrix = new Mat4f(mcProjMatrixRaw);
mcProjectionMatrix.transpose();
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(matrixStackIn.last().pose());
Mat4f mcModelViewMatrix = McObjectConverter.Convert(matrixStackIn.last().pose());
#else
// get the matrices directly from MC
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose());
ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
Mat4f mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose());
Mat4f mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
#endif
float frameTime;
#if MC_VER < MC_1_21_1
ClientApi.RENDER_STATE.frameTime = Minecraft.getInstance().getFrameTime();
frameTime = Minecraft.getInstance().getFrameTime();
#else
ClientApi.RENDER_STATE.frameTime = Minecraft.getInstance().getTimer().getRealtimeDeltaTicks();
frameTime = Minecraft.getInstance().getTimer().getRealtimeDeltaTicks();
#endif
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, this.level);
// only render before solid blocks
if (renderType.equals(RenderType.solid()))
{
ClientApi.INSTANCE.renderLods();
ClientApi.INSTANCE.renderLods(ClientLevelWrapper.getWrapper(this.level), mcModelViewMatrix, mcProjectionMatrix, frameTime);
}
else if (renderType.equals(RenderType.translucent()))
{
ClientApi.INSTANCE.renderDeferredLodsForShaders();
ClientApi.INSTANCE.renderDeferredLods(ClientLevelWrapper.getWrapper(this.level), mcModelViewMatrix, mcProjectionMatrix, frameTime);
}
// render fade
@@ -166,11 +165,27 @@ public class MixinLevelRenderer
// we need to trigger for the renderType after those passes are done
if (renderType.equals(RenderType.cutout()))
{
ClientApi.INSTANCE.renderFadeOpaque();
ClientApi.INSTANCE.renderFadeOpaque(
mcModelViewMatrix,
mcProjectionMatrix,
frameTime,
ClientLevelWrapper.getWrapper(this.level)
);
}
else if (renderType.equals(RenderType.tripwire()))
{
ClientApi.INSTANCE.renderFadeTransparent();
ClientApi.INSTANCE.renderFade(
mcModelViewMatrix,
mcProjectionMatrix,
frameTime,
ClientLevelWrapper.getWrapper(this.level)
);
}
if (Config.Client.Advanced.Debugging.lodOnlyMode.get())
{
callback.cancel();
}
}
@@ -57,25 +57,15 @@ public class MixinMinecraft
if (SelfUpdater.onStart())
{
try
{
instance.setScreen(new UpdateModScreen(
new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
(Config.Client.Advanced.AutoUpdater.updateBranch.get() == EDhApiUpdateBranch.STABLE ? ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()): GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha"))
));
return;
}
catch (Exception e)
{
// info instead of error since this can be ignored and probably just means
// there isn't a new DH version available
LOGGER.info("Unable to show DH update screen, reason: ["+e.getMessage()+"].");
}
instance.setScreen(new UpdateModScreen(
new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
(Config.Client.Advanced.AutoUpdater.updateBranch.get() == EDhApiUpdateBranch.STABLE ? ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()): GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha"))
));
}
else
{
instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened
}
// Sets the screen back to the vanilla screen as if nothing ever happened
// if not done the game will crash
instance.setScreen(guiScreen);
}
#endif
@@ -118,7 +108,7 @@ public class MixinMinecraft
versionId
));
}
catch (Exception e)
catch (IllegalArgumentException e)
{
// info instead of error since this can be ignored and probably just means
// there isn't a new DH version available
+6 -9
View File
@@ -5,8 +5,8 @@ org.gradle.caching=true
# Mod Info
mod_name=DistantHorizons
mod_version=2.3.5-b
api_version=4.1.0
mod_version=2.3.0-b-dev
api_version=4.0.0
maven_group=com.seibel.distanthorizons
mod_readable_name=Distant Horizons
mod_description=This mod generates and renders simplified terrain beyond the normal view distance at a low performance cost. Allowing you to see much farther without turning your game into a slideshow.
@@ -18,23 +18,20 @@ mod_issues=https://gitlab.com/jeseibel/distant-horizons/-/issues
mod_discord=https://discord.gg/xAB8G4cENx
# Global Plugin Versions
manifold_version=2025.1.27
manifold_version=2024.1.37
# 2023.1.17 can be used if there are mystery Java compiler issues
nightconfig_version=3.6.6
lz4_version=1.8.0
xz_version=1.9
# Before updating, read relocate_natives/README.md
sqlite_jdbc_version=3.47.2.0
# 8.2.1 is the newest version we can use since that's the version MC 1.16.5 uses
# (at least until we can fix the gradle script so core and main can use/shade different fastutil versions)
fastutil_version=8.2.1
#svgSalamander_version=1.1.3
# Minecraft related libraries (included in MC's jar)
log4j_version=2.23.1
# if we actually want to use LWJGL methods, this needs to be a MC version variable
# since different MC versions have different LWJGL versions that aren't compatible
# 3.3.3 is for MC 1.21.8
lwjgl_version=3.3.3
lwjgl_version=3.3.1
joml_version=1.10.2
# Architectury config
@@ -54,7 +51,7 @@ versionStr=
# This defines what MC version Intellij will use for the preprocessor
# and what version is used automatically by build and run commands
mcVer=1.21.10
mcVer=1.21.4
# Defines the maximum amount of memory Minecraft is allowed when run in a development environment
#minecraftMemoryJavaArg="-Xmx4G"
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
@@ -21,13 +21,16 @@ package com.seibel.distanthorizons.neoforge;
import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.util.ProxyUtil;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
@@ -37,6 +40,7 @@ import net.minecraft.world.level.LevelAccessor;
import net.minecraft.client.multiplayer.ClientLevel;
import net.neoforged.neoforge.client.event.RenderLevelStageEvent;
import net.neoforged.neoforge.common.NeoForge;
import net.neoforged.neoforge.event.level.ChunkEvent;
import net.neoforged.neoforge.event.level.LevelEvent;
import net.neoforged.neoforge.event.entity.player.PlayerInteractEvent;
@@ -58,14 +62,29 @@ import net.neoforged.neoforge.event.TickEvent;
import net.neoforged.neoforge.client.event.ClientTickEvent;
import java.util.concurrent.AbstractExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
#endif
/**
* This handles all events sent to the client,
* and is the starting point for most of the mod.
*
* @author James_Seibel
* @version 2023-7-27
*/
public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
{
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
// private static SimpleChannel multiversePluginChannel;
// Not the cleanest way of passing this to the LOD renderer, but it'll have to do for now
public static Mat4f currentModelViewMatrix = new Mat4f();
public static Mat4f currentProjectionMatrix = new Mat4f();
@Override
@@ -225,7 +244,16 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
// rendering //
//===========//
#if MC_VER < MC_1_21_6
@SubscribeEvent
public void beforeLevelRenderEvent(RenderLevelStageEvent event)
{
if (event.getStage() == RenderLevelStageEvent.Stage.AFTER_SKY)
{
currentModelViewMatrix = McObjectConverter.Convert(event.getModelViewMatrix());
currentProjectionMatrix = McObjectConverter.Convert(event.getProjectionMatrix());
}
}
@SubscribeEvent
public void afterLevelRenderEvent(RenderLevelStageEvent event)
{
@@ -244,62 +272,14 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
}
}
}
#else
@SubscribeEvent
public void afterLevelEntityRenderEvent(RenderLevelStageEvent.AfterEntities event)
{
#if MC_VER < MC_1_21_10
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, (ClientLevel)event.getLevel());
#else
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, event.getLevelRenderer().level);
#endif
ClientApi.INSTANCE.renderFadeTransparent();
}
//================//
// helper methods //
//================//
@SubscribeEvent
public void afterLevelTranslucentRenderEvent(RenderLevelStageEvent.AfterTranslucentBlocks event)
{
#if MC_VER < MC_1_21_10
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, (ClientLevel)event.getLevel());
#else
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, event.getLevelRenderer().level);
#endif
ClientApi.INSTANCE.renderDeferredLodsForShaders();
}
@SubscribeEvent
public void afterLevelRenderEvent(RenderLevelStageEvent.AfterLevel event)
{
#if MC_VER < MC_1_21_10
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, (ClientLevel)event.getLevel());
#else
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, event.getLevelRenderer().level);
#endif
try
{
// should generally only need to be set once per game session
// allows DH to render directly to Optifine's level frame buffer,
// allowing better shader support
MinecraftRenderWrapper.INSTANCE.finalLevelFrameBufferId = GL32.glGetInteger(GL32.GL_FRAMEBUFFER_BINDING);
}
catch (Exception | Error e)
{
LOGGER.error("Unexpected error in afterLevelRenderEvent: "+e.getMessage(), e);
}
ClientApi.INSTANCE.renderFadeOpaque();
}
#endif
private static LevelAccessor GetEventLevel(LevelEvent e) { return e.getLevel(); }
}
@@ -22,18 +22,14 @@ package com.seibel.distanthorizons.neoforge;
import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.neoforge.wrappers.NeoforgeMinecraftRenderWrapper;
import com.seibel.distanthorizons.neoforge.wrappers.modAccessor.ModChecker;
import com.seibel.distanthorizons.neoforge.wrappers.modAccessor.OptifineAccessor;
import net.minecraft.commands.CommandSourceStack;
@@ -53,13 +49,8 @@ import java.util.function.Consumer;
#if MC_VER < MC_1_20_6
import net.neoforged.neoforge.client.ConfigScreenHandler;
#elif MC_VER < MC_1_21_8
import net.neoforged.neoforge.client.gui.IConfigScreenFactory;
import org.jetbrains.annotations.NotNull;
#else
import net.neoforged.neoforge.client.gui.IConfigScreenFactory;
import org.jetbrains.annotations.NotNull;
import net.neoforged.neoforge.client.network.event.RegisterClientPayloadHandlersEvent;
#endif
/**
@@ -73,21 +64,11 @@ public class NeoforgeMain extends AbstractModInitializer
{
public NeoforgeMain(IEventBus eventBus)
{
// handles singleplayer, LAN, and connecting to a server
eventBus.addListener((FMLClientSetupEvent e) ->
{
eventBus.addListener((FMLClientSetupEvent e) -> {
this.onInitializeClient();
eventBus.addListener(this::registerNetworkingClientServer);
#if MC_VER < MC_1_21_8
#else
eventBus.addListener(this::registerClientPayloadEvent);
#endif
eventBus.addListener(this::registerNetworkingClient);
});
// handles dedicated servers
eventBus.addListener((FMLDedicatedServerSetupEvent e) ->
{
eventBus.addListener((FMLDedicatedServerSetupEvent e) -> {
this.onInitializeServer();
eventBus.addListener(this::registerNetworkingServer);
});
@@ -98,26 +79,11 @@ public class NeoforgeMain extends AbstractModInitializer
//============//
// networking //
//============//
public void registerNetworkingClientServer(RegisterPayloadHandlersEvent event)
{
NeoforgePluginPacketSender.setPacketHandler(event, (IServerPlayerWrapper player, @NotNull AbstractNetworkMessage message) ->
{
ClientApi.INSTANCE.pluginMessageReceived(message);
ServerApi.INSTANCE.pluginMessageReceived(player, message);
});
}
public void registerNetworkingClient(RegisterPayloadHandlersEvent event)
{ NeoforgePluginPacketSender.setPacketHandler(event, ClientApi.INSTANCE::pluginMessageReceived); }
public void registerNetworkingServer(RegisterPayloadHandlersEvent event)
{ NeoforgePluginPacketSender.setPacketHandler(event, ServerApi.INSTANCE::pluginMessageReceived); }
#if MC_VER < MC_1_21_8
#else
public void registerClientPayloadEvent(RegisterClientPayloadHandlersEvent event)
{ NeoforgePluginPacketSender.registerClientPacketHandler(event); }
#endif
@Override
@@ -128,10 +94,6 @@ public class NeoforgeMain extends AbstractModInitializer
{
SingletonInjector.INSTANCE.bind(IModChecker.class, ModChecker.INSTANCE);
SingletonInjector.INSTANCE.bind(IPluginPacketSender.class, new NeoforgePluginPacketSender());
// replace MC RenderWrapper with more specific neoforge version
SingletonInjector.INSTANCE.unbind(IMinecraftRenderWrapper.class, MinecraftRenderWrapper.INSTANCE); // TODO replace with a replaceOrBind for simplicity
SingletonInjector.INSTANCE.bind(IMinecraftRenderWrapper.class, NeoforgeMinecraftRenderWrapper.INSTANCE);
}
@Override
@@ -162,9 +124,7 @@ public class NeoforgeMain extends AbstractModInitializer
@Override
protected void subscribeClientStartedEvent(Runnable eventHandler)
{
// Just run the event handler, since there are no proper ClientLifecycleEvent for the client
// to signify readiness other than FmlClientSetupEvent
eventHandler.run();
// FIXME What event is this?
}
@Override
@@ -14,19 +14,10 @@ import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.Consumer;
#if MC_VER < MC_1_21_8
#else
import net.neoforged.neoforge.client.network.ClientPacketDistributor;
import net.neoforged.neoforge.client.network.event.RegisterClientPayloadHandlersEvent;
#endif
public class NeoforgePluginPacketSender extends AbstractPluginPacketSender
{
private static BiConsumer<IServerPlayerWrapper, AbstractNetworkMessage> packetConsumer;
public static void setPacketHandler(RegisterPayloadHandlersEvent event, Consumer<AbstractNetworkMessage> consumer)
{ setPacketHandler(event, (player, buffer) -> consumer.accept(buffer)); }
public static void setPacketHandler(RegisterPayloadHandlersEvent event, BiConsumer<IServerPlayerWrapper, AbstractNetworkMessage> consumer)
@@ -48,31 +39,9 @@ public class NeoforgePluginPacketSender extends AbstractPluginPacketSender
});
}
#if MC_VER < MC_1_21_8
#else
public static void registerClientPacketHandler(RegisterClientPayloadHandlersEvent event)
{
// as of MC 1.21.7 Neo added a separate client network register
// https://github.com/neoforged/NeoForge/pull/2272
event.register(CommonPacketPayload.TYPE, (payload, context) ->
{
if (payload.message() != null)
{
packetConsumer.accept(null, payload.message());
}
});
}
#endif
@Override
public void sendToServer(AbstractNetworkMessage message)
{
#if MC_VER < MC_1_21_8
PacketDistributor.sendToServer(new CommonPacketPayload(message));
#else
ClientPacketDistributor.sendToServer(new CommonPacketPayload(message));
#endif
}
{ PacketDistributor.sendToServer(new CommonPacketPayload(message)); }
@Override
public void sendToClient(ServerPlayer serverPlayer, AbstractNetworkMessage message)
@@ -152,12 +152,8 @@ public class NeoforgeServerProxy implements AbstractModInitializer.IEventProxy
private static ServerLevelWrapper getServerLevelWrapper(ServerLevel level) { return ServerLevelWrapper.getWrapper(level); }
private static ServerLevelWrapper getServerLevelWrapper(ResourceKey<Level> resourceKey, PlayerEvent event)
{
#if MC_VER < MC_1_21_10
//noinspection DataFlowIssue (possible NPE after getServer())
return getServerLevelWrapper(event.getEntity().getServer().getLevel(resourceKey));
#else
return getServerLevelWrapper(event.getEntity().level().getServer().getLevel(resourceKey));
#endif
}
private static ServerPlayerWrapper getServerPlayerWrapper(PlayerEvent event) { return ServerPlayerWrapper.getWrapper((ServerPlayer) event.getEntity()); }
@@ -1,6 +1,5 @@
package com.seibel.distanthorizons.neoforge.mixins.client;
#if MC_VER < MC_1_21_10
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import net.minecraft.client.gui.components.DebugScreenOverlay;
import org.spongepowered.asm.mixin.Mixin;
@@ -9,24 +8,16 @@ import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import java.util.List;
#else
import org.spongepowered.asm.mixin.Mixin;
import net.minecraft.client.gui.components.DebugScreenOverlay;
#endif
@Mixin(DebugScreenOverlay.class)
public class MixinDebugScreenOverlay
{
#if MC_VER < MC_1_21_10
@Inject(method = "getSystemInformation", at = @At("RETURN"))
private void addCustomF3(CallbackInfoReturnable<List<String>> cir)
{
List<String> messages = cir.getReturnValue();
F3Screen.addStringToDisplay(messages);
}
#else
// handled by DhDebugScreenEntry for MC versions after 1.21.10
#endif
}
@@ -22,87 +22,67 @@ package com.seibel.distanthorizons.neoforge.mixins.client;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import net.minecraft.client.Minecraft;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import com.mojang.blaze3d.systems.RenderSystem;
import net.minecraft.client.Camera;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
#if MC_VER < MC_1_17_1
import net.minecraft.world.level.material.FluidState;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode;
import com.mojang.blaze3d.systems.RenderSystem;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#elif MC_VER < MC_1_21_3
import net.minecraft.world.level.material.FogType;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode;
import com.mojang.blaze3d.systems.RenderSystem;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#elif MC_VER < MC_1_21_6
import net.minecraft.world.level.material.FogType;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.mojang.blaze3d.shaders.FogShape;
import net.minecraft.client.renderer.FogRenderer;
import net.minecraft.client.renderer.FogRenderer.FogMode;
import net.minecraft.client.renderer.FogParameters;
import org.joml.Vector4f;
import com.mojang.blaze3d.systems.RenderSystem;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#else
import net.minecraft.client.renderer.FogParameters;
import net.minecraft.world.level.material.FogType;
import net.minecraft.client.renderer.fog.FogRenderer;
import net.minecraft.client.renderer.fog.FogData;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import org.joml.Vector4f;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
#endif
@Mixin(FogRenderer.class)
public class MixinFogRenderer
{
// Using this instead of Float.MAX_VALUE because Sodium don't like it.
@Unique
private static final float A_REALLY_REALLY_BIG_VALUE = 420694206942069.F;
@Unique
private static final float A_EVEN_LARGER_VALUE = 42069420694206942069.F;
#if MC_VER < MC_1_19_2
@Inject(at = @At("RETURN"), method = "setupFog")
private static void disableSetupFog(Camera camera, FogMode fogMode, float f, boolean bl, CallbackInfo callback)
#if MC_VER == MC_1_17_1 || MC_VER == MC_1_18_2
@Inject(at = @At("RETURN"),
method = "setupFog(Lnet/minecraft/client/Camera;Lnet/minecraft/client/renderer/FogRenderer$FogMode;FZF)V",
remap = false)
private static void disableSetupFog(Camera camera, FogMode fogMode, float f, boolean bl, float partTick, CallbackInfo callback)
#elif MC_VER < MC_1_21_3
@Inject(at = @At("RETURN"), method = "setupFog")
private static void disableSetupFog(Camera camera, FogMode fogMode, float f, boolean bl, float g, CallbackInfo callback)
#elif MC_VER < MC_1_21_6
@Inject(at = @At("RETURN"), method = "setupFog", cancellable = true)
private static void disableSetupFog(Camera camera, FogMode fogMode, Vector4f vector4f, float f, boolean bl, float g, CallbackInfoReturnable<FogParameters> callback)
@Inject(at = @At("RETURN"),
method = "setupFog(Lnet/minecraft/client/Camera;Lnet/minecraft/client/renderer/FogRenderer$FogMode;FZF)V",
remap = true)
private static void disableSetupFog(Camera camera, FogMode fogMode, float f, boolean bl, float partTick, CallbackInfo callback)
#else
@Unique
private static void unused()
@Inject(at = @At("RETURN"),
method = "setupFog(Lnet/minecraft/client/Camera;Lnet/minecraft/client/renderer/FogRenderer$FogMode;Lorg/joml/Vector4f;FZF)Lnet/minecraft/client/renderer/FogParameters;",
remap = true, cancellable = true)
private static void disableSetupFog(Camera camera, FogMode fogMode, Vector4f vector4f, float f, boolean bl, float partTick, CallbackInfoReturnable<FogParameters> callback)
#endif
{
#if MC_VER < MC_1_21_6
boolean cancelFog = cancelFog(camera, fogMode);
#elif MC_VER < MC_1_21_6
boolean cancelFog = cancelFog(camera);
#else
boolean cancelFog = cancelFog();
#endif
boolean cameraNotInFluid = cameraNotInFluid(camera);
if (cancelFog)
Entity entity = camera.getEntity();
boolean isSpecialFog = (entity instanceof LivingEntity) && ((LivingEntity) entity).hasEffect(MobEffects.BLINDNESS);
if (!isSpecialFog && cameraNotInFluid && fogMode == FogMode.FOG_TERRAIN
&& !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial()
&& !Config.Client.Advanced.Graphics.Fog.enableVanillaFog.get())
{
#if MC_VER < MC_1_17_1
RenderSystem.fogStart(A_REALLY_REALLY_BIG_VALUE);
@@ -110,77 +90,12 @@ public class MixinFogRenderer
#elif MC_VER < MC_1_21_3
RenderSystem.setShaderFogStart(A_REALLY_REALLY_BIG_VALUE);
RenderSystem.setShaderFogEnd(A_EVEN_LARGER_VALUE);
#elif MC_VER < MC_1_21_6
callback.setReturnValue(FogParameters.NO_FOG);
#else
callback.setReturnValue(FogParameters.NO_FOG);
#endif
}
}
#if MC_VER < MC_1_21_6
#else
// In MC's FogRenderer they clamp the "renderDistanceEnd" fog field to the render distance,
// which prevents us from disabling the vanilla fog.
// This mixin fires after they set the "renderDistanceEnd" so we can change it.
@WrapOperation(
method = "setupFog",
at = @At(
value = "FIELD",
target = "Lnet/minecraft/client/renderer/fog/FogData;renderDistanceEnd:F",
opcode = org.objectweb.asm.Opcodes.PUTFIELD
)
)
private void onSetRenderDistanceEnd(FogData instance, float value, Operation<Void> original)
{
if (cancelFog())
{
instance.environmentalStart = A_REALLY_REALLY_BIG_VALUE;
instance.environmentalEnd = A_EVEN_LARGER_VALUE;
instance.renderDistanceStart = A_REALLY_REALLY_BIG_VALUE;
instance.renderDistanceEnd = A_EVEN_LARGER_VALUE;
}
// Always call the original with the modified or original value
original.call(instance, value);
}
#endif
@Unique
#if MC_VER < MC_1_21_6
private static boolean cancelFog(Camera camera, FogMode fogMode)
#else
private static boolean cancelFog()
#endif
{
#if MC_VER < MC_1_21_6
Entity entity = camera.getEntity();
#else
Camera camera = Minecraft.getInstance().gameRenderer.getMainCamera();
Entity entity = camera.getEntity();
#endif
boolean cameraNotInFluid = cameraNotInFluid(camera);
boolean isSpecialFog = (entity instanceof LivingEntity) && ((LivingEntity) entity).hasEffect(MobEffects.BLINDNESS);
boolean cancelFog = !isSpecialFog;
cancelFog = cancelFog && cameraNotInFluid;
#if MC_VER < MC_1_21_6
cancelFog = cancelFog && (fogMode == FogMode.FOG_TERRAIN);
#endif
cancelFog = cancelFog && !SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class).isFogStateSpecial();
cancelFog = cancelFog && !Config.Client.Advanced.Graphics.Fog.enableVanillaFog.get();
return cancelFog;
}
@Unique
private static boolean cameraNotInFluid(Camera camera)
{
#if MC_VER < MC_1_17_1
@@ -195,5 +110,4 @@ public class MixinFogRenderer
}
}
@@ -19,53 +19,48 @@
package com.seibel.distanthorizons.neoforge.mixins.client;
#if MC_VER < MC_1_21_6
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.vertex.PoseStack;
#if MC_VER < MC_1_19_4
import com.mojang.math.Matrix4f;
#else
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.neoforge.NeoforgeClientProxy;
import net.minecraft.client.Camera;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.GameRenderer;
import net.minecraft.client.renderer.LightTexture;
import org.joml.Matrix4f;
#endif
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.RenderType;
import net.neoforged.neoforge.client.event.RenderLevelStageEvent;
import org.joml.Matrix4f;
#else
import com.mojang.blaze3d.vertex.PoseStack;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.chunk.ChunkSectionsToRender;
import net.minecraft.client.Camera;
import net.minecraft.client.DeltaTracker;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.renderer.LevelRenderer;
import org.joml.Matrix4f;
import org.joml.Matrix4fc;
import org.joml.Vector4f;
import com.mojang.blaze3d.buffers.GpuBufferSlice;
import com.mojang.blaze3d.resource.GraphicsResourceAllocator;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
#endif
import org.apache.logging.log4j.Logger;
import com.seibel.distanthorizons.neoforge.NeoforgeClientProxy;
import com.seibel.distanthorizons.common.wrappers.McObjectConverter;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.coreapi.ModInfo;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.Unique;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#if MC_VER < MC_1_17_1
import org.lwjgl.opengl.GL15;
#endif
/**
* This class is used to mix in DH's rendering code
* before Minecraft starts rendering blocks.
* If this wasn't done, and we used Forge's
* render last event, the LODs would render on top
* of the normal terrain. <br><br>
*
* This is also the mixin for rendering the clouds
*/
@Mixin(LevelRenderer.class)
public class MixinLevelRenderer
{
@@ -75,67 +70,73 @@ public class MixinLevelRenderer
#endif
private ClientLevel level;
@Unique
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
#if MC_VER < MC_1_21_6
@Inject(at = @At("HEAD"), method = "renderSectionLayer")
private void renderChunkLayer(RenderType renderType, double x, double y, double z, Matrix4f modelViewMatrix, Matrix4f projectionMatrix, CallbackInfo callback)
#elif MC_VER < MC_1_21_10
@Inject(at = @At("HEAD"), method = "renderLevel")
private void onRenderLevel(
GraphicsResourceAllocator resourceAllocator, DeltaTracker deltaTracker,
boolean renderBlockOutline, Camera camera,
Matrix4f positionMatrix, Matrix4f projectionMatrix, GpuBufferSlice gpuBufferSlice,
Vector4f skyColor, boolean thinFog, CallbackInfo callback)
#if MC_VER < MC_1_17_1
@Inject(at = @At("HEAD"),
method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDD)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack matrixStackIn, double xIn, double yIn, double zIn, CallbackInfo callback)
#elif MC_VER < MC_1_19_4
@Inject(at = @At("HEAD"),
method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLcom/mojang/math/Matrix4f;)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, Matrix4f projectionMatrix, CallbackInfo callback)
#elif MC_VER < MC_1_20_2
@Inject(at = @At("HEAD"),
method = "renderChunkLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double cameraXBlockPos, double cameraYBlockPos, double cameraZBlockPos, Matrix4f projectionMatrix, CallbackInfo callback)
#elif MC_VER < MC_1_20_6
@Inject(at = @At("HEAD"),
method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;Lcom/mojang/blaze3d/vertex/PoseStack;DDDLorg/joml/Matrix4f;)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, PoseStack modelViewMatrixStack, double camX, double camY, double camZ, Matrix4f projectionMatrix, CallbackInfo callback)
#else
@Inject(at = @At("HEAD"), method = "renderLevel")
private void renderLevel(
GraphicsResourceAllocator resourceAllocator, DeltaTracker deltaTracker,
boolean renderBlockOutline, Camera camera,
Matrix4f positionMatrix, Matrix4f projectionMatrix, Matrix4f idkMatrix, GpuBufferSlice gpuBufferSlice,
Vector4f skyColor, boolean thinFog, CallbackInfo callback)
#endif
@Inject(at = @At("HEAD"),
method = "Lnet/minecraft/client/renderer/LevelRenderer;renderSectionLayer(Lnet/minecraft/client/renderer/RenderType;DDDLorg/joml/Matrix4f;Lorg/joml/Matrix4f;)V",
cancellable = true)
private void renderChunkLayer(RenderType renderType, double x, double y, double z, Matrix4f projectionMatrix, Matrix4f frustumMatrix, CallbackInfo callback)
#endif
{
#if MC_VER < MC_1_21_6
// MC combined the model view and projection matricies
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrix);
ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
#if MC_VER == MC_1_16_5
// get the matrices from the OpenGL fixed pipeline
float[] mcProjMatrixRaw = new float[16];
GL15.glGetFloatv(GL15.GL_PROJECTION_MATRIX, mcProjMatrixRaw);
Mat4f mcProjectionMatrix = new Mat4f(mcProjMatrixRaw);
mcProjectionMatrix.transpose();
Mat4f mcModelViewMatrix = McObjectConverter.Convert(matrixStackIn.last().pose());
#elif MC_VER <= MC_1_20_4
// get the matrices directly from MC
Mat4f mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrixStack.last().pose());
Mat4f mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
#else
ClientApi.RENDER_STATE.mcProjectionMatrix = McObjectConverter.Convert(projectionMatrix);
// get the matrices from neoForge's render event.
// We can't call the renderer there because we don't have access to the level that's being rendered
Mat4f mcModelViewMatrix = NeoforgeClientProxy.currentModelViewMatrix;
Mat4f mcProjectionMatrix = NeoforgeClientProxy.currentProjectionMatrix;
#endif
float frameTime;
#if MC_VER < MC_1_21_1
ClientApi.RENDER_STATE.frameTime = Minecraft.getInstance().getFrameTime();
frameTime = Minecraft.getInstance().getFrameTime();
#elif MC_VER < MC_1_21_3
ClientApi.RENDER_STATE.frameTime = Minecraft.getInstance().getTimer().getRealtimeDeltaTicks();
frameTime = Minecraft.getInstance().getTimer().getRealtimeDeltaTicks();
#else
ClientApi.RENDER_STATE.frameTime = Minecraft.getInstance().deltaTracker.getRealtimeDeltaTicks();
frameTime = Minecraft.getInstance().deltaTracker.getRealtimeDeltaTicks();
#endif
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, this.level);
#if MC_VER < MC_1_21_6
// only crash during development
if (ModInfo.IS_DEV_BUILD)
{
ClientApi.RENDER_STATE.canRenderOrThrow();
}
// render LODs
if (renderType.equals(RenderType.solid()))
{
ClientApi.INSTANCE.renderLods();
ClientApi.INSTANCE.renderLods(ClientLevelWrapper.getWrapper(this.level), mcModelViewMatrix, mcProjectionMatrix, frameTime);
}
else if (renderType.equals(RenderType.translucent()))
{
ClientApi.INSTANCE.renderDeferredLodsForShaders();
ClientApi.INSTANCE.renderDeferredLods(ClientLevelWrapper.getWrapper(this.level), mcModelViewMatrix, mcProjectionMatrix, frameTime);
}
// render fade
@@ -144,38 +145,28 @@ public class MixinLevelRenderer
// we need to trigger for the renderType after those passes are done
if (renderType.equals(RenderType.cutout()))
{
ClientApi.INSTANCE.renderFadeOpaque();
ClientApi.INSTANCE.renderFadeOpaque(
mcModelViewMatrix,
mcProjectionMatrix,
frameTime,
ClientLevelWrapper.getWrapper(this.level)
);
}
else if (renderType.equals(RenderType.tripwire()))
{
ClientApi.INSTANCE.renderFadeTransparent();
ClientApi.INSTANCE.renderFade(
mcModelViewMatrix,
mcProjectionMatrix,
frameTime,
ClientLevelWrapper.getWrapper(this.level)
);
}
#endif
}
#if MC_VER < MC_1_21_6
// formerly handled in renderChunkLayer()
#else
@Inject(at = @At("HEAD"), method = "prepareChunkRenders")
private void renderChunkLayer(Matrix4fc modelViewMatrix, double d, double e, double f, CallbackInfoReturnable<ChunkSectionsToRender> callback)
{
ClientApi.RENDER_STATE.mcModelViewMatrix = McObjectConverter.Convert(modelViewMatrix);
ClientApi.RENDER_STATE.clientLevelWrapper = ClientLevelWrapper.getWrapperIfDifferent(ClientApi.RENDER_STATE.clientLevelWrapper, this.level);
// only crash during development
if (ModInfo.IS_DEV_BUILD)
if (Config.Client.Advanced.Debugging.lodOnlyMode.get())
{
ClientApi.RENDER_STATE.canRenderOrThrow();
callback.cancel();
}
ClientApi.INSTANCE.renderLods();
}
#endif
}
@@ -19,14 +19,17 @@
package com.seibel.distanthorizons.neoforge.mixins.client;
import com.mojang.blaze3d.pipeline.TextureTarget;
import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.neoforge.wrappers.NeoforgeTextureUnwrapper;
import net.minecraft.client.renderer.LightTexture;
import org.lwjgl.opengl.GL32;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
@@ -34,36 +37,14 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#if MC_VER < MC_1_21_3
import com.mojang.blaze3d.platform.NativeImage;
#elif MC_VER < MC_1_21_5
import com.mojang.blaze3d.pipeline.TextureTarget;
#elif MC_VER < MC_1_21_10
import com.mojang.blaze3d.opengl.GlTexture;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.GpuTexture;
#else
import net.neoforged.neoforge.client.blaze3d.validation.ValidationGpuTexture;
import com.mojang.blaze3d.opengl.GlTexture;
import com.mojang.blaze3d.textures.GpuTexture;
#endif
@Mixin(LightTexture.class)
public class MixinLightTexture
{
#if MC_VER < MC_1_21_3
@Shadow
@Shadow
@Final
private NativeImage lightPixels;
#elif MC_VER < MC_1_21_5
@Shadow
@Final
private TextureTarget target;
#else
@Shadow
@Final
private GpuTexture texture;
#endif
@Inject(method = "updateLightTexture(F)V", at = @At("RETURN"))
public void updateLightTexture(float partialTicks, CallbackInfo ci)
@@ -74,21 +55,24 @@ public class MixinLightTexture
return;
}
IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
MinecraftRenderWrapper.INSTANCE.updateLightmap(this.lightPixels, clientLevel);
}
#else
@Shadow @Final private TextureTarget target;
@Inject(method = "updateLightTexture(F)V", at = @At("RETURN"))
public void updateLightTexture(float partialTicks, CallbackInfo ci)
{
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
if (mc == null || mc.getWrappedClientLevel() == null)
{
return;
}
IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
MinecraftRenderWrapper renderWrapper = (MinecraftRenderWrapper)SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
#if MC_VER < MC_1_21_3
renderWrapper.updateLightmap(this.lightPixels, clientLevel);
#elif MC_VER < MC_1_21_5
renderWrapper.setLightmapId(this.target.getColorTextureId(), clientLevel);
#elif MC_VER < MC_1_21_10
GlTexture glTexture = (GlTexture) this.texture;
renderWrapper.setLightmapId(glTexture.glId(), clientLevel);
#else
int id = NeoforgeTextureUnwrapper.getGlTextureIdFromGpuTexture(this.texture);
renderWrapper.setLightmapId(id, clientLevel);
#endif
MinecraftRenderWrapper.INSTANCE.setLightmapId(this.target.getColorTextureId(), clientLevel);
}
#endif
}
@@ -76,13 +76,14 @@ public class MixinMinecraft
{
try
{
Minecraft.getInstance().setScreen(new UpdateModScreen(
// TODO: Change to runnable, instead of tittle screen
new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
versionId
));
}
catch (Exception e)
catch (IllegalArgumentException e)
{
// info instead of error since this can be ignored and probably just means
// there isn't a new DH version available
@@ -1,8 +1,6 @@
package com.seibel.distanthorizons.neoforge.mixins.client;
#if MC_VER < MC_1_21_5
import com.mojang.blaze3d.platform.GlStateManager;
#endif
import com.mojang.blaze3d.platform.TextureUtil;
import com.seibel.distanthorizons.core.config.Config;
import org.spongepowered.asm.mixin.Mixin;
@@ -17,9 +15,6 @@ import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(TextureUtil.class)
public class MixinTextureUtil
{
// TODO fix for MC 1.21.5+
#if MC_VER < MC_1_21_5
@Redirect(method = "prepareImage(Lcom/mojang/blaze3d/platform/NativeImage$InternalGlFormat;IIII)V",
at = @At(value = "INVOKE", target = "Lcom/mojang/blaze3d/platform/GlStateManager;_texParameter(IIF)V"), remap = false)
private static void setLodBias(int target, int pname, float param)
@@ -32,6 +27,5 @@ public class MixinTextureUtil
GlStateManager._texParameter(target, pname, biasValue);
}
}
#endif
}
@@ -1,27 +1,35 @@
package com.seibel.distanthorizons.neoforge.mixins.server;
#if MC_VER < MC_1_21_4
import net.minecraft.world.entity.Entity;
#if MC_VER < MC_1_21_3
import net.minecraft.Util;
import org.spongepowered.asm.mixin.Mixin;
@Mixin(Entity.class)
/**
* {@link MixinUtilBackgroundThread} was used for versions before 1.21.3
* This is just a dummy class/mixin to make the compiler happy.
*
* @see MixinUtilBackgroundThread
*/
@Mixin(Util.class) // TODO we should allow version specific mixins so we don't have to create dummy mixins that exist for all MC versions
public class MixinLevelTicks<T>
{
// dummy mixin to make the loader happy
}
#else
import com.seibel.distanthorizons.common.wrappers.DependencySetupDoneCheck;
import net.minecraft.world.ticks.LevelTicks;
import net.minecraft.world.ticks.ScheduledTick;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(LevelTicks.class) // available in 1.18.2+, but only needed in 1.21.4+
@Mixin(LevelTicks.class)
public class MixinLevelTicks<T>
{
// TODO put in a common location
@@ -31,7 +39,7 @@ public class MixinLevelTicks<T>
@Inject(method = "schedule", at = @At(value = "HEAD"), cancellable = true)
private void onChunkSave(ScheduledTick<T> tick, CallbackInfo ci)
private void schedule(ScheduledTick<T> tick, CallbackInfo ci)
{
// In MC 1.21.4 an error check was added to log attempting to schedule ticks for unloaded chunks
// this caused a lot of unnecessary errors when generating sand (FallingBlock.class).
@@ -42,5 +50,4 @@ public class MixinLevelTicks<T>
}
}
#endif
@@ -1,82 +0,0 @@
package com.seibel.distanthorizons.neoforge.wrappers;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import net.minecraft.client.Minecraft;
import org.apache.logging.log4j.Logger;
#if MC_VER < MC_1_21_10
#else
import com.mojang.blaze3d.opengl.GlTexture;
import com.mojang.blaze3d.textures.GpuTexture;
import net.neoforged.neoforge.client.blaze3d.validation.ValidationGpuTexture;
#endif
import java.lang.invoke.MethodHandles;
public class NeoforgeMinecraftRenderWrapper extends MinecraftRenderWrapper
{
public static final NeoforgeMinecraftRenderWrapper INSTANCE = new NeoforgeMinecraftRenderWrapper();
private static final Logger LOGGER = DhLoggerBuilder.getLogger(MethodHandles.lookup().lookupClass().getSimpleName());
private static final Minecraft MC = Minecraft.getInstance();
@Override
public int getDepthTextureId()
{
#if MC_VER < MC_1_21_10
// no special handling required,
// both neo/fabric uses the same back end objects
return super.getDepthTextureId();
#else
try
{
GpuTexture gpuTexture = this.getRenderTarget().getDepthTexture();
int id = NeoforgeTextureUnwrapper.getGlTextureIdFromGpuTexture(gpuTexture);
return id;
}
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
}
@Override
public int getColorTextureId()
{
#if MC_VER < MC_1_21_10
// no special handling required,
// both neo/fabric uses the same back end objects
return super.getColorTextureId();
#else
try
{
GpuTexture gpuTexture = this.getRenderTarget().getColorTexture();
int id = NeoforgeTextureUnwrapper.getGlTextureIdFromGpuTexture(gpuTexture);
return id;
}
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 ValidationGpuTexture or GlTexture. MC, Neoforge, or a rendering mod may have changed the object type.", e);
}
return 0;
}
#endif
}
}
@@ -1,39 +0,0 @@
package com.seibel.distanthorizons.neoforge.wrappers;
#if MC_VER < MC_1_21_10
public class NeoforgeTextureUnwrapper
{ /* not needed for older MC versions */ }
#else
import com.mojang.blaze3d.opengl.GlTexture;
import com.mojang.blaze3d.textures.GpuTexture;
import net.neoforged.neoforge.client.blaze3d.validation.ValidationGpuTexture;
public class NeoforgeTextureUnwrapper
{
/**
* if Neoforge texture validation is enabled the GlTexture object will be wrapped with a
* Neoforge specific ValidationGpuTexture object.
* This helper allows us to get the underlying OpenGL texture ID
* regardless of what Neoforge returns.
*/
public static int getGlTextureIdFromGpuTexture(GpuTexture gpuTexture) throws ClassCastException
{
GlTexture glTexture;
if (gpuTexture instanceof ValidationGpuTexture)
{
ValidationGpuTexture validationTexture = (ValidationGpuTexture) gpuTexture;
glTexture = (GlTexture)validationTexture.getRealTexture();
}
else
{
glTexture = (GlTexture) gpuTexture;
}
int id = glTexture.glId();
return id;
}
}
#endif
@@ -3,8 +3,6 @@ loaderVersion = "*" # // mandatory. Allow all forge versions as we are definding
license = "LGPL"
issueTrackerURL = "${issues}"
# https://docs.neoforged.net/docs/gettingstarted/modfiles/#neoforgemodstoml
[[mods]] #//mandatory
modId = "distanthorizons" #//mandatory
@@ -34,11 +32,4 @@ issueTrackerURL = "${issues}"
type = "required"
versionRange = "${compatible_forgemc_versions}" # Where we set what version of mc it is avalible for
ordering = "NONE"
side = "BOTH"
[[dependencies.distanthorizons]]
modId = "neoforge"
type = "required"
versionRange = "${neoforge_version_range}"
ordering = "NONE"
side = "BOTH"
side = "BOTH"
-46
View File
@@ -1,46 +0,0 @@
This directory contains the native files of libraries that were modified for relocation. They will be copied from here during the normal build steps.
Before adding/updating a library, make sure you have Python 3.8+ installed and check the instructions below.
How to add a library's natives:
1. In `build.gradle`:
- Make sure the target package is the same length or shorter (untested) than the source package. Underscores in native methods will be mapped to `_1` so account for that as well.
- Exclude the native files and add them as `relocateNative` (see example).
Example:
```groovy
relocate "org.sqlite", "dh_sqlite", {
exclude "org/sqlite/native/**"
}
transform(NativeTransformer) {
// NativeTransformer configuration
rootDir = project.rootDir
// Replace native strings, e.g. used in calls back to Java
relocateNative "org/sqlite", "dh_sqlite"
// Rename native methods used when calling from Java
relocateNative "org_sqlite", "dh_1sqlite"
}
```
How to update a library's natives:
1. Delete the library's folder in cache/.
2. It will repopulate during the next build.
Why does this step exist?
- Native files are not handled by the shadow plugin correctly.
- I preferred it as a more streamlined approach, although a bit hacky.
- Possible alternatives:
- Use edited libraries' source code: although more straightforward, it requires maintaining and updating the repositories for the libraries being added
- Interfacing with the necessary libraries directly: an absolute mess for technical reasons
What are libraries used?
- LIEF: for fixing binaries after replacing strings
- apple-codesign: for re-signing Mac binaries, since their signatures get invalidated after previous steps

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