Compare commits

..

13 Commits

Author SHA1 Message Date
s809 7c37a5c370 Run prepare only when needed 2025-04-27 00:40:12 +05:00
s809 b495ac4799 remove python dependency for building with correct sqlite natives 2025-04-27 00:13:24 +05:00
Ran 3721ebea6e Improve LodDataBuilder.java
- Use bitwise modulo
- Don't compute certain things 256 times when they can be computed once.
- Removed expressions that are always false
- Improved comments
2025-04-11 11:24:17 +10:00
Ran 98f8a87362 Improve LodDataBuilder.java
- Use bitwise modulo
- Don't compute certain things 256 times when they can be computed once.
- Removed expressions that are always false
- Improved comments
2025-04-11 11:20:05 +10:00
Ran 10a743ddef Don't check for Indium for Sodium version >= 0.6 2025-04-07 23:53:02 +10:00
James Seibel 95c896f964 maybe break n-sized rendering but fix LOD loading getting stuck 2025-04-07 06:56:58 -05:00
James Seibel 040bc16874 re-add comment to getWorldFolderName() 2025-04-07 06:55:45 -05:00
James Seibel 35d3fdb473 Revert "bandaid fix for Forge 1.20.1 UI crashing"
This reverts commit 2b519a826f.
2025-04-07 06:55:01 -05:00
Ran 549f7510f7 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	common/src/main/java/com/seibel/distanthorizons/common/wrappers/block/TintWithoutLevelOverrider.java
2025-04-07 13:52:25 +10:00
Ran fab64d8477 Fix white foliage issue 2025-04-07 13:52:02 +10:00
Ran 4a6a35f617 Fix white foliage issue 2025-04-07 13:47:42 +10:00
James Seibel 2b519a826f bandaid fix for Forge 1.20.1 UI crashing 2025-04-05 09:19:36 -05:00
James Seibel 445c01b5ae up version number 2.3.2 -> 2.3.3 2025-04-05 09:11:45 -05:00
35 changed files with 128 additions and 66 deletions
-1
View File
@@ -29,7 +29,6 @@ buildAllJars/
relocate_natives/.venv/ relocate_natives/.venv/
relocate_natives/__pycache__/ relocate_natives/__pycache__/
relocate_natives/apple-codesign/ relocate_natives/apple-codesign/
relocate_natives/cache/
# file from notepad++ # file from notepad++
*.bak *.bak
-3
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
+3 -25
View File
@@ -106,24 +106,12 @@ forgix {
class NativeTransformer implements Transformer { class NativeTransformer implements Transformer {
private boolean enabled = false
private final HashMap<String, String> replacements = new HashMap() private final HashMap<String, String> replacements = new HashMap()
private final HashMap<String, byte[]> rewrittenFiles = new HashMap() private final HashMap<String, byte[]> rewrittenFiles = new HashMap()
private var nativeRelocator private var nativeRelocator
public File rootDir 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) { void relocateNative(String target, String replacement) {
if (replacement.length() > target.length()) { if (replacement.length() > target.length()) {
throw new GradleException("Length of value \"${replacement}\" exceeds the length of \"${target}\": ${replacement.length()} > ${target.length()}") throw new GradleException("Length of value \"${replacement}\" exceeds the length of \"${target}\": ${replacement.length()} > ${target.length()}")
@@ -132,22 +120,15 @@ class NativeTransformer implements Transformer {
replacements.put(target, replacement) replacements.put(target, replacement)
} }
void before(Closure closure) {
if (enabled)
closure.run()
}
@Override @Override
boolean canTransformResource(@Nonnull FileTreeElement element) { boolean canTransformResource(@Nonnull FileTreeElement element) {
return enabled && replacements.keySet().stream().anyMatch { return replacements.keySet().stream().anyMatch {
element.name.startsWith(it as String) element.name.startsWith(it as String)
} }
} }
@Override @Override
void transform(@Nonnull TransformerContext context) { void transform(@Nonnull TransformerContext context) {
println("Transforming $context.path...")
byte[] content = context.is.readAllBytes() byte[] content = context.is.readAllBytes()
if (nativeRelocator == null) { if (nativeRelocator == null) {
@@ -395,16 +376,13 @@ subprojects { p ->
// Sqlite Database // Sqlite Database
// librariesLocation isn't used because it's too long for replacing paths in native libraries // librariesLocation isn't used because it's too long for replacing paths in native libraries
// Allowing strings larger than the original string would require shifting the entire binary's contents // Allowing strings larger than the original string would require shifting the entire binary's contents
transform(NativeTransformer) {
rootDir = project.rootDir
before {
relocate "org.sqlite", "dh_sqlite", { relocate "org.sqlite", "dh_sqlite", {
exclude "org/sqlite/native/**" exclude "org/sqlite/native/**"
} }
relocate "jdbc:sqlite", "jdbc:dh_sqlite" relocate "jdbc:sqlite", "jdbc:dh_sqlite"
}
transform(NativeTransformer) {
rootDir = project.rootDir
relocateNative "org/sqlite", "dh_sqlite" relocateNative "org/sqlite", "dh_sqlite"
relocateNative "org_sqlite", "dh_1sqlite" relocateNative "org_sqlite", "dh_1sqlite"
} }
+7 -3
View File
@@ -12,14 +12,15 @@ class NativeRelocator
/** /**
* Initializes the NativeRelocator by preparing the environment if necessary. * Initializes the NativeRelocator by preparing the environment if necessary.
* Executes the appropriate preparation script based on the OS. * 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 NativeRelocator(Path rootDirectory)
{ {
this.rootDirectory = rootDirectory; this.rootDirectory = rootDirectory;
this.cacheRoot = this.rootDirectory.resolve("cache"); this.cacheRoot = this.rootDirectory.resolve("cache");
}
private void prepare() throws Exception
{
if (this.rootDirectory.resolve(".venv").toFile().exists()) if (this.rootDirectory.resolve(".venv").toFile().exists())
{ {
return; return;
@@ -196,6 +197,9 @@ class NativeRelocator
return Files.readAllBytes(outputFilePath); return Files.readAllBytes(outputFilePath);
} }
System.out.println("Relocating to " + outputPath + "...");
this.prepare();
for (Map.Entry<String, String> replacement : replacements.entrySet()) for (Map.Entry<String, String> replacement : replacements.entrySet())
{ {
this.replaceInNullTerminatedStrings(content, replacement.getKey(), replacement.getValue()); this.replaceInNullTerminatedStrings(content, replacement.getKey(), replacement.getValue());
@@ -19,19 +19,16 @@
package com.seibel.distanthorizons.common.wrappers.block; package com.seibel.distanthorizons.common.wrappers.block;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.ColorUtil; import com.seibel.distanthorizons.core.util.ColorUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.world.level.*; import net.minecraft.world.level.*;
import net.minecraft.world.level.biome.Biome; import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.biome.Biomes;
import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.lighting.LevelLightEngine; import net.minecraft.world.level.lighting.LevelLightEngine;
import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.FluidState;
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;
@@ -46,30 +43,61 @@ public class TintWithoutLevelOverrider implements BlockAndTintGetter
* but {@link Nullable} is there just in case. * but {@link Nullable} is there just in case.
*/ */
@Nullable @Nullable
private final Biome biome; #if MC_VER >= MC_1_18_2
public final Holder<Biome> biome;
#else
public final Biome biome;
#endif
/**
* Constructs the TintWithoutLevelOverrider, storing the provided Biome Holder for late-binding access.
*
* <p>Previously, this class might have immediately unwrapped the Holder like this:</p>
* <pre>{@code
* // Inside constructor (OLD WAY - PROBLEMATIC):
* Holder<Biome> biomeHolder = getTheHolderFromSomewhere();
* this.biome = biomeHolder.value(); // <-- PROBLEM HERE
* }</pre>
*
* <p>This approach is problematic because the {@link net.minecraft.core.Holder} system,
* particularly {@code Holder.Reference}, is designed for <strong>late binding</strong>. Here's why storing
* the Holder itself is now necessary:</p>
* <ol>
* <li>A {@code Holder.Reference<Biome>} might be created initially just with a
* {@link net.minecraft.resources.ResourceKey} (like {@code minecraft:plains}), but its actual
* {@link net.minecraft.core.Holder#value() value()} (the {@code Biome} object itself) might be {@code null}
* at construction time.</li>
* <li>Later, during game loading, registry population, or potentially due to modifications by other mods
* (e.g., Polytone), the system calls internal binding methods (like {@code bindValue(Biome)})
* on the {@code Holder} instance. This sets or <strong>updates</strong> the internal reference to the
* actual {@code Biome} object.</li>
* <li>Crucially, the binding process might assign a completely <strong>new</strong> {@code Biome} object
* instance to the {@code Holder} reference, replacing any previous one.</li>
* </ol>
*
* <p>If we unwrapped the {@code Holder} using {@code .value()} within the constructor (the old way),
* our class's internal {@code biome} field would permanently store a reference to whatever {@code Biome}
* object the {@code Holder} pointed to *at that exact moment*. It would have no link back to the
* {@code Holder} and would be unaware if the {@code Holder} was later updated to point to a different
* (or the initially missing) {@code Biome} object. This would lead to using stale or even {@code null} data.</p>
*
* <p>By storing the {@code Holder<Biome>} itself, this class can call {@link net.minecraft.core.Holder#value()}
* whenever the biome information is needed, ensuring it always retrieves the most current {@code Biome}
* instance associated with the holder at that time.</p>
*/
public TintWithoutLevelOverrider(BiomeWrapper biomeWrapper, IClientLevelWrapper clientLevelWrapper) public TintWithoutLevelOverrider(BiomeWrapper biomeWrapper, IClientLevelWrapper clientLevelWrapper)
{ {
// try to get the wrapped biome #if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome = biomeWrapper.biome;
Biome unwrappedBiome = null; if (biome == null) // We are looking at the empty biome wrapper
if (biomeWrapper.biome != null)
{ {
unwrappedBiome = unwrap(biomeWrapper.biome);
}
if(unwrappedBiome == null)
{
// we are looking at the empty biome wrapper, try using plains as a backup
BiomeWrapper plainsBiomeWrapper = ((BiomeWrapper) clientLevelWrapper.getPlainsBiomeWrapper()); BiomeWrapper plainsBiomeWrapper = ((BiomeWrapper) clientLevelWrapper.getPlainsBiomeWrapper());
if (plainsBiomeWrapper != null) if (plainsBiomeWrapper != null)
{ {
unwrappedBiome = unwrap(plainsBiomeWrapper.biome); biome = plainsBiomeWrapper.biome;
} }
} }
this.biome = unwrappedBiome; this.biome = biome;
} }
@@ -77,15 +105,12 @@ public class TintWithoutLevelOverrider implements BlockAndTintGetter
@Override @Override
public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver) public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
{ {
if (this.biome != null) if (this.biome == null)
{
return colorResolver.getColor(this.biome, blockPos.getX(), blockPos.getZ());
}
else
{ {
// hopefully unneeded debug color // hopefully unneeded debug color
return ColorUtil.CYAN; return ColorUtil.CYAN;
} }
return colorResolver.getColor(unwrap(biome), blockPos.getX(), blockPos.getZ());
} }
private static Biome unwrap(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome) private static Biome unwrap(#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome)
@@ -115,6 +115,8 @@ public class ServerLevelWrapper implements IServerLevelWrapper
@Override @Override
public String getWorldFolderName() public String getWorldFolderName()
{ {
// TODO can we just replace this with getMcSaveFolder()? Why are we using the screenshot file anyway?
// this can have issues when the screenshot file is null/missing
#if MC_VER >= MC_1_17_1 #if MC_VER >= MC_1_17_1
return this.level.getServer().getWorldScreenshotFile().get().getParent().getFileName().toString(); return this.level.getServer().getWorldScreenshotFile().get().getParent().getFileName().toString();
#else // <= 1.16.5 #else // <= 1.16.5
@@ -90,8 +90,8 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
{ {
ModAccessorInjector.INSTANCE.bind(ISodiumAccessor.class, new SodiumAccessor()); ModAccessorInjector.INSTANCE.bind(ISodiumAccessor.class, new SodiumAccessor());
// If sodium is installed Indium is also necessary in order to use the Fabric rendering API // If sodium is installed Indium is also necessary for versions 0.5 and less in order to use the Fabric rendering API
if (!modChecker.isModLoaded("indium")) if (!modChecker.isModLoaded("indium") && SodiumAccessor.isSodiumV5OrLess)
{ {
String indiumMissingMessage = ModInfo.READABLE_NAME + " needs Indium to work with Sodium.\nPlease download Indium from https://modrinth.com/mod/indium"; String indiumMissingMessage = ModInfo.READABLE_NAME + " needs Indium to work with Sodium.\nPlease download Indium from https://modrinth.com/mod/indium";
LOGGER.fatal(indiumMissingMessage); LOGGER.fatal(indiumMissingMessage);
@@ -43,11 +43,22 @@ import net.minecraft.world.phys.AABB;
public class SodiumAccessor implements ISodiumAccessor public class SodiumAccessor implements ISodiumAccessor
{ {
/**
* True if sodium 0.5 or less is present. <br>
* This field is public because it's also used to check if we need Indium to be present. <br>
* We need Indium if Sodium 0.5 or less is present.
*/
public static final boolean isSodiumV5OrLess;
#if MC_VER >= MC_1_20_1 #if MC_VER >= MC_1_20_1
private static MethodHandle setFogOcclusionMethod; private static MethodHandle setFogOcclusionMethod;
private static Object sodiumPerformanceOptions; private static Object sodiumPerformanceOptions;
#endif #endif
static {
isSodiumV5OrLess = !classPresent("net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer");
}
//======================// //======================//
@@ -72,8 +83,7 @@ public class SodiumAccessor implements ISodiumAccessor
{ {
if (sodiumPerformanceOptions == null) if (sodiumPerformanceOptions == null)
{ {
boolean sodiumV6 = classPresent("net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer"); if (isSodiumV5OrLess)
if (!sodiumV6)
{ {
// sodium 0.5 // sodium 0.5
+2 -1
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.3-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
@@ -23,6 +23,7 @@ manifold_version=2024.1.37
nightconfig_version=3.6.6 nightconfig_version=3.6.6
lz4_version=1.8.0 lz4_version=1.8.0
xz_version=1.9 xz_version=1.9
# Before updating, read relocate_natives/README.md
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
fastutil_version=8.2.1 fastutil_version=8.2.1
+46
View File
@@ -0,0 +1,46 @@
This directory contains the native files of libraries that were modified for relocation. They will be copied from here during the normal build steps.
Before adding/updating a library, make sure you have Python 3.8+ installed and check the instructions below.
How to add a library's natives:
1. In `build.gradle`:
- Make sure the target package is the same length or shorter (untested) than the source package. Underscores in native methods will take 2 characters so account for that as well.
- Exclude the native files and add them as `relocateNative` (see example).
Example:
```groovy
relocate "org.sqlite", "dh_sqlite", {
exclude "org/sqlite/native/**"
}
transform(NativeTransformer) {
// NativeTransformer configuration
rootDir = project.rootDir
// Replace native strings, e.g. used in calls back to Java
relocateNative "org/sqlite", "dh_sqlite"
// Rename native methods used when calling from Java
relocateNative "org_sqlite", "dh_1sqlite"
}
```
How to update a library's natives:
1. Delete the library's folder in cache/.
2. It will repopulate during the next build.
Why does this step exist?
- Native files are not handled by the shadow plugin correctly.
- I preferred it as a more streamlined approach, although a bit hacky.
- Possible alternatives:
- Use edited libraries' source code: although more straightforward, it requires maintaining and updating the repositories for the libraries being added
- Interfacing with the necessary libraries directly: an absolute mess for technical reasons
What are libraries used?
- LIEF: for fixing binaries after replacing strings
- apple-codesign: for re-signing Mac binaries, since their signatures get invalidated after previous steps