Compare commits
13 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7c37a5c370 | |||
| b495ac4799 | |||
| 3721ebea6e | |||
| 98f8a87362 | |||
| 10a743ddef | |||
| 95c896f964 | |||
| 040bc16874 | |||
| 35d3fdb473 | |||
| 549f7510f7 | |||
| fab64d8477 | |||
| 4a6a35f617 | |||
| 2b519a826f | |||
| 445c01b5ae |
@@ -29,7 +29,6 @@ buildAllJars/
|
||||
relocate_natives/.venv/
|
||||
relocate_natives/__pycache__/
|
||||
relocate_natives/apple-codesign/
|
||||
relocate_natives/cache/
|
||||
|
||||
# file from notepad++
|
||||
*.bak
|
||||
|
||||
@@ -17,9 +17,6 @@ variables:
|
||||
# These can be extended so code is a bit less duplicated
|
||||
.build_java:
|
||||
#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:
|
||||
key: "gradleCache_$CI_JOB_NAME_SLUG"
|
||||
policy: pull-push
|
||||
|
||||
+3
-25
@@ -106,24 +106,12 @@ forgix {
|
||||
|
||||
|
||||
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()}")
|
||||
@@ -132,22 +120,15 @@ class NativeTransformer implements Transformer {
|
||||
replacements.put(target, replacement)
|
||||
}
|
||||
|
||||
void before(Closure closure) {
|
||||
if (enabled)
|
||||
closure.run()
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
boolean canTransformResource(@Nonnull FileTreeElement element) {
|
||||
return enabled && replacements.keySet().stream().anyMatch {
|
||||
return 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) {
|
||||
@@ -395,16 +376,13 @@ subprojects { p ->
|
||||
// Sqlite Database
|
||||
// librariesLocation isn't used because it's too long for replacing paths in native libraries
|
||||
// Allowing strings larger than the original string would require shifting the entire binary's contents
|
||||
transform(NativeTransformer) {
|
||||
rootDir = project.rootDir
|
||||
|
||||
before {
|
||||
relocate "org.sqlite", "dh_sqlite", {
|
||||
exclude "org/sqlite/native/**"
|
||||
}
|
||||
relocate "jdbc:sqlite", "jdbc:dh_sqlite"
|
||||
}
|
||||
|
||||
transform(NativeTransformer) {
|
||||
rootDir = project.rootDir
|
||||
relocateNative "org/sqlite", "dh_sqlite"
|
||||
relocateNative "org_sqlite", "dh_1sqlite"
|
||||
}
|
||||
|
||||
@@ -12,14 +12,15 @@ class NativeRelocator
|
||||
/**
|
||||
* 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
|
||||
NativeRelocator(Path rootDirectory)
|
||||
{
|
||||
this.rootDirectory = rootDirectory;
|
||||
this.cacheRoot = this.rootDirectory.resolve("cache");
|
||||
}
|
||||
|
||||
private void prepare() throws Exception
|
||||
{
|
||||
if (this.rootDirectory.resolve(".venv").toFile().exists())
|
||||
{
|
||||
return;
|
||||
@@ -196,6 +197,9 @@ class NativeRelocator
|
||||
return Files.readAllBytes(outputFilePath);
|
||||
}
|
||||
|
||||
System.out.println("Relocating to " + outputPath + "...");
|
||||
this.prepare();
|
||||
|
||||
for (Map.Entry<String, String> replacement : replacements.entrySet())
|
||||
{
|
||||
this.replaceInNullTerminatedStrings(content, replacement.getKey(), replacement.getValue());
|
||||
|
||||
+47
-22
@@ -19,19 +19,16 @@
|
||||
|
||||
package com.seibel.distanthorizons.common.wrappers.block;
|
||||
|
||||
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
|
||||
import com.seibel.distanthorizons.core.util.ColorUtil;
|
||||
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.core.Direction;
|
||||
import net.minecraft.world.level.*;
|
||||
import net.minecraft.world.level.biome.Biome;
|
||||
import net.minecraft.world.level.biome.Biomes;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.level.lighting.LevelLightEngine;
|
||||
import net.minecraft.world.level.material.FluidState;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
@@ -46,30 +43,61 @@ public class TintWithoutLevelOverrider implements BlockAndTintGetter
|
||||
* but {@link Nullable} is there just in case.
|
||||
*/
|
||||
@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)
|
||||
{
|
||||
// try to get the wrapped biome
|
||||
Biome unwrappedBiome = null;
|
||||
if (biomeWrapper.biome != null)
|
||||
#if MC_VER >= MC_1_18_2 Holder<Biome> #else Biome #endif biome = biomeWrapper.biome;
|
||||
if (biome == null) // We are looking at the empty biome wrapper
|
||||
{
|
||||
unwrappedBiome = unwrap(biomeWrapper.biome);
|
||||
}
|
||||
|
||||
if(unwrappedBiome == null)
|
||||
{
|
||||
// we are looking at the empty biome wrapper, try using plains as a backup
|
||||
BiomeWrapper plainsBiomeWrapper = ((BiomeWrapper) clientLevelWrapper.getPlainsBiomeWrapper());
|
||||
if (plainsBiomeWrapper != null)
|
||||
{
|
||||
unwrappedBiome = unwrap(plainsBiomeWrapper.biome);
|
||||
biome = plainsBiomeWrapper.biome;
|
||||
}
|
||||
}
|
||||
|
||||
this.biome = unwrappedBiome;
|
||||
this.biome = biome;
|
||||
}
|
||||
|
||||
|
||||
@@ -77,15 +105,12 @@ public class TintWithoutLevelOverrider implements BlockAndTintGetter
|
||||
@Override
|
||||
public int getBlockTint(@NotNull BlockPos blockPos, @NotNull ColorResolver colorResolver)
|
||||
{
|
||||
if (this.biome != null)
|
||||
{
|
||||
return colorResolver.getColor(this.biome, blockPos.getX(), blockPos.getZ());
|
||||
}
|
||||
else
|
||||
if (this.biome == null)
|
||||
{
|
||||
// hopefully unneeded debug color
|
||||
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)
|
||||
|
||||
+2
@@ -115,6 +115,8 @@ public class ServerLevelWrapper implements IServerLevelWrapper
|
||||
@Override
|
||||
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
|
||||
return this.level.getServer().getWorldScreenshotFile().get().getParent().getFileName().toString();
|
||||
#else // <= 1.16.5
|
||||
|
||||
+1
-1
Submodule coreSubProjects updated: 3ed50e5134...09174c2d2a
@@ -90,8 +90,8 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
|
||||
{
|
||||
ModAccessorInjector.INSTANCE.bind(ISodiumAccessor.class, new SodiumAccessor());
|
||||
|
||||
// If sodium is installed Indium is also necessary in order to use the Fabric rendering API
|
||||
if (!modChecker.isModLoaded("indium"))
|
||||
// If sodium is installed Indium is also necessary for versions 0.5 and less in order to use the Fabric rendering API
|
||||
if (!modChecker.isModLoaded("indium") && SodiumAccessor.isSodiumV5OrLess)
|
||||
{
|
||||
String indiumMissingMessage = ModInfo.READABLE_NAME + " needs Indium to work with Sodium.\nPlease download Indium from https://modrinth.com/mod/indium";
|
||||
LOGGER.fatal(indiumMissingMessage);
|
||||
|
||||
+12
-2
@@ -43,11 +43,22 @@ import net.minecraft.world.phys.AABB;
|
||||
|
||||
public class SodiumAccessor implements ISodiumAccessor
|
||||
{
|
||||
/**
|
||||
* True if sodium 0.5 or less is present. <br>
|
||||
* This field is public because it's also used to check if we need Indium to be present. <br>
|
||||
* We need Indium if Sodium 0.5 or less is present.
|
||||
*/
|
||||
public static final boolean isSodiumV5OrLess;
|
||||
|
||||
#if MC_VER >= MC_1_20_1
|
||||
private static MethodHandle setFogOcclusionMethod;
|
||||
private static Object sodiumPerformanceOptions;
|
||||
#endif
|
||||
|
||||
static {
|
||||
isSodiumV5OrLess = !classPresent("net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//======================//
|
||||
@@ -72,8 +83,7 @@ public class SodiumAccessor implements ISodiumAccessor
|
||||
{
|
||||
if (sodiumPerformanceOptions == null)
|
||||
{
|
||||
boolean sodiumV6 = classPresent("net.caffeinemc.mods.sodium.client.render.SodiumWorldRenderer");
|
||||
if (!sodiumV6)
|
||||
if (isSodiumV5OrLess)
|
||||
{
|
||||
// sodium 0.5
|
||||
|
||||
|
||||
+2
-1
@@ -5,7 +5,7 @@ org.gradle.caching=true
|
||||
|
||||
# Mod Info
|
||||
mod_name=DistantHorizons
|
||||
mod_version=2.3.2-b
|
||||
mod_version=2.3.3-b-dev
|
||||
api_version=4.0.0
|
||||
maven_group=com.seibel.distanthorizons
|
||||
mod_readable_name=Distant Horizons
|
||||
@@ -23,6 +23,7 @@ manifold_version=2024.1.37
|
||||
nightconfig_version=3.6.6
|
||||
lz4_version=1.8.0
|
||||
xz_version=1.9
|
||||
# Before updating, read relocate_natives/README.md
|
||||
sqlite_jdbc_version=3.47.2.0
|
||||
# 8.2.1 is the newest version we can use since that's the version MC 1.16.5 uses
|
||||
fastutil_version=8.2.1
|
||||
|
||||
@@ -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
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user