Compare commits
85 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fce2868c62 | |||
| 0fba015f54 | |||
| f251c90472 | |||
| 492a051a3b | |||
| 0aa4743c1b | |||
| 85f16944b2 | |||
| dddb0be2ac | |||
| 4a3effa2f5 | |||
| a0a9151bfd | |||
| aa3d15f38f | |||
| adcb2a3a05 | |||
| 78f2cb24cc | |||
| 67945509ed | |||
| c653e526a5 | |||
| 49b50c4c88 | |||
| 7449f46c5e | |||
| 069fc39aad | |||
| 4979ccf3e2 | |||
| dd7f9c20b6 | |||
| e96f9de1f0 | |||
| c902e1957f | |||
| d40afb7a2a | |||
| 01474d72e3 | |||
| 7c5af1836b | |||
| a9bf6ae7e4 | |||
| aef3162246 | |||
| 97ce869076 | |||
| 91b3c83ffd | |||
| 1522df19cb | |||
| 3845564128 | |||
| 23ef7cf27a | |||
| bec28a5694 | |||
| 8f20f103ad | |||
| 246e77cc56 | |||
| 87fa29c77a | |||
| 5010256ce6 | |||
| a66ad19343 | |||
| 913a458a1a | |||
| 093d3a801e | |||
| 61ccf7bf60 | |||
| f948072253 | |||
| b748f27a1c | |||
| 4dd4bb9ef0 | |||
| 5051bde3b0 | |||
| 42cf639acc | |||
| 9e6953a596 | |||
| 7f4f8a40eb | |||
| 89ca535a6f | |||
| 145182502e | |||
| d61dfc9e03 | |||
| 611d7d87ae | |||
| 2f6a2d99ab | |||
| d88ca0c98d | |||
| 0f64df7be0 | |||
| 23a1f0b025 | |||
| 4a72e02550 | |||
| 521bcdcc0f | |||
| 4eb20d5ce8 | |||
| 3ad68aaf42 | |||
| 2a9a03771e | |||
| 8f7823a4d2 | |||
| cc4b965966 | |||
| a6418de927 | |||
| 5303415d05 | |||
| 836515934f | |||
| 228dc46d6b | |||
| a91f9670dc | |||
| 81313252f2 | |||
| f65d411978 | |||
| 8c8a5ffeaf | |||
| 68793fbe8d | |||
| d8401a8f49 | |||
| 07ff00f7c9 | |||
| fadaff1113 | |||
| ff6bf7b4c9 | |||
| 082b1224a8 | |||
| bc475373fc | |||
| 498e958eca | |||
| 82e0cfe0b4 | |||
| 31d89e3349 | |||
| a3775c1f88 | |||
| 834269da67 | |||
| a9bebf03d5 | |||
| 939f6304bf | |||
| a0b5cc7a5c |
@@ -26,6 +26,11 @@ Merged/
|
|||||||
# Folder created by the buildAll scripts
|
# Folder created by the buildAll scripts
|
||||||
buildAllJars/
|
buildAllJars/
|
||||||
|
|
||||||
|
relocate_natives/.venv/
|
||||||
|
relocate_natives/__pycache__/
|
||||||
|
relocate_natives/apple-codesign/
|
||||||
|
relocate_natives/cache/
|
||||||
|
|
||||||
# file from notepad++
|
# file from notepad++
|
||||||
*.bak
|
*.bak
|
||||||
|
|
||||||
|
|||||||
@@ -17,6 +17,9 @@ variables:
|
|||||||
# These can be extended so code is a bit less duplicated
|
# These can be extended so code is a bit less duplicated
|
||||||
.build_java:
|
.build_java:
|
||||||
#image: eclipse-temurin:17
|
#image: eclipse-temurin:17
|
||||||
|
before_script:
|
||||||
|
- apt-get update
|
||||||
|
- apt-get install python3 python3-pip python-is-python3 python3-venv -y --no-install-recommends
|
||||||
cache:
|
cache:
|
||||||
key: "gradleCache_$CI_JOB_NAME_SLUG"
|
key: "gradleCache_$CI_JOB_NAME_SLUG"
|
||||||
policy: pull-push
|
policy: pull-push
|
||||||
|
|||||||
@@ -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:
|
1. [ ] Check the FAQ to see if your issue has already been reported and has a solution:
|
||||||
[Problems-and-solutions](https://gitlab.com/jeseibel/distant-horizons/-/wikis/2-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions)
|
[Problems-and-solutions](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/2-problems-and-solutions/Problems-and-Solutions)
|
||||||
|
|
||||||
2. [ ] Make sure you are not using any mods on the incompatible list:
|
2. [ ] Make sure you are not using any mods on the incompatible list:
|
||||||
[Mod support FAQ](https://gitlab.com/jeseibel/distant-horizons/-/wikis/2-frequently-asked-questions/4-mod-support/Mod-Support)
|
[Mod support FAQ](https://gitlab.com/distant-horizons-team/distant-horizons/-/wikis/1-user-guide/1-frequently-asked-questions/4-mod-support/Mod-Support)
|
||||||
|
|
||||||
3. [ ] Check the existing issues to verify that your bug hasn't already been submitted:
|
3. [ ] Check the existing issues to verify that your bug hasn't already been submitted:
|
||||||
[Issues](https://gitlab.com/jeseibel/distant-horizons/-/issues/)
|
[Issues](https://gitlab.com/distant-horizons-team/distant-horizons/-/issues)
|
||||||
|
|
||||||
4. [ ] Upload Minecraft's crash report and/or log. \
|
4. [ ] Upload Minecraft's crash report and/or log. \
|
||||||
Minecraft crash reports are located in: `.minecraft/crash-reports` \
|
Minecraft crash reports are located in: `.minecraft/crash-reports` \
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
- [ ] 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.
|
- [ ] 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.
|
||||||
|
|
||||||
1. **Describe the feature**:
|
1. **Describe the feature**:
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
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.
|
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.
|
||||||
|
|
||||||
2. **Describe the improvement**:
|
2. **Describe the improvement**:
|
||||||
|
|||||||
+102
-20
@@ -1,3 +1,11 @@
|
|||||||
|
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 {
|
plugins {
|
||||||
id "java"
|
id "java"
|
||||||
|
|
||||||
@@ -96,6 +104,83 @@ forgix {
|
|||||||
removeDuplicate "com.seibel.distanthorizons"
|
removeDuplicate "com.seibel.distanthorizons"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class NativeTransformer implements Transformer {
|
||||||
|
private boolean enabled = false
|
||||||
|
private final HashMap<String, String> replacements = new HashMap()
|
||||||
|
private final HashMap<String, byte[]> rewrittenFiles = new HashMap()
|
||||||
|
private var nativeRelocator
|
||||||
|
|
||||||
|
public File rootDir
|
||||||
|
|
||||||
|
NativeTransformer() {
|
||||||
|
try {
|
||||||
|
int exitCode = Runtime.getRuntime().exec(new String[]{"python", "--version"}).waitFor()
|
||||||
|
if (exitCode == 0) {
|
||||||
|
enabled = true
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
println(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void relocateNative(String target, String replacement) {
|
||||||
|
if (replacement.length() > target.length()) {
|
||||||
|
throw new GradleException("Length of value \"${replacement}\" exceeds the length of \"${target}\": ${replacement.length()} > ${target.length()}")
|
||||||
|
}
|
||||||
|
|
||||||
|
replacements.put(target, replacement)
|
||||||
|
}
|
||||||
|
|
||||||
|
void before(Closure closure) {
|
||||||
|
if (enabled)
|
||||||
|
closure.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean canTransformResource(@Nonnull FileTreeElement element) {
|
||||||
|
return enabled && replacements.keySet().stream().anyMatch {
|
||||||
|
element.name.startsWith(it as String)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
void transform(@Nonnull TransformerContext context) {
|
||||||
|
println("Transforming $context.path...")
|
||||||
|
byte[] content = context.is.readAllBytes()
|
||||||
|
|
||||||
|
if (nativeRelocator == null) {
|
||||||
|
nativeRelocator = new NativeRelocator(rootDir.toPath().resolve("relocate_natives"))
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
subprojects { p ->
|
subprojects { p ->
|
||||||
// Does the same as "p == project(":common") || p == project(":fabric") || p == project(":quilt") || p == project(":forge") || p == project("WhateverWeAddLaterOn")"
|
// Does the same as "p == project(":common") || p == project(":fabric") || p == project(":quilt") || p == project(":forge") || p == project("WhateverWeAddLaterOn")"
|
||||||
// Useful later on so we dont have duplicated code
|
// Useful later on so we dont have duplicated code
|
||||||
@@ -306,26 +391,23 @@ subprojects { p ->
|
|||||||
|
|
||||||
// Logging
|
// Logging
|
||||||
relocate "org.slf4j", "${librariesLocation}.slf4j"
|
relocate "org.slf4j", "${librariesLocation}.slf4j"
|
||||||
|
|
||||||
// // Sqlite Database
|
// Sqlite Database
|
||||||
// // James can't determine how to relocate the library correctly so this is commented out
|
// librariesLocation isn't used because it's too long for replacing paths in native libraries
|
||||||
// relocate ("org.sqlite", "${librariesLocation}.sqlite") {
|
// Allowing strings larger than the original string would require shifting the entire binary's contents
|
||||||
// exclude("org/sqlite/core/NativeDB/**")
|
transform(NativeTransformer) {
|
||||||
//
|
rootDir = project.rootDir
|
||||||
// exclude("org/sqlite/native/FreeBSD/**")
|
|
||||||
// exclude("org/sqlite/native/Linux-Android/**")
|
before {
|
||||||
// exclude("org/sqlite/native/Linux-Musl/**")
|
relocate "org.sqlite", "dh_sqlite", {
|
||||||
// exclude("org/sqlite/native/Linux/arm/**")
|
exclude "org/sqlite/native/**"
|
||||||
// exclude("org/sqlite/native/Linux/aarch64/**")
|
}
|
||||||
// exclude("org/sqlite/native/Linux/armv6/**")
|
relocate "jdbc:sqlite", "jdbc:dh_sqlite"
|
||||||
// exclude("org/sqlite/native/Linux/x86/**")
|
}
|
||||||
// exclude("org/sqlite/native/Linux/armv7/**")
|
|
||||||
// exclude("org/sqlite/native/Linux/ppc64/**")
|
relocateNative "org/sqlite", "dh_sqlite"
|
||||||
// exclude("org/sqlite/native/Linux/riscv64/**")
|
relocateNative "org_sqlite", "dh_1sqlite"
|
||||||
// exclude("org/sqlite/native/Windows/armv7/**")
|
}
|
||||||
// exclude("org/sqlite/native/Windows/aarch64/**")
|
|
||||||
// exclude("org/sqlite/native/Windows/armv7/**")
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// JOML
|
// JOML
|
||||||
|
|||||||
@@ -0,0 +1,207 @@
|
|||||||
|
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.
|
||||||
|
*
|
||||||
|
* @throws Exception if the preparation script fails or an unsupported OS is detected.
|
||||||
|
*/
|
||||||
|
NativeRelocator(Path rootDirectory) throws Exception
|
||||||
|
{
|
||||||
|
this.rootDirectory = rootDirectory;
|
||||||
|
this.cacheRoot = this.rootDirectory.resolve("cache");
|
||||||
|
|
||||||
|
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", "./prepare.ps1");
|
||||||
|
}
|
||||||
|
else if (os.contains("nix") || os.contains("nux") || os.contains("mac"))
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (Map.Entry<String, String> replacement : replacements.entrySet())
|
||||||
|
{
|
||||||
|
this.replaceInNullTerminatedStrings(content, replacement.getKey(), replacement.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.fixModifiedBinary(outputFilePath, content);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -9,6 +9,7 @@ import com.seibel.distanthorizons.core.config.types.ConfigEntry;
|
|||||||
import net.minecraft.commands.CommandSourceStack;
|
import net.minecraft.commands.CommandSourceStack;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import java.util.function.Function;
|
import java.util.function.Function;
|
||||||
@@ -40,6 +41,7 @@ public class ConfigCommand extends AbstractCommand
|
|||||||
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
|
public LiteralArgumentBuilder<CommandSourceStack> buildCommand()
|
||||||
{
|
{
|
||||||
LiteralArgumentBuilder<CommandSourceStack> builder = literal("config");
|
LiteralArgumentBuilder<CommandSourceStack> builder = literal("config");
|
||||||
|
HashSet<String> addedCommands = new HashSet<>();
|
||||||
|
|
||||||
for (AbstractConfigType<?, ?> type : ConfigBase.INSTANCE.entries)
|
for (AbstractConfigType<?, ?> type : ConfigBase.INSTANCE.entries)
|
||||||
{
|
{
|
||||||
@@ -56,6 +58,11 @@ public class ConfigCommand extends AbstractCommand
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!addedCommands.add(configEntry.getChatCommandName()))
|
||||||
|
{
|
||||||
|
throw new IllegalStateException("Duplicate command name: " + configEntry.getChatCommandName());
|
||||||
|
}
|
||||||
|
|
||||||
LiteralArgumentBuilder<CommandSourceStack> subcommand = literal(configEntry.getChatCommandName())
|
LiteralArgumentBuilder<CommandSourceStack> subcommand = literal(configEntry.getChatCommandName())
|
||||||
.executes(commandContext -> this.sendSuccessResponse(commandContext,
|
.executes(commandContext -> this.sendSuccessResponse(commandContext,
|
||||||
"\n" +
|
"\n" +
|
||||||
|
|||||||
@@ -38,24 +38,6 @@ public class VersionConstants implements IVersionConstants
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMinimumWorldHeight()
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getWorldGenerationCountPerThread()
|
|
||||||
{
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isVanillaRenderedChunkSquare()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getMinecraftVersion()
|
public String getMinecraftVersion()
|
||||||
{
|
{
|
||||||
|
|||||||
+31
-11
@@ -43,6 +43,8 @@ import java.util.*;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
#if MC_VER == MC_1_16_5 || MC_VER == MC_1_17_1
|
||||||
import net.minecraft.core.Registry;
|
import net.minecraft.core.Registry;
|
||||||
import net.minecraft.core.BlockPos;
|
import net.minecraft.core.BlockPos;
|
||||||
@@ -84,12 +86,13 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
|||||||
public static HashSet<IBlockStateWrapper> rendererIgnoredCaveBlocks = null;
|
public static HashSet<IBlockStateWrapper> rendererIgnoredCaveBlocks = null;
|
||||||
|
|
||||||
/** keep track of broken blocks so we don't log every time */
|
/** keep track of broken blocks so we don't log every time */
|
||||||
private static final HashSet<ResourceLocation> BrokenResourceLocations = new HashSet<>();
|
private static final HashSet<ResourceLocation> BROKEN_RESOURCE_LOCATIONS = new HashSet<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// properties //
|
// properties //
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public final BlockState blockState;
|
public final BlockState blockState;
|
||||||
/** technically final, but since it requires a method call to generate it can't be marked as such */
|
/** technically final, but since it requires a method call to generate it can't be marked as such */
|
||||||
private String serialString;
|
private String serialString;
|
||||||
@@ -350,25 +353,42 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// get block properties (default to the values used by air)
|
||||||
|
boolean canOcclude = false;
|
||||||
|
boolean propagatesSkyLightDown = true;
|
||||||
|
if (this.blockState != null)
|
||||||
|
{
|
||||||
|
canOcclude = this.blockState.canOcclude();
|
||||||
|
|
||||||
|
#if MC_VER < MC_1_21_3
|
||||||
|
propagatesSkyLightDown = this.blockState.propagatesSkylightDown(EmptyBlockGetter.INSTANCE, BlockPos.ZERO);
|
||||||
|
#else
|
||||||
|
propagatesSkyLightDown = this.blockState.propagatesSkylightDown();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// this method isn't perfect, but works well enough for our use case
|
// this method isn't perfect, but works well enough for our use case
|
||||||
int opacity;
|
int opacity;
|
||||||
if (this.isAir())
|
if (this.isAir())
|
||||||
{
|
{
|
||||||
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
|
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
|
||||||
}
|
}
|
||||||
else if (this.isLiquid() && !this.blockState.canOcclude())
|
else if (this.isLiquid() && !canOcclude)
|
||||||
{
|
{
|
||||||
// probably not a waterlogged block (which should block light entirely)
|
// probably not a waterlogged block (which should block light entirely)
|
||||||
|
|
||||||
// +1 to indicate that the block is translucent (in between transparent and opaque)
|
// +1 to indicate that the block is translucent (in between transparent and opaque)
|
||||||
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT + 1;
|
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT + 1;
|
||||||
}
|
}
|
||||||
#if MC_VER < MC_1_21_3
|
else if (propagatesSkyLightDown && !canOcclude)
|
||||||
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;
|
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -596,9 +616,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
|||||||
if (block == null)
|
if (block == null)
|
||||||
{
|
{
|
||||||
// shouldn't normally happen, but here to make the compiler happy
|
// shouldn't normally happen, but here to make the compiler happy
|
||||||
if (!BrokenResourceLocations.contains(resourceLocation))
|
if (!BROKEN_RESOURCE_LOCATIONS.contains(resourceLocation))
|
||||||
{
|
{
|
||||||
BrokenResourceLocations.add(resourceLocation);
|
BROKEN_RESOURCE_LOCATIONS.add(resourceLocation);
|
||||||
LOGGER.warn("Unable to find BlockState with the resourceLocation [" + resourceLocation + "] and properties: [" + blockStatePropertiesString + "]. Air will be used instead, some data may be lost.");
|
LOGGER.warn("Unable to find BlockState with the resourceLocation [" + resourceLocation + "] and properties: [" + blockStatePropertiesString + "]. Air will be used instead, some data may be lost.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,9 +648,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
|
|||||||
if (blockStatePropertiesString != null)
|
if (blockStatePropertiesString != null)
|
||||||
{
|
{
|
||||||
// we should have found a blockstate, but didn't
|
// we should have found a blockstate, but didn't
|
||||||
if (!BrokenResourceLocations.contains(resourceLocation))
|
if (!BROKEN_RESOURCE_LOCATIONS.contains(resourceLocation))
|
||||||
{
|
{
|
||||||
BrokenResourceLocations.add(resourceLocation);
|
BROKEN_RESOURCE_LOCATIONS.add(resourceLocation);
|
||||||
LOGGER.warn("Unable to find BlockState for Block [" + resourceLocation + "] with properties: [" + blockStatePropertiesString + "]. Using the default block state.");
|
LOGGER.warn("Unable to find BlockState for Block [" + resourceLocation + "] with properties: [" + blockStatePropertiesString + "]. Using the default block state.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-2
@@ -278,14 +278,14 @@ public class ChunkWrapper implements IChunkWrapper
|
|||||||
{
|
{
|
||||||
// is this block solid?
|
// is this block solid?
|
||||||
if (solidHeight == minInclusiveBuildHeight
|
if (solidHeight == minInclusiveBuildHeight
|
||||||
&& block.isSolid())
|
&& block.isSolid())
|
||||||
{
|
{
|
||||||
solidHeight = y;
|
solidHeight = y;
|
||||||
}
|
}
|
||||||
|
|
||||||
// is this block light blocking?
|
// is this block light blocking?
|
||||||
if (lightBlockingHeight == minInclusiveBuildHeight
|
if (lightBlockingHeight == minInclusiveBuildHeight
|
||||||
&& block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT)
|
&& block.getOpacity() != LodUtil.BLOCK_FULLY_TRANSPARENT)
|
||||||
{
|
{
|
||||||
lightBlockingHeight = y;
|
lightBlockingHeight = y;
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-3
@@ -214,17 +214,29 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
|
|||||||
@Override
|
@Override
|
||||||
public DhBlockPos getPlayerBlockPos()
|
public DhBlockPos getPlayerBlockPos()
|
||||||
{
|
{
|
||||||
BlockPos playerPos = this.getPlayer().blockPosition();
|
LocalPlayer player = this.getPlayer();
|
||||||
|
if (player == null)
|
||||||
|
{
|
||||||
|
return new DhBlockPos(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockPos playerPos = player.blockPosition();
|
||||||
return new DhBlockPos(playerPos.getX(), playerPos.getY(), playerPos.getZ());
|
return new DhBlockPos(playerPos.getX(), playerPos.getY(), playerPos.getZ());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public DhChunkPos getPlayerChunkPos()
|
public DhChunkPos getPlayerChunkPos()
|
||||||
{
|
{
|
||||||
|
LocalPlayer player = this.getPlayer();
|
||||||
|
if (player == null)
|
||||||
|
{
|
||||||
|
return new DhChunkPos(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
#if MC_VER < MC_1_17_1
|
#if MC_VER < MC_1_17_1
|
||||||
ChunkPos playerPos = new ChunkPos(this.getPlayer().blockPosition());
|
ChunkPos playerPos = new ChunkPos(player.blockPosition());
|
||||||
#else
|
#else
|
||||||
ChunkPos playerPos = this.getPlayer().chunkPosition();
|
ChunkPos playerPos = player.chunkPosition();
|
||||||
#endif
|
#endif
|
||||||
return new DhChunkPos(playerPos.x, playerPos.z);
|
return new DhChunkPos(playerPos.x, playerPos.z);
|
||||||
}
|
}
|
||||||
|
|||||||
+42
-33
@@ -28,72 +28,80 @@ import net.minecraft.world.level.dimension.DimensionType;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @author James Seibel
|
* @author James Seibel
|
||||||
* @version 2022-9-16
|
|
||||||
*/
|
*/
|
||||||
public class DimensionTypeWrapper implements IDimensionTypeWrapper
|
public class DimensionTypeWrapper implements IDimensionTypeWrapper
|
||||||
{
|
{
|
||||||
private static final ConcurrentMap<DimensionType, DimensionTypeWrapper> dimensionTypeWrapperMap = new ConcurrentHashMap<>();
|
private static final ConcurrentMap<String, DimensionTypeWrapper> DIMENSION_WRAPPER_BY_NAME = new ConcurrentHashMap<>();
|
||||||
private final DimensionType dimensionType;
|
private final DimensionType dimensionType;
|
||||||
|
|
||||||
public DimensionTypeWrapper(DimensionType dimensionType)
|
|
||||||
{
|
|
||||||
this.dimensionType = dimensionType;
|
//=============//
|
||||||
}
|
// Constructor //
|
||||||
|
//=============//
|
||||||
|
|
||||||
|
public DimensionTypeWrapper(DimensionType dimensionType) { this.dimensionType = dimensionType; }
|
||||||
|
|
||||||
public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType)
|
public static DimensionTypeWrapper getDimensionTypeWrapper(DimensionType dimensionType)
|
||||||
{
|
{
|
||||||
//first we check if the biome has already been wrapped
|
String dimName = getName(dimensionType);
|
||||||
if (dimensionTypeWrapperMap.containsKey(dimensionType) && dimensionTypeWrapperMap.get(dimensionType) != null)
|
|
||||||
|
// check if the dimension has already been wrapped
|
||||||
|
if (DIMENSION_WRAPPER_BY_NAME.containsKey(dimName)
|
||||||
|
&& DIMENSION_WRAPPER_BY_NAME.get(dimName) != null)
|
||||||
{
|
{
|
||||||
return dimensionTypeWrapperMap.get(dimensionType);
|
return DIMENSION_WRAPPER_BY_NAME.get(dimName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//if it hasn't been created yet, we create it and save it in the map
|
// create the missing wrapper
|
||||||
DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType);
|
DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType);
|
||||||
dimensionTypeWrapperMap.put(dimensionType, dimensionTypeWrapper);
|
DIMENSION_WRAPPER_BY_NAME.put(dimName, dimensionTypeWrapper);
|
||||||
|
|
||||||
//we return the newly created wrapper
|
|
||||||
return dimensionTypeWrapper;
|
return dimensionTypeWrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void clearMap()
|
public static void clearMap() { DIMENSION_WRAPPER_BY_NAME.clear(); }
|
||||||
{
|
|
||||||
dimensionTypeWrapperMap.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private String getDimensionName()
|
|
||||||
|
//=================//
|
||||||
|
// wrapper methods //
|
||||||
|
//=================//
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getName() { return getName(this.dimensionType); }
|
||||||
|
public static String getName(DimensionType dimensionType)
|
||||||
{
|
{
|
||||||
#if MC_VER <= MC_1_16_5
|
#if MC_VER <= MC_1_16_5
|
||||||
// effectsLocation() is marked as client only, so using the backing field directly
|
// effectsLocation() is marked as client only, so using the backing field directly
|
||||||
return dimensionType.effectsLocation.getPath();
|
return dimensionType.effectsLocation.getPath();
|
||||||
#else
|
#else
|
||||||
return this.dimensionType.effectsLocation().getPath();
|
return dimensionType.effectsLocation().getPath();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasCeiling()
|
public boolean hasCeiling() { return this.dimensionType.hasCeiling(); }
|
||||||
{
|
|
||||||
return this.dimensionType.hasCeiling();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasSkyLight()
|
public boolean hasSkyLight() { return this.dimensionType.hasSkyLight(); }
|
||||||
{
|
|
||||||
return this.dimensionType.hasSkyLight();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object getWrappedMcObject()
|
public Object getWrappedMcObject() { return this.dimensionType; }
|
||||||
{
|
|
||||||
return this.dimensionType;
|
|
||||||
}
|
|
||||||
|
|
||||||
// there's definitely a better way of doing this, but it should work well enough for now
|
// there's definitely a better way of doing this, but it should work well enough for now
|
||||||
@Override
|
@Override
|
||||||
public boolean isTheEnd() { return this.getDimensionName().equalsIgnoreCase("the_end"); }
|
public boolean isTheEnd() { return this.getName().equalsIgnoreCase("the_end"); }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getCoordinateScale() { return this.dimensionType.coordinateScale(); }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//================//
|
||||||
|
// base overrides //
|
||||||
|
//================//
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean equals(Object obj)
|
public boolean equals(Object obj)
|
||||||
@@ -105,9 +113,10 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
DimensionTypeWrapper other = (DimensionTypeWrapper) obj;
|
DimensionTypeWrapper other = (DimensionTypeWrapper) obj;
|
||||||
return other.getDimensionName().equals(this.getDimensionName());
|
return other.getName().equals(this.getName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+38
-31
@@ -125,7 +125,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
|||||||
* pull chunks using their async method), or if there
|
* pull chunks using their async method), or if there
|
||||||
* was an issue with the sync pulling method.
|
* was an issue with the sync pulling method.
|
||||||
*/
|
*/
|
||||||
private boolean pullExistingChunkAsync = false;
|
private boolean pullExistingChunkUsingMcAsyncMethod = false;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -241,19 +241,10 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
|||||||
|
|
||||||
if (MOD_CHECKER.isModLoaded("c2me"))
|
if (MOD_CHECKER.isModLoaded("c2me"))
|
||||||
{
|
{
|
||||||
EVENT_LOGGER.info("C2ME detected: DH's pre-existing chunk accessing will use async methods handled by C2ME.");
|
EVENT_LOGGER.info("C2ME detected: DH's pre-existing chunk accessing will use methods handled by C2ME.");
|
||||||
this.pullExistingChunkAsync = true;
|
this.pullExistingChunkUsingMcAsyncMethod = 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);
|
this.params = new GlobalParameters(serverlevel);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,6 +333,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
|||||||
// world generation //
|
// 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. */
|
/** @throws RejectedExecutionException if the given {@link Executor} is cancelled. */
|
||||||
public CompletableFuture<Void> generateLodFromListAsync(GenerationEvent genEvent, Executor executor) throws RejectedExecutionException, InterruptedException
|
public CompletableFuture<Void> generateLodFromListAsync(GenerationEvent genEvent, Executor executor) throws RejectedExecutionException, InterruptedException
|
||||||
{
|
{
|
||||||
@@ -385,10 +378,12 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
|||||||
.map((chunkPos) -> this.createEmptyOrPreExistingChunkAsync(chunkPos.x, chunkPos.z, chunkSkyLightingByDhPos, chunkBlockLightingByDhPos, generatedChunkByDhPos))
|
.map((chunkPos) -> this.createEmptyOrPreExistingChunkAsync(chunkPos.x, chunkPos.z, chunkSkyLightingByDhPos, chunkBlockLightingByDhPos, generatedChunkByDhPos))
|
||||||
.toArray(CompletableFuture[]::new);
|
.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
|
// future chain for generation
|
||||||
return CompletableFuture.allOf(readFutures)
|
return CompletableFuture.runAsync(() ->
|
||||||
.thenRunAsync(() ->
|
|
||||||
{
|
{
|
||||||
// offset 1 chunk in both X and Z direction so we can generate an even number of chunks wide
|
// 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
|
// while still submitting an odd number width to MC's internal generators
|
||||||
@@ -424,7 +419,8 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
|||||||
|
|
||||||
ChunkAccess centerChunk = regionChunks.stream()
|
ChunkAccess centerChunk = regionChunks.stream()
|
||||||
.filter((chunk) -> chunk.getPos().x == centerX && chunk.getPos().z == centerZ)
|
.filter((chunk) -> chunk.getPos().x == centerX && chunk.getPos().z == centerZ)
|
||||||
.findFirst().get();
|
.findFirst()
|
||||||
|
.orElseGet(() -> regionChunks.getFirst());
|
||||||
|
|
||||||
genEvent.refreshTimeout();
|
genEvent.refreshTimeout();
|
||||||
DhLitWorldGenRegion region = new DhLitWorldGenRegion(
|
DhLitWorldGenRegion region = new DhLitWorldGenRegion(
|
||||||
@@ -591,6 +587,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
|||||||
return newChunk;
|
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)
|
private CompletableFuture<CompoundTag> getChunkNbtDataAsync(ChunkPos chunkPos)
|
||||||
{
|
{
|
||||||
ServerLevel level = this.params.level;
|
ServerLevel level = this.params.level;
|
||||||
@@ -609,7 +606,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
|||||||
#else
|
#else
|
||||||
|
|
||||||
// storage will be null if C2ME is installed
|
// storage will be null if C2ME is installed
|
||||||
if (!this.pullExistingChunkAsync && ioWorker.storage != null)
|
if (!this.pullExistingChunkUsingMcAsyncMethod && ioWorker.storage != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@@ -623,7 +620,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
|||||||
// ioWorker.storage
|
// ioWorker.storage
|
||||||
// but just in case
|
// 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);
|
EVENT_LOGGER.error("Unexpected issue pulling pre-existing chunk ["+chunkPos+"], falling back to async chunk pulling. This may cause server-tick lag.", e);
|
||||||
this.pullExistingChunkAsync = true;
|
this.pullExistingChunkUsingMcAsyncMethod = true;
|
||||||
|
|
||||||
// try again now using the async method
|
// try again now using the async method
|
||||||
return this.getChunkNbtDataAsync(chunkPos);
|
return this.getChunkNbtDataAsync(chunkPos);
|
||||||
@@ -632,32 +629,42 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
// log if we unexpectedly weren't able to run the sync chunk pulling
|
// log if we unexpectedly weren't able to run the sync chunk pulling
|
||||||
if (!this.pullExistingChunkAsync)
|
if (!this.pullExistingChunkUsingMcAsyncMethod)
|
||||||
{
|
{
|
||||||
// this shouldn't happen, but just in case
|
// 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.");
|
EVENT_LOGGER.info("Unable to pull pre-existing chunk using synchronous method. Falling back to async method. this may cause server-tick lag.");
|
||||||
this.pullExistingChunkAsync = true;
|
this.pullExistingChunkUsingMcAsyncMethod = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//GET_CHUNK_COUNT_REF.incrementAndGet();
|
||||||
|
|
||||||
// When running in vanilla MC on versions before 1.21.4,
|
// 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,
|
// DH would attempt to run loadAsync on this same thread via a threading mixin,
|
||||||
// to prevent causing lag on the server thread.
|
// to prevent causing lag on the server thread.
|
||||||
// However, if a mod like C2ME is installed this will run on a C2ME thread instead.
|
// However, if a mod like C2ME is installed this will run on a C2ME thread instead.
|
||||||
return ioWorker.loadAsync(chunkPos)
|
return ioWorker.loadAsync(chunkPos)
|
||||||
.thenApply(optional -> optional.orElse(null))
|
.thenApply(optional ->
|
||||||
.exceptionally((throwable) ->
|
|
||||||
{
|
|
||||||
// unwrap the CompletionException if necessary
|
|
||||||
Throwable actualThrowable = throwable;
|
|
||||||
while (actualThrowable instanceof CompletionException completionException)
|
|
||||||
{
|
{
|
||||||
actualThrowable = completionException.getCause();
|
// 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.
|
||||||
LOAD_LOGGER.warn("DistantHorizons: Couldn't load or make chunk ["+chunkPos+"], error: ["+actualThrowable.getMessage()+"].", actualThrowable);
|
|
||||||
return null;
|
//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;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
+62
-11
@@ -29,6 +29,7 @@ import com.seibel.distanthorizons.core.util.LodUtil;
|
|||||||
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
|
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
|
||||||
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
|
||||||
import it.unimi.dsi.fastutil.shorts.ShortList;
|
import it.unimi.dsi.fastutil.shorts.ShortList;
|
||||||
@@ -103,6 +104,8 @@ public class ChunkLoader
|
|||||||
|
|
||||||
private static boolean lightingSectionErrorLogged = false;
|
private static boolean lightingSectionErrorLogged = false;
|
||||||
|
|
||||||
|
private static final ConcurrentHashMap<String, Object> LOGGED_ERROR_MESSAGE_MAP = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//============//
|
//============//
|
||||||
@@ -285,17 +288,18 @@ public class ChunkLoader
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
blockStateContainer = tagSection.contains("block_states", 10)
|
blockStateContainer = tagSection.contains("block_states", 10)
|
||||||
? BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagSection.getCompound("block_states")).promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
|
? BLOCK_STATE_CODEC.parse(NbtOps.INSTANCE, tagSection.getCompound("block_states"))
|
||||||
|
.promotePartial(string -> logBlockDeserializationWarning(chunkPos, sectionYPos, string))
|
||||||
#if MC_VER < MC_1_20_6
|
#if MC_VER < MC_1_20_6
|
||||||
.getOrThrow(false, LOGGER::error)
|
.getOrThrow(false, (message) -> logWarningOnce(message))
|
||||||
#else
|
#else
|
||||||
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null))
|
.getOrThrow((message) -> logErrorAndReturnException(message))
|
||||||
#endif
|
#endif
|
||||||
: new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
|
: new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
|
||||||
|
|
||||||
#if MC_VER < MC_1_18_2
|
#if MC_VER < MC_1_18_2
|
||||||
biomeContainer = tagSection.contains("biomes", 10)
|
biomeContainer = tagSection.contains("biomes", 10)
|
||||||
? biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logErrors(chunkPos, sectionYPos, string)).getOrThrow(false, LOGGER::error)
|
? 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);
|
: new PalettedContainer<Biome>(biomes, biomes.getOrThrow(Biomes.PLAINS), PalettedContainer.Strategy.SECTION_BIOMES);
|
||||||
#else
|
#else
|
||||||
|
|
||||||
@@ -303,11 +307,12 @@ public class ChunkLoader
|
|||||||
if (tagSection.contains("biomes", 10))
|
if (tagSection.contains("biomes", 10))
|
||||||
{
|
{
|
||||||
biomeContainer =
|
biomeContainer =
|
||||||
biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes")).promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
|
biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes"))
|
||||||
|
.promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
|
||||||
#if MC_VER < MC_1_20_6
|
#if MC_VER < MC_1_20_6
|
||||||
.getOrThrow(false, LOGGER::error);
|
.getOrThrow(false, (message) -> logWarningOnce(message));
|
||||||
#else
|
#else
|
||||||
.getOrThrow((message) -> (RuntimeException) LOGGER.errorAndThrow(message, null));
|
.getOrThrow((message) -> logErrorAndReturnException(message));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -387,9 +392,9 @@ public class ChunkLoader
|
|||||||
Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data"));
|
Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data"));
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_3
|
#if MC_VER < MC_1_21_3
|
||||||
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial(LOGGER::error).orElse(null);
|
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial((message) -> logWarningOnce(message)).orElse(null);
|
||||||
#else
|
#else
|
||||||
blendingData = BlendingData.unpack(BlendingData.Packed.CODEC.parse(blendingDataTag).resultOrPartial(LOGGER::error).orElse(null));
|
blendingData = BlendingData.unpack(BlendingData.Packed.CODEC.parse(blendingDataTag).resultOrPartial((message) -> logWarningOnce(message)).orElse(null));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
return blendingData;
|
return blendingData;
|
||||||
@@ -515,15 +520,61 @@ public class ChunkLoader
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
//=========//
|
||||||
|
// logging //
|
||||||
|
//=========//
|
||||||
|
|
||||||
private static void logBlockDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
|
private static void logBlockDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
|
||||||
{
|
{
|
||||||
LOGGER.warn("Unable to deserialize blocks for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+message+"]. This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
|
||||||
|
{
|
||||||
|
LOGGER.warn("Unable to deserialize blocks for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
|
||||||
|
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
||||||
|
|
||||||
|
return newMessage;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
private static void logBiomeDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
|
private static void logBiomeDeserializationWarning(ChunkPos chunkPos, int sectionYIndex, String message)
|
||||||
{
|
{
|
||||||
LOGGER.warn("Unable to deserialize biomes for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+message+"]. This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
|
||||||
|
{
|
||||||
|
LOGGER.warn("Unable to deserialize biomes for chunk section [" + chunkPos.x + ", " + sectionYIndex + ", " + chunkPos.z + "], error: ["+newMessage+"]. " +
|
||||||
|
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
||||||
|
|
||||||
|
return newMessage;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void logWarningOnce(String message) { logWarningOnce(message, null); }
|
||||||
|
private static void logWarningOnce(String message, Exception e)
|
||||||
|
{
|
||||||
|
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
|
||||||
|
{
|
||||||
|
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
|
||||||
|
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.",
|
||||||
|
e);
|
||||||
|
|
||||||
|
return newMessage;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RuntimeException logErrorAndReturnException(String message)
|
||||||
|
{
|
||||||
|
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
|
||||||
|
{
|
||||||
|
LOGGER.warn("Parsing error: ["+newMessage+"]. " +
|
||||||
|
"This can probably be ignored, although if your world looks wrong, optimizing it via the single player menu then deleting your DH database(s) should fix the problem.");
|
||||||
|
|
||||||
|
return newMessage;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Currently we want to ignore these errors, if returning null is a problem, we can change this later
|
||||||
|
return null; //new RuntimeException(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//================//
|
//================//
|
||||||
|
|||||||
+1
-1
Submodule coreSubProjects updated: 863bfbaff5...e701c0e5ea
+19
-10
@@ -76,15 +76,25 @@ public abstract class MixinMinecraft
|
|||||||
|
|
||||||
if (SelfUpdater.onStart() || DEBUG_ALWAYS_SHOW_UPDATER)
|
if (SelfUpdater.onStart() || DEBUG_ALWAYS_SHOW_UPDATER)
|
||||||
{
|
{
|
||||||
instance.setScreen(new UpdateModScreen(
|
try
|
||||||
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"))
|
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
|
));
|
||||||
{
|
return;
|
||||||
instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened
|
}
|
||||||
|
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()+"].");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets the screen back to the vanilla screen as if nothing ever happened
|
||||||
|
// if not done the game will crash
|
||||||
|
instance.setScreen(guiScreen);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -124,14 +134,13 @@ public abstract class MixinMinecraft
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
Minecraft.getInstance().setScreen(new UpdateModScreen(
|
Minecraft.getInstance().setScreen(new UpdateModScreen(
|
||||||
// TODO: Change to runnable, instead of tittle screen
|
// 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
|
new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
|
||||||
versionId
|
versionId
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
// info instead of error since this can be ignored and probably just means
|
// info instead of error since this can be ignored and probably just means
|
||||||
// there isn't a new DH version available
|
// there isn't a new DH version available
|
||||||
|
|||||||
-55
@@ -1,55 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.fabric.mixins.server;
|
|
||||||
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
|
|
||||||
import net.minecraft.Util;
|
|
||||||
import net.minecraft.world.entity.Entity;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@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
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
#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)
|
|
||||||
public class MixinLevelTicks<T>
|
|
||||||
{
|
|
||||||
// TODO put in a common location
|
|
||||||
private static boolean isWorldGenThread()
|
|
||||||
{ return DependencySetupDoneCheck.isDone && DependencySetupDoneCheck.getIsCurrentThreadDistantGeneratorThread.get(); }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Inject(method = "schedule", at = @At(value = "HEAD"), cancellable = true)
|
|
||||||
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).
|
|
||||||
if (isWorldGenThread())
|
|
||||||
{
|
|
||||||
ci.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
+12
-12
@@ -46,8 +46,8 @@ public class TestGenericWorldGenerator implements IDhApiWorldGenerator
|
|||||||
public byte getSmallestDataDetailLevel() { return (byte) (EDhApiDetailLevel.BLOCK.detailLevel); }
|
public byte getSmallestDataDetailLevel() { return (byte) (EDhApiDetailLevel.BLOCK.detailLevel); }
|
||||||
@Override
|
@Override
|
||||||
public byte getLargestDataDetailLevel()
|
public byte getLargestDataDetailLevel()
|
||||||
//{ return (byte) (EDhApiDetailLevel.BLOCK.detailLevel + 12); }
|
{ return (byte) (EDhApiDetailLevel.BLOCK.detailLevel + 12); }
|
||||||
{ return (byte) (EDhApiDetailLevel.BLOCK.detailLevel); }
|
//{ return (byte) (EDhApiDetailLevel.BLOCK.detailLevel); }
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -105,43 +105,43 @@ public class TestGenericWorldGenerator implements IDhApiWorldGenerator
|
|||||||
{
|
{
|
||||||
case 0:
|
case 0:
|
||||||
blockResourceLocation = "minecraft:red_wool";
|
blockResourceLocation = "minecraft:red_wool";
|
||||||
maxHeight = 60;
|
maxHeight = 20;
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
blockResourceLocation = "minecraft:orange_wool";
|
blockResourceLocation = "minecraft:orange_wool";
|
||||||
maxHeight = 70;
|
maxHeight = 30;
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
blockResourceLocation = "minecraft:yellow_wool";
|
blockResourceLocation = "minecraft:yellow_wool";
|
||||||
maxHeight = 80;
|
maxHeight = 40;
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
blockResourceLocation = "minecraft:lime_wool";
|
blockResourceLocation = "minecraft:lime_wool";
|
||||||
maxHeight = 90;
|
maxHeight = 50;
|
||||||
break;
|
break;
|
||||||
case 4:
|
case 4:
|
||||||
blockResourceLocation = "minecraft:cyan_wool";
|
blockResourceLocation = "minecraft:cyan_wool";
|
||||||
maxHeight = 100;
|
maxHeight = 60;
|
||||||
break;
|
break;
|
||||||
case 5:
|
case 5:
|
||||||
blockResourceLocation = "minecraft:blue_wool";
|
blockResourceLocation = "minecraft:blue_wool";
|
||||||
maxHeight = 100;
|
maxHeight = 70;
|
||||||
break;
|
break;
|
||||||
case 6:
|
case 6:
|
||||||
blockResourceLocation = "minecraft:magenta_wool";
|
blockResourceLocation = "minecraft:magenta_wool";
|
||||||
maxHeight = 110;
|
maxHeight = 80;
|
||||||
break;
|
break;
|
||||||
case 7:
|
case 7:
|
||||||
blockResourceLocation = "minecraft:white_wool";
|
blockResourceLocation = "minecraft:white_wool";
|
||||||
maxHeight = 120;
|
maxHeight = 90;
|
||||||
break;
|
break;
|
||||||
case 8:
|
case 8:
|
||||||
blockResourceLocation = "minecraft:gray_wool";
|
blockResourceLocation = "minecraft:gray_wool";
|
||||||
maxHeight = 120;
|
maxHeight = 100;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
blockResourceLocation = "minecraft:black_wool";
|
blockResourceLocation = "minecraft:black_wool";
|
||||||
maxHeight = 140;
|
maxHeight = 110;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -25,7 +25,7 @@ public class TestWorldGenBindingEvent extends DhApiLevelLoadEvent
|
|||||||
ServerLevel level = (ServerLevel) event.value.levelWrapper.getWrappedMcObject();
|
ServerLevel level = (ServerLevel) event.value.levelWrapper.getWrappedMcObject();
|
||||||
|
|
||||||
// override the core DH world generator for this level
|
// override the core DH world generator for this level
|
||||||
//IDhApiWorldGenerator exampleWorldGen = new TestChunkWorldGenerator(level);
|
//IDhApiWorldGenerator exampleWorldGen = new TestChunkWorldGenerator(level); // TODO biomes are broken for some reason
|
||||||
IDhApiWorldGenerator exampleWorldGen = new TestGenericWorldGenerator(event.value.levelWrapper);
|
IDhApiWorldGenerator exampleWorldGen = new TestGenericWorldGenerator(event.value.levelWrapper);
|
||||||
DhApi.worldGenOverrides.registerWorldGeneratorOverride(event.value.levelWrapper, exampleWorldGen);
|
DhApi.worldGenOverrides.registerWorldGeneratorOverride(event.value.levelWrapper, exampleWorldGen);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,8 +8,7 @@
|
|||||||
"server.MixinEntity",
|
"server.MixinEntity",
|
||||||
"server.MixinServerPlayer",
|
"server.MixinServerPlayer",
|
||||||
"server.MixinTracingExecutor",
|
"server.MixinTracingExecutor",
|
||||||
"server.MixinUtilBackgroundThread",
|
"server.MixinUtilBackgroundThread"
|
||||||
"server.MixinLevelTicks"
|
|
||||||
],
|
],
|
||||||
"client": [
|
"client": [
|
||||||
"client.MixinClientLevel",
|
"client.MixinClientLevel",
|
||||||
|
|||||||
+19
-9
@@ -57,15 +57,25 @@ public class MixinMinecraft
|
|||||||
|
|
||||||
if (SelfUpdater.onStart())
|
if (SelfUpdater.onStart())
|
||||||
{
|
{
|
||||||
instance.setScreen(new UpdateModScreen(
|
try
|
||||||
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"))
|
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
|
));
|
||||||
{
|
return;
|
||||||
instance.setScreen(guiScreen); // Sets the screen back to the vanilla screen as if nothing ever happened
|
}
|
||||||
|
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()+"].");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sets the screen back to the vanilla screen as if nothing ever happened
|
||||||
|
// if not done the game will crash
|
||||||
|
instance.setScreen(guiScreen);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -108,7 +118,7 @@ public class MixinMinecraft
|
|||||||
versionId
|
versionId
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
// info instead of error since this can be ignored and probably just means
|
// info instead of error since this can be ignored and probably just means
|
||||||
// there isn't a new DH version available
|
// there isn't a new DH version available
|
||||||
|
|||||||
+1
-1
@@ -5,7 +5,7 @@ org.gradle.caching=true
|
|||||||
|
|
||||||
# Mod Info
|
# Mod Info
|
||||||
mod_name=DistantHorizons
|
mod_name=DistantHorizons
|
||||||
mod_version=2.3.0-b-dev
|
mod_version=2.3.0-b
|
||||||
api_version=4.0.0
|
api_version=4.0.0
|
||||||
maven_group=com.seibel.distanthorizons
|
maven_group=com.seibel.distanthorizons
|
||||||
mod_readable_name=Distant Horizons
|
mod_readable_name=Distant Horizons
|
||||||
|
|||||||
+1
-2
@@ -76,14 +76,13 @@ public class MixinMinecraft
|
|||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
|
||||||
Minecraft.getInstance().setScreen(new UpdateModScreen(
|
Minecraft.getInstance().setScreen(new UpdateModScreen(
|
||||||
// TODO: Change to runnable, instead of tittle screen
|
// 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
|
new TitleScreen(false), // We don't want to use the vanilla title screen as it would fade the buttons
|
||||||
versionId
|
versionId
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
// info instead of error since this can be ignored and probably just means
|
// info instead of error since this can be ignored and probably just means
|
||||||
// there isn't a new DH version available
|
// there isn't a new DH version available
|
||||||
|
|||||||
-53
@@ -1,53 +0,0 @@
|
|||||||
package com.seibel.distanthorizons.neoforge.mixins.server;
|
|
||||||
|
|
||||||
|
|
||||||
#if MC_VER < MC_1_21_3
|
|
||||||
|
|
||||||
import net.minecraft.Util;
|
|
||||||
import org.spongepowered.asm.mixin.Mixin;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* {@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>
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
#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)
|
|
||||||
public class MixinLevelTicks<T>
|
|
||||||
{
|
|
||||||
// TODO put in a common location
|
|
||||||
private static boolean isWorldGenThread()
|
|
||||||
{ return DependencySetupDoneCheck.isDone && DependencySetupDoneCheck.getIsCurrentThreadDistantGeneratorThread.get(); }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@Inject(method = "schedule", at = @At(value = "HEAD"), cancellable = true)
|
|
||||||
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).
|
|
||||||
if (isWorldGenThread())
|
|
||||||
{
|
|
||||||
ci.cancel();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
@@ -8,8 +8,7 @@
|
|||||||
"server.MixinServerPlayer",
|
"server.MixinServerPlayer",
|
||||||
"server.MixinTFChunkGenerator",
|
"server.MixinTFChunkGenerator",
|
||||||
"server.MixinTracingExecutor",
|
"server.MixinTracingExecutor",
|
||||||
"server.MixinUtilBackgroundThread",
|
"server.MixinUtilBackgroundThread"
|
||||||
"server.MixinLevelTicks"
|
|
||||||
],
|
],
|
||||||
"client": [
|
"client": [
|
||||||
"client.MixinClientPacketListener",
|
"client.MixinClientPacketListener",
|
||||||
|
|||||||
@@ -0,0 +1,110 @@
|
|||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import requests
|
||||||
|
import tarfile
|
||||||
|
import zipfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def get_platform_specific_filename():
|
||||||
|
system = platform.system()
|
||||||
|
machine = platform.machine()
|
||||||
|
|
||||||
|
if system == "Darwin":
|
||||||
|
if machine == "arm64":
|
||||||
|
return "apple-codesign-*-aarch64-apple-darwin.tar.gz"
|
||||||
|
else:
|
||||||
|
return "apple-codesign-*-x86_64-apple-darwin.tar.gz"
|
||||||
|
elif system == "Linux":
|
||||||
|
if machine == "aarch64":
|
||||||
|
return "apple-codesign-*-aarch64-unknown-linux-musl.tar.gz"
|
||||||
|
else:
|
||||||
|
return "apple-codesign-*-x86_64-unknown-linux-musl.tar.gz"
|
||||||
|
elif system == "Windows":
|
||||||
|
if machine.endswith("64"):
|
||||||
|
return "apple-codesign-*-x86_64-pc-windows-msvc.zip"
|
||||||
|
else:
|
||||||
|
return "apple-codesign-*-i686-pc-windows-msvc.zip"
|
||||||
|
else:
|
||||||
|
raise Exception(f"Unsupported platform: {system} {machine}")
|
||||||
|
|
||||||
|
|
||||||
|
def download_and_unpack():
|
||||||
|
dest_dir = Path("./apple-codesign")
|
||||||
|
|
||||||
|
repo_url = "https://api.github.com/repos/indygreg/apple-platform-rs/releases/latest"
|
||||||
|
dest_dir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
|
# Fetch the latest release info from GitHub
|
||||||
|
print("Fetching latest release information...")
|
||||||
|
response = requests.get(repo_url)
|
||||||
|
response.raise_for_status()
|
||||||
|
release_data = response.json()
|
||||||
|
|
||||||
|
# Ensure release data has assets
|
||||||
|
if "assets" not in release_data:
|
||||||
|
raise Exception("Release data does not contain assets.")
|
||||||
|
|
||||||
|
# Determine the correct asset
|
||||||
|
platform_filename = get_platform_specific_filename()
|
||||||
|
asset = next((asset for asset in release_data["assets"] if asset["name"].startswith("apple-codesign-") and asset["name"].endswith(platform_filename.split("*")[-1])), None)
|
||||||
|
|
||||||
|
if not asset:
|
||||||
|
raise Exception(f"No matching asset found for platform: {platform_filename}")
|
||||||
|
|
||||||
|
# Download the archive
|
||||||
|
print(f"Downloading {asset['name']}...")
|
||||||
|
download_url = asset["browser_download_url"]
|
||||||
|
archive_path = dest_dir / asset["name"]
|
||||||
|
|
||||||
|
with requests.get(download_url, stream=True) as r:
|
||||||
|
r.raise_for_status()
|
||||||
|
with open(archive_path, "wb") as f:
|
||||||
|
for chunk in r.iter_content(chunk_size=8192):
|
||||||
|
f.write(chunk)
|
||||||
|
|
||||||
|
print(f"Downloaded to {archive_path}")
|
||||||
|
|
||||||
|
# Extract the archive
|
||||||
|
print("Extracting archive...")
|
||||||
|
temp_extract_dir = dest_dir / "temp_extract"
|
||||||
|
temp_extract_dir.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
if archive_path.suffix == ".zip":
|
||||||
|
with zipfile.ZipFile(archive_path, "r") as zip_ref:
|
||||||
|
zip_ref.extractall(temp_extract_dir)
|
||||||
|
elif archive_path.suffixes[-2:] == [".tar", ".gz"]:
|
||||||
|
with tarfile.open(archive_path, "r:gz") as tar_ref:
|
||||||
|
tar_ref.extractall(temp_extract_dir)
|
||||||
|
else:
|
||||||
|
raise Exception(f"Unknown archive format: {archive_path}")
|
||||||
|
|
||||||
|
# Move contents of the root directory inside the archive to dest_dir
|
||||||
|
root_dir = next(temp_extract_dir.iterdir()) # Assuming only one root directory
|
||||||
|
for item in root_dir.iterdir():
|
||||||
|
target_path = dest_dir / item.name
|
||||||
|
if target_path.exists():
|
||||||
|
if target_path.is_dir():
|
||||||
|
os.rmdir(target_path)
|
||||||
|
else:
|
||||||
|
os.remove(target_path)
|
||||||
|
item.rename(target_path)
|
||||||
|
|
||||||
|
# Clean up temporary directories
|
||||||
|
for item in temp_extract_dir.iterdir():
|
||||||
|
if item.is_dir():
|
||||||
|
os.rmdir(item)
|
||||||
|
temp_extract_dir.rmdir()
|
||||||
|
|
||||||
|
print(f"Extracted to {dest_dir}")
|
||||||
|
|
||||||
|
# Clean up the archive
|
||||||
|
os.remove(archive_path)
|
||||||
|
print(f"Removed archive {archive_path}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
download_and_unpack()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Error: {e}")
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import sys
|
||||||
|
import lief
|
||||||
|
import subprocess
|
||||||
|
import download_codesign
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
# Parse the input binary & xit if binary is invalid
|
||||||
|
output_path = sys.argv[1]
|
||||||
|
binary = lief.parse(sys.stdin.buffer.read())
|
||||||
|
if binary is None:
|
||||||
|
exit(1)
|
||||||
|
|
||||||
|
# Remove signature from Mac binaries
|
||||||
|
if isinstance(binary, lief.MachO.Binary):
|
||||||
|
binary.remove_signature()
|
||||||
|
|
||||||
|
# Write the modified binary to the output path
|
||||||
|
binary.write(output_path)
|
||||||
|
|
||||||
|
# Sign Mac binaries (required to make them usable because apple)
|
||||||
|
if isinstance(binary, lief.MachO.Binary):
|
||||||
|
print(f"Signing {output_path}...")
|
||||||
|
|
||||||
|
# Check if the Apple code-signing files are available, if not, download them
|
||||||
|
if not Path("./apple-codesign/COPYING").exists():
|
||||||
|
download_codesign.download_and_unpack()
|
||||||
|
|
||||||
|
# Run the code-signing process
|
||||||
|
sign_process = subprocess.Popen(["./apple-codesign/rcodesign", "sign", output_path], shell=False,
|
||||||
|
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
sign_process.wait()
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
$ErrorActionPreference = "Stop"
|
||||||
|
|
||||||
|
python -m venv .venv
|
||||||
|
.\.venv\Scripts\activate
|
||||||
|
pip install -r requirements.txt
|
||||||
Executable
+7
@@ -0,0 +1,7 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
python -m venv .venv
|
||||||
|
. ./.venv/bin/activate
|
||||||
|
pip install -r requirements.txt
|
||||||
Binary file not shown.
@@ -12,7 +12,7 @@ netty_version=4.1.97.Final
|
|||||||
|
|
||||||
# Fabric loader
|
# Fabric loader
|
||||||
fabric_loader_version=0.16.9
|
fabric_loader_version=0.16.9
|
||||||
fabric_api_version=0.107.0+1.21.1
|
fabric_api_version=0.115.0+1.21.1
|
||||||
# Fabric mod versions
|
# Fabric mod versions
|
||||||
modmenu_version=11.0.0-beta.1
|
modmenu_version=11.0.0-beta.1
|
||||||
starlight_version_fabric=
|
starlight_version_fabric=
|
||||||
|
|||||||
Reference in New Issue
Block a user