Compare commits

..

1 Commits

Author SHA1 Message Date
s809 a442a1f3ca Test build 2025-01-19 21:23:01 +05:00
49 changed files with 496 additions and 1527 deletions
-5
View File
@@ -26,11 +26,6 @@ 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
+25 -15
View File
@@ -17,9 +17,6 @@ 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
@@ -38,21 +35,34 @@ build:
stage: build stage: build
parallel: parallel:
matrix: matrix:
- MC_VER: ["1.16.5", "1.17.1", "1.18.2", "1.19.2", "1.19.4", "1.20.1", "1.20.2", "1.20.4", "1.20.6", "1.21.1", "1.21.3", "1.21.4", "1.21.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: script:
# this both runs the unit tests and assembles the code # 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 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 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/; - ./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/* ./Merged/* . || true
artifacts: artifacts:
name: "NightlyBuild_${MC_VER}-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}" name: "NightlyBuild_${MC_VER}-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths: paths:
- ./*.jar - Merged/*.jar
- quilt/build/libs/*.jar
- fabric/build/libs/*.jar
- forge/build/libs/*.jar
- neoforge/build/libs/*.jar
exclude: exclude:
- ./*-all.jar # TODO: There is a lot of duplicate stuff here, try to maybe make it smaller
- ./*-dev.jar - fabric/build/libs/*-all.jar
- ./*-sources.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 expire_in: 14 days
when: always when: always
extends: .build_java extends: .build_java
@@ -67,15 +77,15 @@ api:
# this also runs unit tests # this also runs unit tests
- ./gradlew api:build --gradle-user-home cache/; - ./gradlew api:build --gradle-user-home cache/;
- ./gradlew api:addSourcesToCompiledJar --gradle-user-home cache/; - ./gradlew api:addSourcesToCompiledJar --gradle-user-home cache/;
- cp ./coreSubProjects/api/build/libs/merged/* .
artifacts: artifacts:
name: "NightlyBuild_Api-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}" name: "Api_NightlyBuild-${CI_COMMIT_SHORT_SHA}-${CI_COMMIT_TIMESTAMP}"
paths: 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: exclude:
- ./*-all.jar - coreSubProjects/api/build/libs/merged/*-all.jar
- ./*-dev.jar - coreSubProjects/api/build/libs/merged/*-sources.jar
- ./*-sources.jar
expire_in: 1 day expire_in: 1 day
when: always when: always
extends: .build_java 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: 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: 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: 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. \ 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 -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. **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**: 2. **Describe the improvement**:
+22 -104
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 { plugins {
id "java" id "java"
@@ -19,7 +11,7 @@ plugins {
id "systems.manifold.manifold-gradle-plugin" version "0.0.2-alpha" id "systems.manifold.manifold-gradle-plugin" version "0.0.2-alpha"
// Architectury is used here only as a replacement for forge's own loom // Architectury is used here only as a replacement for forge's own loom
id "dev.architectury.loom" version "1.10-SNAPSHOT" apply false id "dev.architectury.loom" version "1.7-SNAPSHOT" apply false
} }
@@ -78,7 +70,7 @@ forgix {
String loaderHyphenSeparatedList = ((String)gradle.builds_for).replaceAll(",", "-"); String loaderHyphenSeparatedList = ((String)gradle.builds_for).replaceAll(",", "-");
group = "com.seibel.distanthorizons" group = "com.seibel.distanthorizons"
mergedJarName = "DistantHorizons-${loaderHyphenSeparatedList}-${rootProject.versionStr}.jar" mergedJarName = "DistantHorizons-${rootProject.versionStr}-${loaderHyphenSeparatedList}.jar"
if (findProject(":forge")) if (findProject(":forge"))
forge { forge {
@@ -104,83 +96,6 @@ 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
@@ -391,23 +306,26 @@ subprojects { p ->
// Logging // Logging
relocate "org.slf4j", "${librariesLocation}.slf4j" relocate "org.slf4j", "${librariesLocation}.slf4j"
// Sqlite Database // // Sqlite Database
// librariesLocation isn't used because it's too long for replacing paths in native libraries // // James can't determine how to relocate the library correctly so this is commented out
// Allowing strings larger than the original string would require shifting the entire binary's contents // relocate ("org.sqlite", "${librariesLocation}.sqlite") {
transform(NativeTransformer) { // exclude("org/sqlite/core/NativeDB/**")
rootDir = project.rootDir //
// exclude("org/sqlite/native/FreeBSD/**")
before { // exclude("org/sqlite/native/Linux-Android/**")
relocate "org.sqlite", "dh_sqlite", { // exclude("org/sqlite/native/Linux-Musl/**")
exclude "org/sqlite/native/**" // exclude("org/sqlite/native/Linux/arm/**")
} // exclude("org/sqlite/native/Linux/aarch64/**")
relocate "jdbc:sqlite", "jdbc:dh_sqlite" // exclude("org/sqlite/native/Linux/armv6/**")
} // exclude("org/sqlite/native/Linux/x86/**")
// exclude("org/sqlite/native/Linux/armv7/**")
relocateNative "org/sqlite", "dh_sqlite" // exclude("org/sqlite/native/Linux/ppc64/**")
relocateNative "org_sqlite", "dh_1sqlite" // 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 // JOML
+1 -1
View File
@@ -14,7 +14,7 @@ for %%f in (versionProperties\*) do (
@rem Clean out the folders, build it, and merge it @rem Clean out the folders, build it, and merge it
echo ==================== Cleaning workspace to build !version! ==================== echo ==================== Cleaning workspace to build !version! ====================
call .\gradlew.bat clean call .\gradlew.bat clean -PmcVer="!version!"
echo ==================== Building !version! ==================== echo ==================== Building !version! ====================
call .\gradlew.bat build -PmcVer="!version!" call .\gradlew.bat build -PmcVer="!version!"
echo ==================== Merging !version! ==================== echo ==================== Merging !version! ====================
-207
View File
@@ -1,207 +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.
*
* @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", "-ExecutionPolicy", "Bypass", "./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);
}
}
@@ -20,6 +20,8 @@
package com.seibel.distanthorizons.common.wrappers; package com.seibel.distanthorizons.common.wrappers;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants; import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import net.minecraft.SharedConstants;
import net.minecraft.client.Minecraft;
/** /**
* @author James Seibel * @author James Seibel
@@ -36,48 +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 @Override
public String getMinecraftVersion() public String getMinecraftVersion()
{ {
// these values are hard-coded to prevent an issue with Forge (specifically 1.18.2) where #if MC_VER < MC_1_19_2
// it can't load client classes when running as a dedicated server, return Minecraft.getInstance().getGame().getVersion().getId();
// 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";
#else #else
ERROR MC version constant missing return SharedConstants.getCurrentVersion().getId();
#endif #endif
} }
} }
@@ -43,8 +43,6 @@ 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;
@@ -92,19 +90,20 @@ public class BlockStateWrapper implements IBlockStateWrapper
// 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;
private final int hashCode; 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 */ /** used by the Iris shader mod to determine how each LOD should be rendered */
private byte blockMaterialId = 0; private byte blockMaterialId = 0;
private final boolean isBeaconBlock; private final boolean isBeaconBlock;
private final boolean isBeaconBaseBlock; private final boolean isBeaconBaseBlock;
private final boolean allowsBeaconBeamPassage;
/** null if this block can't tint beacons */ /** null if this block can't tint beacons */
private final Color beaconTintColor; private final Color beaconTintColor;
private final Color mapColor; private final Color mapColor;
@@ -161,12 +160,9 @@ public class BlockStateWrapper implements IBlockStateWrapper
this.serialString = this.serialize(levelWrapper); this.serialString = this.serialize(levelWrapper);
this.hashCode = Objects.hash(this.serialString); this.hashCode = Objects.hash(this.serialString);
this.blockMaterialId = this.calculateEDhApiBlockMaterialId().index; this.blockMaterialId = this.calculateEDhApiBlockMaterialId().index;
this.opacity = this.calculateOpacity();
String lowercaseSerial = this.serialString.toLowerCase();
// beacon blocks // beacon blocks
String lowercaseSerial = this.serialString.toLowerCase();
boolean isBeaconBaseBlock = false; boolean isBeaconBaseBlock = false;
for (int i = 0; i < LodUtil.BEACON_BASE_BLOCK_NAME_LIST.size(); i++) for (int i = 0; i < LodUtil.BEACON_BASE_BLOCK_NAME_LIST.size(); i++)
{ {
@@ -202,39 +198,6 @@ public class BlockStateWrapper implements IBlockStateWrapper
this.beaconTintColor = beaconTintColor; 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; int mcColor = 0;
if (this.blockState != null) if (this.blockState != null)
{ {
@@ -378,13 +341,13 @@ public class BlockStateWrapper implements IBlockStateWrapper
//=================// //=================//
@Override @Override
public int getOpacity() { return this.opacity; } public int getOpacity()
private int calculateOpacity()
{ {
// get block properties (defaults to the values used by air) // use the cached opacity value if possible
boolean canOcclude = this.getCanOcclude(); if (this.opacity != -1)
boolean propagatesSkyLightDown = this.getPropagatesSkyLightDown(); {
return this.opacity;
}
// 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
@@ -393,20 +356,19 @@ public class BlockStateWrapper implements IBlockStateWrapper
{ {
opacity = LodUtil.BLOCK_FULLY_TRANSPARENT; 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) // 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;
} }
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; opacity = LodUtil.BLOCK_FULLY_TRANSPARENT;
} }
else else
@@ -416,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 @Override
public int getLightEmission() { return (this.blockState != null) ? this.blockState.getLightEmission() : 0; } public int getLightEmission() { return (this.blockState != null) ? this.blockState.getLightEmission() : 0; }
@@ -519,8 +453,6 @@ public class BlockStateWrapper implements IBlockStateWrapper
public boolean isBeaconBaseBlock() { return this.isBeaconBaseBlock; } public boolean isBeaconBaseBlock() { return this.isBeaconBaseBlock; }
@Override @Override
public boolean isBeaconTintBlock() { return this.beaconTintColor != null; } public boolean isBeaconTintBlock() { return this.beaconTintColor != null; }
@Override
public boolean allowsBeaconBeamPassage() { return this.allowsBeaconBeamPassage; }
@Override @Override
public Color getMapColor() { return this.mapColor; } public Color getMapColor() { return this.mapColor; }
@@ -40,18 +40,11 @@ import java.util.Random;
#endif #endif
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.concurrent.locks.ReentrantLock; 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 * This stores and calculates the colors
* the given {@link BlockState} should have based * the given {@link BlockState} should have based
@@ -203,7 +196,9 @@ public class ClientBlockStateColorCache
List<BakedQuad> quads = null; List<BakedQuad> quads = null;
for (Direction direction : COLOR_RESOLUTION_DIRECTION_ORDER) 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() if (quads != null && !quads.isEmpty()
&& !( && !(
this.blockState.getBlock() instanceof RotatedPillarBlock this.blockState.getBlock() instanceof RotatedPillarBlock
@@ -217,37 +212,19 @@ public class ClientBlockStateColorCache
if (quads == null || quads.isEmpty()) if (quads == null || quads.isEmpty())
{ {
quads = this.getUnculledQuads(); quads = Minecraft.getInstance().getModelManager().getBlockModelShaper().
getBlockModel(this.blockState).getQuads(this.blockState, null, RANDOM);
} }
if (quads != null if (quads != null && !quads.isEmpty())
&& !quads.isEmpty()
&& quads.get(0) != null)
{ {
BakedQuad firstQuad = quads.get(0); this.needPostTinting = quads.get(0).isTinted();
this.needShade = quads.get(0).isShade();
this.needPostTinting = firstQuad.isTinted(); this.tintIndex = quads.get(0).getTintIndex();
#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.baseColor = calculateColorFromTexture( this.baseColor = calculateColorFromTexture(
firstQuad.sprite, #if MC_VER < MC_1_17_1 quads.get(0).sprite,
ColorMode.getColorMode(this.blockState.getBlock())); #else quads.get(0).getSprite(), #endif
#elif MC_VER < MC_1_21_5 ColorMode.getColorMode(this.blockState.getBlock()));
this.baseColor = calculateColorFromTexture(
firstQuad.getSprite(),
ColorMode.getColorMode(this.blockState.getBlock()));
#else
this.baseColor = calculateColorFromTexture(
firstQuad.sprite(),
ColorMode.getColorMode(this.blockState.getBlock()));
#endif
} }
else else
{ {
@@ -255,7 +232,8 @@ public class ClientBlockStateColorCache
this.needPostTinting = false; this.needPostTinting = false;
this.needShade = false; this.needShade = false;
this.tintIndex = 0; this.tintIndex = 0;
this.baseColor = this.getParticleIconColor(); this.baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
ColorMode.getColorMode(this.blockState.getBlock()));
} }
} }
else else
@@ -264,7 +242,8 @@ public class ClientBlockStateColorCache
this.needPostTinting = true; this.needPostTinting = true;
this.needShade = false; this.needShade = false;
this.tintIndex = 0; this.tintIndex = 0;
this.baseColor = this.getParticleIconColor(); this.baseColor = calculateColorFromTexture(Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
ColorMode.getColorMode(this.blockState.getBlock()));
} }
this.isColorResolved = true; this.isColorResolved = true;
@@ -274,35 +253,6 @@ public class ClientBlockStateColorCache
RESOLVE_LOCK.unlock(); 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? //TODO: Perhaps make this not just use the first frame?
private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode) private static int calculateColorFromTexture(TextureAtlasSprite texture, ColorMode colorMode)
{ {
@@ -433,13 +383,6 @@ public class ClientBlockStateColorCache
return (bias + (scale * t)) >>> 16; return (bias + (scale * t)) >>> 16;
} }
private int getParticleIconColor()
{
return calculateColorFromTexture(
Minecraft.getInstance().getModelManager().getBlockModelShaper().getParticleIcon(this.blockState),
ColorMode.getColorMode(this.blockState.getBlock()));
}
//===============// //===============//
@@ -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;
} }
@@ -214,29 +214,17 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Override @Override
public DhBlockPos getPlayerBlockPos() public DhBlockPos getPlayerBlockPos()
{ {
LocalPlayer player = this.getPlayer(); BlockPos playerPos = this.getPlayer().blockPosition();
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(player.blockPosition()); ChunkPos playerPos = new ChunkPos(this.getPlayer().blockPosition());
#else #else
ChunkPos playerPos = player.chunkPosition(); ChunkPos playerPos = this.getPlayer().chunkPosition();
#endif #endif
return new DhChunkPos(playerPos.x, playerPos.z); return new DhChunkPos(playerPos.x, playerPos.z);
} }
@@ -19,11 +19,7 @@
package com.seibel.distanthorizons.common.wrappers.minecraft; package com.seibel.distanthorizons.common.wrappers.minecraft;
#if MC_VER < MC_1_21_5
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.GlStateManager;
#elif MC_VER == MC_1_21_5
import com.mojang.blaze3d.opengl.GlStateManager;
#endif
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
@@ -152,10 +148,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper
public void glBlendFunc(int sfactor, int dfactor) public void glBlendFunc(int sfactor, int dfactor)
{ {
GL32.glBlendFunc(sfactor, dfactor); GL32.glBlendFunc(sfactor, dfactor);
GlStateManager._blendFunc(sfactor, dfactor);
#if MC_VER < MC_1_21_5
GlStateManager._blendFunc(sfactor, dfactor);
#endif
} }
/** @see GL32#glBlendFuncSeparate */ /** @see GL32#glBlendFuncSeparate */
@Override @Override
@@ -187,15 +180,7 @@ public class MinecraftGLWrapper implements IMinecraftGLWrapper
/** @see GL32#glDeleteBuffers(int) */ /** @see GL32#glDeleteBuffers(int) */
@Override @Override
public void glDeleteBuffers(int buffer) public void glDeleteBuffers(int buffer)
{ { GlStateManager._glDeleteBuffers(buffer); }
GL32.glDeleteBuffers(buffer);
// MC's implementation has a bug where it will throw:
// GL_INVALID_OPERATION in glBufferData(immutable)
// when attempting to delete Storage Buffers
// So we need to manually delete the buffers ourselves
//GlStateManager._glDeleteBuffers(buffer);
}
// culling // // culling //
@@ -71,10 +71,6 @@ import net.minecraft.world.phys.Vec3;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.joml.Vector4f; import org.joml.Vector4f;
#if MC_VER >= MC_1_21_5
import com.mojang.blaze3d.opengl.GlTexture;
import org.lwjgl.opengl.GL32;
#endif
/** /**
* A singleton that contains everything * A singleton that contains everything
@@ -106,15 +102,8 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
*/ */
public int finalLevelFrameBufferId = -1; public int finalLevelFrameBufferId = -1;
public boolean colorTextureCastFailLogged = false;
public boolean depthTextureCastFailLogged = false;
//=========//
// methods //
//=========//
@Override @Override
public Vec3f getLookAtVector() public Vec3f getLookAtVector()
{ {
@@ -261,26 +250,6 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
private RenderTarget getRenderTarget() { return MC.getMainRenderTarget(); } private 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
}
@Override @Override
public int getTargetFrameBuffer() public int getTargetFrameBuffer()
{ {
@@ -290,87 +259,27 @@ public class MinecraftRenderWrapper implements IMinecraftRenderWrapper
return this.finalLevelFrameBufferId; return this.finalLevelFrameBufferId;
} }
#if MC_VER < MC_1_21_5
return this.getRenderTarget().frameBufferId; 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 @Override
public void clearTargetFrameBuffer() { this.finalLevelFrameBufferId = -1; } public void clearTargetFrameBuffer() { this.finalLevelFrameBufferId = -1; }
@Override @Override
public int getDepthTextureId() public int getDepthTextureId() { return this.getRenderTarget().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 (ClassCastException 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 @Override
public int getColorTextureId() public int getColorTextureId() { return this.getRenderTarget().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 (ClassCastException 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
}
@Override @Override
public int getTargetFrameBufferViewportWidth() public int getTargetFrameBufferViewportWidth()
{ {
return this.getRenderTarget().viewWidth; return getRenderTarget().viewWidth;
} }
@Override @Override
public int getTargetFrameBufferViewportHeight() public int getTargetFrameBufferViewportHeight()
{ {
return this.getRenderTarget().viewHeight; return getRenderTarget().viewHeight;
} }
@Override @Override
@@ -21,10 +21,8 @@ package com.seibel.distanthorizons.common.wrappers.misc;
import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.platform.NativeImage;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; 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.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
import org.apache.logging.log4j.Logger;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -32,7 +30,6 @@ import java.nio.ByteBuffer;
public class LightMapWrapper implements ILightMapWrapper public class LightMapWrapper implements ILightMapWrapper
{ {
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class); private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
private static final Logger LOGGER = DhLoggerBuilder.getLogger();
private int textureId = 0; private int textureId = 0;
@@ -52,7 +49,6 @@ public class LightMapWrapper implements ILightMapWrapper
public void uploadLightmap(NativeImage image) public void uploadLightmap(NativeImage image)
{ {
#if MC_VER < MC_1_21_5
int currentTexture = GLMC.getActiveTexture(); int currentTexture = GLMC.getActiveTexture();
if (this.textureId == 0) if (this.textureId == 0)
{ {
@@ -63,27 +59,14 @@ public class LightMapWrapper implements ILightMapWrapper
GLMC.glBindTexture(this.textureId); GLMC.glBindTexture(this.textureId);
} }
image.upload(0, 0, 0, false); image.upload(0, 0, 0, false);
GLMC.glBindTexture(currentTexture);
// 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
} }
private void createLightmap(NativeImage image) private void createLightmap(NativeImage image)
{ {
#if MC_VER < MC_1_21_5
this.textureId = GLMC.glGenTextures(); this.textureId = GLMC.glGenTextures();
GLMC.glBindTexture(this.textureId); GLMC.glBindTexture(this.textureId);
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, image.format().glFormat(), image.getWidth(), image.getHeight(), GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, image.format().glFormat(), image.getWidth(), image.getHeight(),
0, image.format().glFormat(), GL32.GL_UNSIGNED_BYTE, (ByteBuffer) null); 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) public void setLightmapId(int minecraftLightmapTetxureId)
@@ -25,6 +25,7 @@ import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.chunk.ChunkAccess; import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.chunk.ChunkSource; import net.minecraft.world.level.chunk.ChunkSource;
import net.minecraft.world.phys.Vec3;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
@@ -32,10 +33,6 @@ import org.jetbrains.annotations.Nullable;
import java.awt.*; import java.awt.*;
import java.io.File; import java.io.File;
import java.io.IOException; 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.concurrent.ConcurrentHashMap;
#if MC_VER <= MC_1_20_4 #if MC_VER <= MC_1_20_4
@@ -45,7 +42,6 @@ import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif #endif
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
import net.minecraft.world.phys.Vec3;
#else #else
import com.seibel.distanthorizons.core.util.ColorUtil; import com.seibel.distanthorizons.core.util.ColorUtil;
#endif #endif
@@ -53,12 +49,7 @@ import com.seibel.distanthorizons.core.util.ColorUtil;
public class ClientLevelWrapper implements IClientLevelWrapper public class ClientLevelWrapper implements IClientLevelWrapper
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(ClientLevelWrapper.class.getSimpleName()); private static final Logger LOGGER = DhLoggerBuilder.getLogger(ClientLevelWrapper.class.getSimpleName());
/** private static final ConcurrentHashMap<ClientLevel, ClientLevelWrapper> LEVEL_WRAPPER_BY_CLIENT_LEVEL = new ConcurrentHashMap<>(); // TODO can leak
* 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 IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class); private static final IKeyedClientLevelManager KEYED_CLIENT_LEVEL_MANAGER = SingletonInjector.INSTANCE.get(IKeyedClientLevelManager.class);
private static final Minecraft MINECRAFT = Minecraft.getInstance(); private static final Minecraft MINECRAFT = Minecraft.getInstance();
@@ -81,9 +72,9 @@ public class ClientLevelWrapper implements IClientLevelWrapper
//==================// //===============//
// instance methods // // wrapper logic //
//==================// //===============//
public static IClientLevelWrapper getWrapper(@NotNull ClientLevel level) { return getWrapper(level, false); } public static IClientLevelWrapper getWrapper(@NotNull ClientLevel level) { return getWrapper(level, false); }
@@ -105,19 +96,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
} }
} }
return LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.compute(level, (newLevel, levelRef) -> return LEVEL_WRAPPER_BY_CLIENT_LEVEL.computeIfAbsent(level, ClientLevelWrapper::new);
{
if (levelRef != null)
{
ClientLevelWrapper oldLevelWrapper = levelRef.get();
if (oldLevelWrapper != null)
{
return levelRef;
}
}
return new WeakReference<>(new ClientLevelWrapper(newLevel));
}).get();
} }
@Nullable @Nullable
@@ -286,7 +265,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
@Override @Override
public void onUnload() public void onUnload()
{ {
LEVEL_WRAPPER_REF_BY_CLIENT_LEVEL.remove(this.level); LEVEL_WRAPPER_BY_CLIENT_LEVEL.remove(this.level);
this.parentDhLevel = null; this.parentDhLevel = null;
} }
@@ -28,80 +28,72 @@ 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<String, DimensionTypeWrapper> DIMENSION_WRAPPER_BY_NAME = new ConcurrentHashMap<>(); private static final ConcurrentMap<DimensionType, DimensionTypeWrapper> dimensionTypeWrapperMap = 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)
{ {
String dimName = getName(dimensionType); //first we check if the biome has already been wrapped
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 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); DimensionTypeWrapper dimensionTypeWrapper = new DimensionTypeWrapper(dimensionType);
DIMENSION_WRAPPER_BY_NAME.put(dimName, dimensionTypeWrapper); dimensionTypeWrapperMap.put(dimensionType, dimensionTypeWrapper);
//we return the newly created wrapper
return dimensionTypeWrapper; return dimensionTypeWrapper;
} }
public static void clearMap() { DIMENSION_WRAPPER_BY_NAME.clear(); } public static void clearMap()
{
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 dimensionType.effectsLocation().getPath(); return this.dimensionType.effectsLocation().getPath();
#endif #endif
} }
@Override @Override
public boolean hasCeiling() { return this.dimensionType.hasCeiling(); } public boolean hasCeiling()
{
return this.dimensionType.hasCeiling();
}
@Override @Override
public boolean hasSkyLight() { return this.dimensionType.hasSkyLight(); } public boolean hasSkyLight()
{
return this.dimensionType.hasSkyLight();
}
@Override @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 // there's definitely a better way of doing this, but it should work well enough for now
@Override @Override
public boolean isTheEnd() { return this.getName().equalsIgnoreCase("the_end"); } public boolean isTheEnd() { return this.getDimensionName().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)
@@ -113,10 +105,9 @@ public class DimensionTypeWrapper implements IDimensionTypeWrapper
else else
{ {
DimensionTypeWrapper other = (DimensionTypeWrapper) obj; 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; package com.seibel.distanthorizons.common.wrappers.world;
import java.io.File; 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 java.util.concurrent.ConcurrentHashMap;
import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType; import com.seibel.distanthorizons.api.enums.worldGeneration.EDhApiLevelType;
@@ -61,11 +57,7 @@ import org.apache.logging.log4j.Logger;
public class ServerLevelWrapper implements IServerLevelWrapper public class ServerLevelWrapper implements IServerLevelWrapper
{ {
private static final Logger LOGGER = DhLoggerBuilder.getLogger(); private static final Logger LOGGER = DhLoggerBuilder.getLogger();
/** private static final ConcurrentHashMap<ServerLevel, ServerLevelWrapper> LEVEL_WRAPPER_BY_SERVER_LEVEL = new ConcurrentHashMap<>();
* 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 final ServerLevel level; private final ServerLevel level;
@Deprecated // TODO circular references are bad @Deprecated // TODO circular references are bad
@@ -78,29 +70,15 @@ public class ServerLevelWrapper implements IServerLevelWrapper
//==============// //==============//
public static ServerLevelWrapper getWrapper(ServerLevel level) public static ServerLevelWrapper getWrapper(ServerLevel level)
{ { return LEVEL_WRAPPER_BY_SERVER_LEVEL.computeIfAbsent(level, ServerLevelWrapper::new); }
return LEVEL_WRAPPER_REF_BY_SERVER_LEVEL.compute(level, (newLevel, levelRef) ->
{
if (levelRef != null)
{
ServerLevelWrapper oldLevelWrapper = levelRef.get();
if (oldLevelWrapper != null)
{
return levelRef;
}
}
return new WeakReference<>(new ServerLevelWrapper(newLevel));
}).get();
}
public ServerLevelWrapper(ServerLevel level) { this.level = level; } public ServerLevelWrapper(ServerLevel level) { this.level = level; }
//==================// //=========//
// instance methods // // methods //
//==================// //=========//
@Override @Override
public File getMcSaveFolder() public File getMcSaveFolder()
@@ -201,7 +179,7 @@ public class ServerLevelWrapper implements IServerLevelWrapper
public ServerLevel getWrappedMcObject() { return this.level; } public ServerLevel getWrappedMcObject() { return this.level; }
@Override @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 @Override
@@ -113,11 +113,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
new ConfigBasedLogger(LogManager.getLogger("LodWorldGen"), new ConfigBasedLogger(LogManager.getLogger("LodWorldGen"),
() -> Config.Common.Logging.logWorldGenLoadEvent.get()); () -> 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)); private static final TicketType<ChunkPos> DH_SERVER_GEN_TICKET = TicketType.create("dh_server_gen_ticket", Comparator.comparingLong(ChunkPos::toLong));
#else
private static final TicketType DH_SERVER_GEN_TICKET = new TicketType(/* timeout, 0 = disabled*/0L, /* persist */ false, TicketType.TicketUse.LOADING);
#endif
private static final IModChecker MOD_CHECKER = SingletonInjector.INSTANCE.get(IModChecker.class); 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 * 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 pullExistingChunkUsingMcAsyncMethod = false; private boolean pullExistingChunkAsync = false;
@@ -245,10 +241,19 @@ 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 methods handled by C2ME."); EVENT_LOGGER.info("C2ME detected: DH's pre-existing chunk accessing will use async methods handled by C2ME.");
this.pullExistingChunkUsingMcAsyncMethod = true; 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); this.params = new GlobalParameters(serverlevel);
} }
@@ -337,8 +342,6 @@ 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
{ {
@@ -382,12 +385,10 @@ 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.runAsync(() -> return CompletableFuture.allOf(readFutures)
.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
@@ -591,7 +592,6 @@ 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;
@@ -610,7 +610,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.pullExistingChunkUsingMcAsyncMethod && ioWorker.storage != null) if (!this.pullExistingChunkAsync && ioWorker.storage != null)
{ {
try try
{ {
@@ -624,7 +624,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.pullExistingChunkUsingMcAsyncMethod = true; this.pullExistingChunkAsync = true;
// try again now using the async method // try again now using the async method
return this.getChunkNbtDataAsync(chunkPos); return this.getChunkNbtDataAsync(chunkPos);
@@ -633,42 +633,32 @@ 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.pullExistingChunkUsingMcAsyncMethod) if (!this.pullExistingChunkAsync)
{ {
// 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.pullExistingChunkUsingMcAsyncMethod = true; this.pullExistingChunkAsync = 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 -> .thenApply(optional -> optional.orElse(null))
.exceptionally((throwable) ->
{
// unwrap the CompletionException if necessary
Throwable actualThrowable = throwable;
while (actualThrowable instanceof CompletionException completionException)
{ {
// Debugging note: actualThrowable = completionException.getCause();
// 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);
//GET_CHUNK_COUNT_REF.decrementAndGet(); return null;
//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
} }
@@ -882,11 +872,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
chunkLevel = generateUpToFeatures ? ChunkLevel.byStatus(ChunkStatus.FEATURES) : 33; chunkLevel = generateUpToFeatures ? ChunkLevel.byStatus(ChunkStatus.FEATURES) : 33;
#endif #endif
#if MC_VER < MC_1_21_5
level.getChunkSource().distanceManager.addTicket(DH_SERVER_GEN_TICKET, pos, chunkLevel, pos); 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 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()); ChunkHolder holder = level.getChunkSource().chunkMap.getUpdatingChunkIfPresent(pos.toLong());
if (holder == null) if (holder == null)
@@ -923,11 +909,7 @@ public final class BatchGenerationEnvironment extends AbstractBatchGenerationEnv
chunkLevel = chunkWasGeneratedUpToFeatures ? ChunkLevel.byStatus(ChunkStatus.FEATURES) : 33; chunkLevel = chunkWasGeneratedUpToFeatures ? ChunkLevel.byStatus(ChunkStatus.FEATURES) : 33;
#endif #endif
#if MC_VER < MC_1_21_5
level.getChunkSource().distanceManager.removeTicket(DH_SERVER_GEN_TICKET, pos, chunkLevel, pos); 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 // mitigate OOM issues in vanilla chunk system: see https://github.com/pop4959/Chunky/pull/383
level.getChunkSource().chunkMap.tick(() -> false); level.getChunkSource().chunkMap.tick(() -> false);
@@ -29,9 +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.Optional;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicBoolean;
import it.unimi.dsi.fastutil.shorts.ShortList; import it.unimi.dsi.fastutil.shorts.ShortList;
@@ -86,13 +84,11 @@ import net.minecraft.world.level.chunk.status.ChunkType;
#endif #endif
import net.minecraft.world.level.material.Fluid; import net.minecraft.world.level.material.Fluid;
import org.jetbrains.annotations.Nullable;
public class ChunkLoader 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_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()); private static final Codec<PalettedContainer<BlockState>> BLOCK_STATE_CODEC = PalettedContainer.codecRW(Block.BLOCK_STATE_REGISTRY, BlockState.CODEC, PalettedContainer.Strategy.SECTION_STATES, Blocks.AIR.defaultBlockState());
@@ -124,16 +120,19 @@ public class ChunkLoader
CompoundTag tagLevel = chunkData; CompoundTag tagLevel = chunkData;
#endif #endif
int chunkX = tagGetInt(tagLevel,"xPos"); ChunkPos actualPos = new ChunkPos(tagLevel.getInt("xPos"), tagLevel.getInt("zPos"));
int chunkZ = tagGetInt(tagLevel, "zPos");
ChunkPos actualPos = new ChunkPos(chunkX, chunkZ);
if (!Objects.equals(chunkPos, actualPos)) 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 // 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" + 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" + "This might happen if the world was created using an external program. \n" +
@@ -175,27 +174,14 @@ public class ChunkLoader
#endif #endif
long inhabitedTime = tagGetLong(tagLevel, "InhabitedTime"); long inhabitedTime = tagLevel.getLong("InhabitedTime");
//================== Read params for making the LevelChunk ================== //================== Read params for making the LevelChunk ==================
UpgradeData upgradeData = tagLevel.contains(TAG_UPGRADE_DATA, 10)
UpgradeData upgradeData; ? new UpgradeData(tagLevel.getCompound(TAG_UPGRADE_DATA)#if MC_VER >= MC_1_17_1 , level #endif )
#if MC_VER < MC_1_17_1
upgradeData = tagLevel.contains(TAG_UPGRADE_DATA, 10)
? new UpgradeData(tagGetCompoundTag(tagLevel, TAG_UPGRADE_DATA))
: UpgradeData.EMPTY; : 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 = tagLevel.getBoolean("isLightOn");
boolean isLightOn = tagGetBoolean(tagLevel, "isLightOn");
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
ChunkBiomeContainer chunkBiomeContainer = new ChunkBiomeContainer( ChunkBiomeContainer chunkBiomeContainer = new ChunkBiomeContainer(
level.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY)#if MC_VER >= MC_1_17_1 , level #endif , level.getLevel().registryAccess().registryOrThrow(Registry.BIOME_REGISTRY)#if MC_VER >= MC_1_17_1 , level #endif ,
@@ -212,21 +198,17 @@ public class ChunkLoader
: new ProtoTickList<Fluid>(fluid -> (fluid == null || fluid == Fluids.EMPTY), chunkPos, : new ProtoTickList<Fluid>(fluid -> (fluid == null || fluid == Fluids.EMPTY), chunkPos,
tagLevel.getList("LiquidsToBeTicked", 9)#if MC_VER >= MC_1_17_1 , level #endif ); tagLevel.getList("LiquidsToBeTicked", 9)#if MC_VER >= MC_1_17_1 , level #endif );
#else #else
#if MC_VER < MC_1_19_4 #if MC_VER < MC_1_19_4
LevelChunkTicks<Block> blockTicks = LevelChunkTicks.load(tagLevel.getList(BLOCK_TICKS_TAG_18, 10), LevelChunkTicks<Block> blockTicks = LevelChunkTicks.load(tagLevel.getList(BLOCK_TICKS_TAG_18, 10),
string -> Registry.BLOCK.getOptional(ResourceLocation.tryParse(string)), chunkPos); string -> Registry.BLOCK.getOptional(ResourceLocation.tryParse(string)), chunkPos);
LevelChunkTicks<Fluid> fluidTicks = LevelChunkTicks.load(tagLevel.getList(FLUID_TICKS_TAG_18, 10), LevelChunkTicks<Fluid> fluidTicks = LevelChunkTicks.load(tagLevel.getList(FLUID_TICKS_TAG_18, 10),
string -> Registry.FLUID.getOptional(ResourceLocation.tryParse(string)), chunkPos); string -> Registry.FLUID.getOptional(ResourceLocation.tryParse(string)), chunkPos);
#elif MC_VER < MC_1_21_4 #else
LevelChunkTicks<Block> blockTicks = LevelChunkTicks.load(tagLevel.getList(BLOCK_TICKS_TAG_18, 10), LevelChunkTicks<Block> blockTicks = LevelChunkTicks.load(tagLevel.getList(BLOCK_TICKS_TAG_18, 10),
(string -> BuiltInRegistries.BLOCK.getOptional(ResourceLocation.tryParse(string))), chunkPos); (string -> BuiltInRegistries.BLOCK.getOptional(ResourceLocation.tryParse(string))), chunkPos);
LevelChunkTicks<Fluid> fluidTicks = LevelChunkTicks.load(tagLevel.getList(FLUID_TICKS_TAG_18, 10), LevelChunkTicks<Fluid> fluidTicks = LevelChunkTicks.load(tagLevel.getList(FLUID_TICKS_TAG_18, 10),
string -> BuiltInRegistries.FLUID.getOptional(ResourceLocation.tryParse(string)), chunkPos); string -> BuiltInRegistries.FLUID.getOptional(ResourceLocation.tryParse(string)), chunkPos);
#else #endif
// do we need the ticks for what we're doing?
LevelChunkTicks<Block> blockTicks = new LevelChunkTicks<>();
LevelChunkTicks<Fluid> fluidTicks = new LevelChunkTicks<>();
#endif
#endif #endif
LevelChunkSection[] levelChunkSections = readSections(level, chunkPos, tagLevel); LevelChunkSection[] levelChunkSections = readSections(level, chunkPos, tagLevel);
@@ -236,6 +218,7 @@ public class ChunkLoader
LevelChunk chunk = new LevelChunk((Level) level.getLevel(), chunkPos, chunkBiomeContainer, upgradeData, blockTicks, LevelChunk chunk = new LevelChunk((Level) level.getLevel(), chunkPos, chunkBiomeContainer, upgradeData, blockTicks,
fluidTicks, inhabitedTime, levelChunkSections, null); fluidTicks, inhabitedTime, levelChunkSections, null);
#else #else
LevelChunk chunk = new LevelChunk((Level) level, chunkPos, upgradeData, blockTicks, LevelChunk chunk = new LevelChunk((Level) level, chunkPos, upgradeData, blockTicks,
fluidTicks, inhabitedTime, levelChunkSections, null, blendingData); fluidTicks, inhabitedTime, levelChunkSections, null, blendingData);
#endif #endif
@@ -269,23 +252,18 @@ public class ChunkLoader
biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS)); biomes.asHolderIdMap(), biomes.holderByNameCodec(), PalettedContainer.Strategy.SECTION_BIOMES, biomes.getOrThrow(Biomes.PLAINS));
#endif #endif
#endif #endif
int sectionYIndex = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif int sectionYIndex = #if MC_VER < MC_1_17_1 16; #else level.getSectionsCount(); #endif
LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYIndex]; LevelChunkSection[] chunkSections = new LevelChunkSection[sectionYIndex];
ListTag tagSections = tagGetListTag(chunkData, "Sections", 10); boolean isLightOn = chunkData.getBoolean("isLightOn");
if (tagSections == null || tagSections.isEmpty()) boolean hasSkyLight = level.dimensionType().hasSkyLight();
{ ListTag tagSections = chunkData.getList("Sections", 10);
tagSections = tagGetListTag(chunkData, "sections", 10); if (tagSections.isEmpty()) tagSections = chunkData.getList("sections", 10);
}
for (int j = 0; j < tagSections.size(); ++j)
if (tagSections != null)
{ {
for (int j = 0; j < tagSections.size(); ++j) CompoundTag tagSection = tagSections.getCompound(j);
{ int sectionYPos = tagSection.getByte("Y");
CompoundTag tagSection = tagGetCompoundTag(tagSections, j);
int sectionYPos = tagGetByte(tagSection, "Y");
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
if (tagSection.contains("Palette", 9) && tagSection.contains("BlockStates", 12)) if (tagSection.contains("Palette", 9) && tagSection.contains("BlockStates", 12))
@@ -299,89 +277,65 @@ public class ChunkLoader
= levelChunkSection; = levelChunkSection;
} }
#else #else
int sectionId = level.getSectionIndexFromSectionY(sectionYPos); int sectionId = level.getSectionIndexFromSectionY(sectionYPos);
if (sectionId >= 0 && sectionId < chunkSections.length) if (sectionId >= 0 && sectionId < chunkSections.length)
{ {
PalettedContainer<BlockState> blockStateContainer; PalettedContainer<BlockState> blockStateContainer;
#if MC_VER < MC_1_18_2 #if MC_VER < MC_1_18_2
PalettedContainer<Biome> biomeContainer; PalettedContainer<Biome> biomeContainer;
#else #else
PalettedContainer<Holder<Biome>> biomeContainer; PalettedContainer<Holder<Biome>> biomeContainer;
#endif #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
{
blockStateContainer = new PalettedContainer<BlockState>(Block.BLOCK_STATE_REGISTRY, Blocks.AIR.defaultBlockState(), PalettedContainer.Strategy.SECTION_STATES);
}
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 #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, (message) -> logWarningOnce(message)) ? 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
boolean containsBiomes; if (tagSection.contains("biomes", 10))
#if MC_VER < MC_1_21_5 {
containsBiomes = tagSection.contains("biomes", 10); biomeContainer =
#else biomeCodec.parse(NbtOps.INSTANCE, tagSection.getCompound("biomes"))
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)) .promotePartial(string -> logBiomeDeserializationWarning(chunkPos, sectionYIndex, (String) string))
.getOrThrow((message) -> logErrorAndReturnException(message)); #if MC_VER < MC_1_20_6
#endif .getOrThrow(false, (message) -> logWarningOnce(message));
} #else
else .getOrThrow((message) -> logErrorAndReturnException(message));
{ #endif
biomeContainer = new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(), }
else
{
biomeContainer = new PalettedContainer<Holder<Biome>>(biomes.asHolderIdMap(),
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
biomes.getHolderOrThrow(Biomes.PLAINS), biomes.getHolderOrThrow(Biomes.PLAINS),
#else #else
biomes.getOrThrow(Biomes.PLAINS), biomes.getOrThrow(Biomes.PLAINS),
#endif #endif
PalettedContainer.Strategy.SECTION_BIOMES); PalettedContainer.Strategy.SECTION_BIOMES);
} }
#endif #endif
#if MC_VER < MC_1_20_1 #if MC_VER < MC_1_20_1
chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer); chunkSections[sectionId] = new LevelChunkSection(sectionYPos, blockStateContainer, biomeContainer);
#else #else
chunkSections[sectionId] = new LevelChunkSection(blockStateContainer, biomeContainer); chunkSections[sectionId] = new LevelChunkSection(blockStateContainer, biomeContainer);
#endif #endif
} }
#endif #endif
}
} }
return chunkSections; return chunkSections;
} }
@@ -391,54 +345,39 @@ public class ChunkLoader
#else ChunkType #endif #else ChunkType #endif
readChunkType(CompoundTag tagLevel) readChunkType(CompoundTag tagLevel)
{ {
ChunkStatus chunkStatus = ChunkStatus.byName(tagGetString(tagLevel,"Status")); ChunkStatus chunkStatus = ChunkStatus.byName(tagLevel.getString("Status"));
if (chunkStatus != null) if (chunkStatus != null)
{ {
return chunkStatus.getChunkType(); return chunkStatus.getChunkType();
} }
#if MC_VER <= MC_1_20_4 return
return ChunkStatus.ChunkType.PROTOCHUNK; #if MC_VER <= MC_1_20_4 ChunkStatus.ChunkType.PROTOCHUNK;
#else #else ChunkType.PROTOCHUNK; #endif
return ChunkType.PROTOCHUNK;
#endif
} }
private static void readHeightmaps(LevelChunk chunk, CompoundTag chunkData) private static void readHeightmaps(LevelChunk chunk, CompoundTag chunkData)
{ {
CompoundTag tagHeightmaps = tagGetCompoundTag(chunkData, "Heightmaps"); CompoundTag tagHeightmaps = chunkData.getCompound("Heightmaps");
for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter()) for (Heightmap.Types type : ChunkStatus.FULL.heightmapsAfter())
{ {
String heightmap = type.getSerializationKey(); String heightmap = type.getSerializationKey();
#if MC_VER < MC_1_21_5
if (tagHeightmaps.contains(heightmap, 12)) if (tagHeightmaps.contains(heightmap, 12))
{
chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmap)); chunk.setHeightmap(type, tagHeightmaps.getLongArray(heightmap));
}
#else
if (tagHeightmaps.contains(heightmap))
{
Optional<long[]> optionalHeightmap = tagHeightmaps.getLongArray(heightmap);
if (optionalHeightmap.isPresent())
{
chunk.setHeightmap(type, optionalHeightmap.get());
}
}
#endif
} }
Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter()); Heightmap.primeHeightmaps(chunk, ChunkStatus.FULL.heightmapsAfter());
} }
private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData) private static void readPostPocessings(LevelChunk chunk, CompoundTag chunkData)
{ {
ListTag tagPostProcessings = tagGetListTag(chunkData,"PostProcessing", 9); ListTag tagPostProcessings = chunkData.getList("PostProcessing", 9);
for (int i = 0; i < tagPostProcessings.size(); ++i) for (int i = 0; i < tagPostProcessings.size(); ++i)
{ {
ListTag listTag3 = tagGetListTag(tagPostProcessings, i); ListTag listTag3 = tagPostProcessings.getList(i);
for (int j = 0; j < listTag3.size(); ++j) for (int j = 0; j < listTag3.size(); ++j)
{ {
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
chunk.addPackedPostProcess(listTag3.getShort(j), i); chunk.addPackedPostProcess(listTag3.getShort(j), i);
#else #else
chunk.addPackedPostProcess(ShortList.of(tagGetShort(listTag3, j)), i); chunk.addPackedPostProcess(ShortList.of(listTag3.getShort(j)), i);
#endif #endif
} }
} }
@@ -447,38 +386,16 @@ public class ChunkLoader
private static BlendingData readBlendingData(CompoundTag chunkData) private static BlendingData readBlendingData(CompoundTag chunkData)
{ {
BlendingData blendingData = null; BlendingData blendingData = null;
if (chunkData.contains("blending_data", 10))
boolean containsBlendingData;
#if MC_VER < MC_1_21_5
containsBlendingData = chunkData.contains("blending_data", 10);
#else
containsBlendingData = chunkData.contains("blending_data");
#endif
if (containsBlendingData)
{ {
@SuppressWarnings({"unchecked", "rawtypes"}) @SuppressWarnings({"unchecked", "rawtypes"})
Dynamic<CompoundTag> blendingDataTag = new Dynamic(NbtOps.INSTANCE, chunkData.getCompound("blending_data")); 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) -> logWarningOnce(message)).orElse(null);
#if MC_VER < MC_1_21_3 #else
blendingData = BlendingData.CODEC.parse(blendingDataTag).resultOrPartial((message) -> logParsingWarningOnce(message)).orElse(null); blendingData = BlendingData.unpack(BlendingData.Packed.CODEC.parse(blendingDataTag).resultOrPartial((message) -> logWarningOnce(message)).orElse(null));
#else #endif
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);
}
} }
return blendingData; return blendingData;
} }
@@ -555,37 +472,33 @@ public class ChunkLoader
// if null all lights = 0 // if null all lights = 0
byte[] blockLightNibbleArray = tagGetByteArray(chunkSectionCompoundTag, "BlockLight"); byte[] blockLightNibbleArray = chunkSectionCompoundTag.getByteArray("BlockLight");
byte[] skyLightNibbleArray = tagGetByteArray(chunkSectionCompoundTag, "SkyLight"); byte[] skyLightNibbleArray = chunkSectionCompoundTag.getByteArray("SkyLight");
if (blockLightNibbleArray != null // if any sky light was found then all lights above will be max brightness
&& skyLightNibbleArray != null) if (skyLightNibbleArray.length != 0)
{ {
// if any sky light was found then all lights above will be max brightness foundSkyLight = true;
if (skyLightNibbleArray.length != 0) }
for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++)
{
for (int relZ = 0; relZ < LodUtil.CHUNK_WIDTH; relZ++)
{ {
foundSkyLight = true; // chunk sections are also 16 blocks tall
} for (int relY = 0; relY < LodUtil.CHUNK_WIDTH; relY++)
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 int blockPosIndex = relY*16*16 + relZ*16 + relX;
for (int relY = 0; relY < LodUtil.CHUNK_WIDTH; relY++) 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; skyLight = LodUtil.MAX_MC_LIGHT;
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);
} }
int y = relY + (sectionIndex * LodUtil.CHUNK_WIDTH) + ChunkWrapper.getInclusiveMinBuildHeight(chunk);
blockLightStorage.set(relX, y, relZ, blockLight);
skyLightStorage.set(relX, y, relZ, skyLight);
} }
} }
} }
@@ -634,14 +547,9 @@ public class ChunkLoader
}); });
} }
private static void logParsingWarningOnce(String message) { logParsingWarningOnce(message, null); } private static void logWarningOnce(String message) { logWarningOnce(message, null); }
private static void logParsingWarningOnce(String message, Exception e) private static void logWarningOnce(String message, Exception e)
{ {
if (message == null)
{
return;
}
LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) -> LOGGED_ERROR_MESSAGE_MAP.computeIfAbsent(message, (newMessage) ->
{ {
LOGGER.warn("Parsing error: ["+newMessage+"]. " + LOGGER.warn("Parsing error: ["+newMessage+"]. " +
@@ -668,134 +576,6 @@ public class ChunkLoader
//====================//
// 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 */
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 */
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 // // helper classes //
@@ -229,16 +229,8 @@ public class DhLitWorldGenRegion extends WorldGenRegion
{ {
ChunkAccess chunkAccess = this.getChunk(blockPos); ChunkAccess chunkAccess = this.getChunk(blockPos);
if (chunkAccess instanceof LevelChunk) if (chunkAccess instanceof LevelChunk)
{
return true; return true;
}
#if MC_VER < MC_1_21_5
chunkAccess.setBlockState(blockPos, blockState, /*isBlockMoving*/false); 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 // This is for post ticking for water on gen and stuff like that. Not enabled
// for now. // for now.
// if (blockState.hasPostProcess(this, blockPos)) // if (blockState.hasPostProcess(this, blockPos))
+1 -1
View File
@@ -1,5 +1,5 @@
plugins { plugins {
id "fabric-loom" version "1.10-SNAPSHOT" id "fabric-loom" version "1.8-SNAPSHOT"
} }
loom { loom {
@@ -34,34 +34,21 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
import com.mojang.blaze3d.platform.NativeImage; import com.mojang.blaze3d.platform.NativeImage;
#elif MC_VER < MC_1_21_5
import com.mojang.blaze3d.pipeline.TextureTarget;
#else #else
import com.mojang.blaze3d.opengl.GlTexture; import com.mojang.blaze3d.pipeline.TextureTarget;
import com.mojang.blaze3d.systems.RenderSystem;
import com.mojang.blaze3d.textures.GpuTexture;
#endif #endif
@Mixin(LightTexture.class) @Mixin(LightTexture.class)
public class MixinLightTexture public class MixinLightTexture
{ {
#if MC_VER < MC_1_21_3
@Shadow @Shadow
@Final @Final
#if MC_VER < MC_1_21_3
private NativeImage lightPixels; private NativeImage lightPixels;
#elif MC_VER < MC_1_21_5
@Shadow
@Final
private TextureTarget target;
#else #else
@Shadow private TextureTarget target;
@Final
private GpuTexture texture;
#endif #endif
@Inject(method = "updateLightTexture(F)V", at = @At("RETURN")) @Inject(method = "updateLightTexture(F)V", at = @At("RETURN"))
public void updateLightTexture(float partialTicks, CallbackInfo ci) public void updateLightTexture(float partialTicks, CallbackInfo ci)
{ {
@@ -76,11 +63,8 @@ public class MixinLightTexture
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
MinecraftRenderWrapper.INSTANCE.updateLightmap(this.lightPixels, clientLevel); MinecraftRenderWrapper.INSTANCE.updateLightmap(this.lightPixels, clientLevel);
#elif MC_VER < MC_1_21_5
MinecraftRenderWrapper.INSTANCE.setLightmapId(this.target.getColorTextureId(), clientLevel);
#else #else
GlTexture glTexture = (GlTexture) this.texture; MinecraftRenderWrapper.INSTANCE.setLightmapId(this.target.getColorTextureId(), clientLevel);
MinecraftRenderWrapper.INSTANCE.setLightmapId(glTexture.glId(), clientLevel);
#endif #endif
} }
@@ -76,25 +76,15 @@ public abstract class MixinMinecraft
if (SelfUpdater.onStart() || DEBUG_ALWAYS_SHOW_UPDATER) 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
instance.setScreen(new UpdateModScreen( (Config.Client.Advanced.AutoUpdater.updateBranch.get() == EDhApiUpdateBranch.STABLE ? ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()): GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha"))
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
@@ -134,13 +124,14 @@ 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 (Exception e) catch (IllegalArgumentException 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,15 +1,12 @@
package com.seibel.distanthorizons.fabric.mixins.client; package com.seibel.distanthorizons.fabric.mixins.client;
#if MC_VER < MC_1_21_5
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.GlStateManager;
#endif
import com.mojang.blaze3d.platform.TextureUtil; import com.mojang.blaze3d.platform.TextureUtil;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect; import org.spongepowered.asm.mixin.injection.Redirect;
/** /**
* Sets Minecraft's LOD Bias (looks similar to mipmaps) * Sets Minecraft's LOD Bias (looks similar to mipmaps)
* *
@@ -18,9 +15,6 @@ import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(TextureUtil.class) @Mixin(TextureUtil.class)
public class MixinTextureUtil 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", @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)) 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) private static void setLodBias(int target, int pname, float param)
@@ -33,6 +27,5 @@ public class MixinTextureUtil
GlStateManager._texParameter(target, pname, biasValue); GlStateManager._texParameter(target, pname, biasValue);
} }
} }
#endif
} }
@@ -0,0 +1,55 @@
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
@@ -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 = 20; maxHeight = 60;
break; break;
case 1: case 1:
blockResourceLocation = "minecraft:orange_wool"; blockResourceLocation = "minecraft:orange_wool";
maxHeight = 30; maxHeight = 70;
break; break;
case 2: case 2:
blockResourceLocation = "minecraft:yellow_wool"; blockResourceLocation = "minecraft:yellow_wool";
maxHeight = 40; maxHeight = 80;
break; break;
case 3: case 3:
blockResourceLocation = "minecraft:lime_wool"; blockResourceLocation = "minecraft:lime_wool";
maxHeight = 50; maxHeight = 90;
break; break;
case 4: case 4:
blockResourceLocation = "minecraft:cyan_wool"; blockResourceLocation = "minecraft:cyan_wool";
maxHeight = 60; maxHeight = 100;
break; break;
case 5: case 5:
blockResourceLocation = "minecraft:blue_wool"; blockResourceLocation = "minecraft:blue_wool";
maxHeight = 70; maxHeight = 100;
break; break;
case 6: case 6:
blockResourceLocation = "minecraft:magenta_wool"; blockResourceLocation = "minecraft:magenta_wool";
maxHeight = 80; maxHeight = 110;
break; break;
case 7: case 7:
blockResourceLocation = "minecraft:white_wool"; blockResourceLocation = "minecraft:white_wool";
maxHeight = 90; maxHeight = 120;
break; break;
case 8: case 8:
blockResourceLocation = "minecraft:gray_wool"; blockResourceLocation = "minecraft:gray_wool";
maxHeight = 100; maxHeight = 120;
break; break;
default: default:
blockResourceLocation = "minecraft:black_wool"; blockResourceLocation = "minecraft:black_wool";
maxHeight = 110; maxHeight = 140;
break; break;
} }
@@ -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); // TODO biomes are broken for some reason //IDhApiWorldGenerator exampleWorldGen = new TestChunkWorldGenerator(level);
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,7 +8,8 @@
"server.MixinEntity", "server.MixinEntity",
"server.MixinServerPlayer", "server.MixinServerPlayer",
"server.MixinTracingExecutor", "server.MixinTracingExecutor",
"server.MixinUtilBackgroundThread" "server.MixinUtilBackgroundThread",
"server.MixinLevelTicks"
], ],
"client": [ "client": [
"client.MixinClientLevel", "client.MixinClientLevel",
@@ -24,16 +24,13 @@ import com.seibel.distanthorizons.common.util.ProxyUtil;
import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper; import com.seibel.distanthorizons.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper; import com.seibel.distanthorizons.common.wrappers.world.ClientLevelWrapper;
import com.seibel.distanthorizons.core.api.internal.ClientApi; 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.api.internal.SharedApi;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; 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.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
@@ -55,7 +52,6 @@ import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraftforge.common.MinecraftForge; import net.minecraftforge.common.MinecraftForge;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.jetbrains.annotations.NotNull;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper; import com.seibel.distanthorizons.common.wrappers.chunk.ChunkWrapper;
@@ -94,13 +90,7 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
public void registerEvents() public void registerEvents()
{ {
MinecraftForge.EVENT_BUS.register(this); MinecraftForge.EVENT_BUS.register(this);
ForgePluginPacketSender.setPacketHandler(ClientApi.INSTANCE::pluginMessageReceived);
// handles singleplayer, LAN, and connecting to a server
ForgePluginPacketSender.setPacketHandler((IServerPlayerWrapper player, @NotNull AbstractNetworkMessage message) ->
{
ClientApi.INSTANCE.pluginMessageReceived(message);
ServerApi.INSTANCE.pluginMessageReceived(player, message);
});
} }
@@ -78,7 +78,10 @@ public class ForgeServerProxy implements AbstractModInitializer.IEventProxy
// constructor // // constructor //
//=============// //=============//
public ForgeServerProxy(boolean isDedicated) { this.isDedicated = isDedicated; } public ForgeServerProxy(boolean isDedicated)
{
this.isDedicated = isDedicated;
}
@@ -57,25 +57,15 @@ public class MixinMinecraft
if (SelfUpdater.onStart()) 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
instance.setScreen(new UpdateModScreen( (Config.Client.Advanced.AutoUpdater.updateBranch.get() == EDhApiUpdateBranch.STABLE ? ModrinthGetter.getLatestIDForVersion(SingletonInjector.INSTANCE.get(IVersionConstants.class).getMinecraftVersion()): GitlabGetter.INSTANCE.projectPipelines.get(0).get("sha"))
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
@@ -118,7 +108,7 @@ public class MixinMinecraft
versionId versionId
)); ));
} }
catch (Exception e) catch (IllegalArgumentException 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
+3 -2
View File
@@ -5,7 +5,7 @@ org.gradle.caching=true
# Mod Info # Mod Info
mod_name=DistantHorizons mod_name=DistantHorizons
mod_version=2.3.2-b mod_version=2.3.0-b-dev
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
@@ -25,6 +25,7 @@ lz4_version=1.8.0
xz_version=1.9 xz_version=1.9
sqlite_jdbc_version=3.47.2.0 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 # 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 fastutil_version=8.2.1
#svgSalamander_version=1.1.3 #svgSalamander_version=1.1.3
@@ -50,7 +51,7 @@ versionStr=
# This defines what MC version Intellij will use for the preprocessor # This defines what MC version Intellij will use for the preprocessor
# and what version is used automatically by build and run commands # and what version is used automatically by build and run commands
mcVer=1.21.5 mcVer=1.21.4
# Defines the maximum amount of memory Minecraft is allowed when run in a development environment # Defines the maximum amount of memory Minecraft is allowed when run in a development environment
#minecraftMemoryJavaArg="-Xmx4G" #minecraftMemoryJavaArg="-Xmx4G"
+1 -1
View File
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.10-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
@@ -26,9 +26,7 @@ import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.api.internal.ServerApi; import com.seibel.distanthorizons.core.api.internal.ServerApi;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender; 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.IModChecker;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
@@ -53,7 +51,6 @@ import java.util.function.Consumer;
import net.neoforged.neoforge.client.ConfigScreenHandler; import net.neoforged.neoforge.client.ConfigScreenHandler;
#else #else
import net.neoforged.neoforge.client.gui.IConfigScreenFactory; import net.neoforged.neoforge.client.gui.IConfigScreenFactory;
import org.jetbrains.annotations.NotNull;
#endif #endif
/** /**
@@ -67,16 +64,11 @@ public class NeoforgeMain extends AbstractModInitializer
{ {
public NeoforgeMain(IEventBus eventBus) public NeoforgeMain(IEventBus eventBus)
{ {
// handles singleplayer, LAN, and connecting to a server eventBus.addListener((FMLClientSetupEvent e) -> {
eventBus.addListener((FMLClientSetupEvent e) ->
{
this.onInitializeClient(); this.onInitializeClient();
eventBus.addListener(this::registerNetworkingClientServer); eventBus.addListener(this::registerNetworkingClient);
}); });
eventBus.addListener((FMLDedicatedServerSetupEvent e) -> {
// handles dedicated servers
eventBus.addListener((FMLDedicatedServerSetupEvent e) ->
{
this.onInitializeServer(); this.onInitializeServer();
eventBus.addListener(this::registerNetworkingServer); eventBus.addListener(this::registerNetworkingServer);
}); });
@@ -87,22 +79,13 @@ public class NeoforgeMain extends AbstractModInitializer
//============// //============//
// networking // // networking //
//============// //============//
public void registerNetworkingClient(RegisterPayloadHandlersEvent event)
public void registerNetworkingClientServer(RegisterPayloadHandlersEvent event) { NeoforgePluginPacketSender.setPacketHandler(event, ClientApi.INSTANCE::pluginMessageReceived); }
{
NeoforgePluginPacketSender.setPacketHandler(event, (IServerPlayerWrapper player, @NotNull AbstractNetworkMessage message) ->
{
ClientApi.INSTANCE.pluginMessageReceived(message);
ServerApi.INSTANCE.pluginMessageReceived(player, message);
});
}
public void registerNetworkingServer(RegisterPayloadHandlersEvent event) public void registerNetworkingServer(RegisterPayloadHandlersEvent event)
{ NeoforgePluginPacketSender.setPacketHandler(event, ServerApi.INSTANCE::pluginMessageReceived); } { NeoforgePluginPacketSender.setPacketHandler(event, ServerApi.INSTANCE::pluginMessageReceived); }
@Override @Override
protected IEventProxy createServerProxy(boolean isDedicated) { return new NeoforgeServerProxy(isDedicated); } protected IEventProxy createServerProxy(boolean isDedicated) { return new NeoforgeServerProxy(isDedicated); }
@@ -19,12 +19,17 @@
package com.seibel.distanthorizons.neoforge.mixins.client; 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.common.wrappers.minecraft.MinecraftRenderWrapper;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.client.renderer.LightTexture; import net.minecraft.client.renderer.LightTexture;
import org.lwjgl.opengl.GL32;
import org.spongepowered.asm.mixin.Final; import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
@@ -32,32 +37,14 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 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;
#endif
@Mixin(LightTexture.class) @Mixin(LightTexture.class)
public class MixinLightTexture public class MixinLightTexture
{ {
#if MC_VER < MC_1_21_3 #if MC_VER < MC_1_21_3
@Shadow @Shadow
@Final @Final
private NativeImage lightPixels; 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")) @Inject(method = "updateLightTexture(F)V", at = @At("RETURN"))
public void updateLightTexture(float partialTicks, CallbackInfo ci) public void updateLightTexture(float partialTicks, CallbackInfo ci)
@@ -68,17 +55,24 @@ public class MixinLightTexture
return; 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(); IClientLevelWrapper clientLevel = mc.getWrappedClientLevel();
#if MC_VER < MC_1_21_3
MinecraftRenderWrapper.INSTANCE.updateLightmap(this.lightPixels, clientLevel);
#elif MC_VER < MC_1_21_5
MinecraftRenderWrapper.INSTANCE.setLightmapId(this.target.getColorTextureId(), clientLevel); MinecraftRenderWrapper.INSTANCE.setLightmapId(this.target.getColorTextureId(), clientLevel);
#else
GlTexture glTexture = (GlTexture) this.texture;
MinecraftRenderWrapper.INSTANCE.setLightmapId(glTexture.glId(), clientLevel);
#endif
} }
#endif
} }
@@ -76,13 +76,14 @@ 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 (Exception e) catch (IllegalArgumentException 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,8 +1,6 @@
package com.seibel.distanthorizons.neoforge.mixins.client; package com.seibel.distanthorizons.neoforge.mixins.client;
#if MC_VER < MC_1_21_5
import com.mojang.blaze3d.platform.GlStateManager; import com.mojang.blaze3d.platform.GlStateManager;
#endif
import com.mojang.blaze3d.platform.TextureUtil; import com.mojang.blaze3d.platform.TextureUtil;
import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.Config;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
@@ -17,9 +15,6 @@ import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(TextureUtil.class) @Mixin(TextureUtil.class)
public class MixinTextureUtil 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", @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) 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) private static void setLodBias(int target, int pname, float param)
@@ -32,6 +27,5 @@ public class MixinTextureUtil
GlStateManager._texParameter(target, pname, biasValue); GlStateManager._texParameter(target, pname, biasValue);
} }
} }
#endif
} }
@@ -0,0 +1,53 @@
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,7 +8,8 @@
"server.MixinServerPlayer", "server.MixinServerPlayer",
"server.MixinTFChunkGenerator", "server.MixinTFChunkGenerator",
"server.MixinTracingExecutor", "server.MixinTracingExecutor",
"server.MixinUtilBackgroundThread" "server.MixinUtilBackgroundThread",
"server.MixinLevelTicks"
], ],
"client": [ "client": [
"client.MixinClientPacketListener", "client.MixinClientPacketListener",
-110
View File
@@ -1,110 +0,0 @@
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}")
-31
View File
@@ -1,31 +0,0 @@
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()
-5
View File
@@ -1,5 +0,0 @@
$ErrorActionPreference = "Stop"
python -m venv .venv
.\.venv\Scripts\activate
pip install -r requirements.txt
-7
View File
@@ -1,7 +0,0 @@
#!/bin/sh
set -e
python -m venv .venv
. ./.venv/bin/activate
pip install -r requirements.txt
Binary file not shown.
+1 -1
View File
@@ -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.115.0+1.21.1 fabric_api_version=0.107.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=
-56
View File
@@ -1,56 +0,0 @@
# 1.21.4 version
java_version=21
minecraft_version=1.21.5
parchment_version=1.21:2024.07.28
compatible_minecraft_versions=["1.21.5"]
accessWidenerVersion=1_21_4
builds_for=fabric,neoforge
# forge is broken due to gradle/build script issues
# Netty
netty_version=4.1.97.Final
# Fabric loader
fabric_loader_version=0.16.10
fabric_api_version=0.119.5+1.21.5
modmenu_version=14.0.0-rc.2
starlight_version_fabric=
phosphor_version_fabric=
lithium_version=
sodium_version=mc1.21.5-0.6.11-fabric
iris_version=1.8.10+1.21.5-fabric
bclib_version=
immersive_portals_version=
canvas_version=
# some versions of 1.8.11 nightly builds may not work, but the ones after 2025-03-30 should
fabric_incompatibility_list={ "iris": "<=1.8.10" }
fabric_recommend_list={}
# Fabric mod run
# 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client
enable_starlight=0
enable_phosphor=0
enable_sodium=1
enable_lithium=0
enable_iris=1
enable_bclib=0
enable_immersive_portals=0
enable_canvas=0
# (Neo)Forge loader
forge_version=
neoforge_version=21.5.0-beta
# (Neo)Forge mod versions
starlight_version_forge=
terraforged_version=
# (Neo)Forge mod run
# 0 = Don't enable and don't run
# 1 = Can be referenced in code but doesn't run
# 2 = Can be referenced in code and runs in client
enable_starlight_forge=0
enable_terraforged=0
enable_terrafirmacraft=0