Compare commits

...

29 Commits

Author SHA1 Message Date
James Seibel ead59d0817 remove dev from version number 2025-12-18 09:35:57 -06:00
James Seibel 1c9229c8f1 Fix 1.21.11 stuttering when flying into new chunks in singleplayer 2025-12-18 09:35:54 -06:00
James Seibel 968a14c6a5 remove chunkWrapper.isStillValid() 2025-12-18 09:35:21 -06:00
James Seibel 851c7439d5 Fix GLProxy error in multiplayer 2025-12-17 09:02:15 -06:00
s809 c902357a8f Update core 2025-12-17 00:17:27 +05:00
s809 63170078f5 Fix returning wrong dimension name 2025-12-17 00:17:21 +05:00
James Seibel d0dd1f125b ignore chunk update events during all world gen pos 2025-12-15 15:07:01 -06:00
James Seibel 32950d793e slight light engine optimization 2025-12-15 14:37:22 -06:00
James Seibel 54e9bad907 up version number 2.4.2 -> 2.4.3-dev 2025-12-15 10:17:34 -06:00
James Seibel bb4ac770bd remove dev from version number 2025-12-15 09:49:23 -06:00
James Seibel 16afada6e9 Fix inconsistency with server/client wrapper dim names 2025-12-15 09:49:23 -06:00
James Seibel 7d0785a5fa Fix dimension names missing namespace for multiplayer folders 2025-12-15 08:56:08 -06:00
James Seibel 6a67df462b Move GC warning into the log 2025-12-15 08:44:12 -06:00
s809 0c45c76ff8 Use a different path for zstd natives 2025-12-15 11:21:28 +05:00
James Seibel bcb442e38d Improve initial library check error handling 2025-12-14 22:29:22 -06:00
James Seibel 977ae471ea Fix auto update success dialog 2025-12-14 21:51:00 -06:00
James Seibel b1701ab0d0 remove iris unsupported error for neo 1.21.11 2025-12-14 21:20:22 -06:00
James Seibel c048d5cb56 hide LODs when underwater 2025-12-14 17:22:40 -06:00
James Seibel 2702f742d6 up version number 2.4.1 -> 2.4.2-dev 2025-12-14 17:00:44 -06:00
James Seibel 7add025c8a remove dev from version number 2025-12-14 13:46:24 -06:00
James Seibel 9dc220feb2 re-generate ZStd relocated cache
The previous versions appeared to be out of date, causing linker errors
2025-12-13 21:32:15 -06:00
s809 79955252c9 Add zstd to relocated natives & update sqlite 2025-12-14 04:03:19 +05:00
James Seibel bf13cc48a3 Print a warning if G1GC is used 2025-12-13 16:47:03 -06:00
James Seibel cf454c80d7 add Zstd decompress lib check in initalizer 2025-12-13 15:48:09 -06:00
James Seibel e6404cd882 remove double ";" in ForgeMain 2025-12-13 15:44:40 -06:00
James Seibel 52e64dc403 log if a mod accessor isn't added 2025-12-13 15:44:27 -06:00
James Seibel 7c858afc5d add partial oculus support 2025-12-13 13:17:43 -06:00
James Seibel a44a5d7465 replace client ticks with a timer
Prevents DH loading issues when MC ticks are paused
2025-12-13 11:19:39 -06:00
James Seibel f2d373b779 up version number 2.4.0 -> 2.4.1-dev 2025-12-13 10:20:53 -06:00
36 changed files with 193 additions and 84 deletions
+39 -8
View File
@@ -5,6 +5,9 @@ import org.apache.tools.zip.ZipEntry
import javax.annotation.Nonnull
import org.apache.tools.zip.ZipOutputStream
import java.util.function.Function
import java.util.function.Predicate
plugins {
id "java"
@@ -73,12 +76,23 @@ writeBuildGradlePredefine(rootProject.mcVers, rootProject.mcIndex)
rootProject.versionStr = rootProject.mod_version + "-" + rootProject.minecraft_version // + "-" + new Date().format("yyyy_MM_dd_HH_mm")
class NativeTransformer implements Transformer {
private Predicate<String> fileMatcher
private Function<String, String> filePathMapper
private final HashMap<String, String> replacements = new HashMap()
private final HashMap<String, byte[]> rewrittenFiles = new HashMap()
private var nativeRelocator
public File rootDir
void matchFiles(Predicate<String> matcher) {
fileMatcher = matcher
}
void mapPaths(Function<String, String> mapper) {
filePathMapper = mapper
}
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()}")
@@ -89,9 +103,7 @@ class NativeTransformer implements Transformer {
@Override
boolean canTransformResource(@Nonnull FileTreeElement element) {
return replacements.keySet().stream().anyMatch {
element.name.startsWith(it as String)
}
return fileMatcher.test(element.name)
}
@Override
@@ -103,11 +115,9 @@ class NativeTransformer implements Transformer {
}
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)
String path = filePathMapper != null
? filePathMapper.apply(context.path)
: context.path
content = nativeRelocator.processBinary(path, content, replacements)
rewrittenFiles.put(path, content)
@@ -351,9 +361,30 @@ subprojects { p ->
transform(NativeTransformer) {
rootDir = project.rootDir
matchFiles { it.startsWith("org/sqlite") }
mapPaths { it.replace("org/sqlite", "dh_sqlite") }
relocateNative "org/sqlite", "dh_sqlite"
relocateNative "org_sqlite", "dh_1sqlite"
}
// ZStd
// librariesLocation isn't used because it's too long for replacing paths in native libraries
// Allowing strings larger than the original string would require shifting the entire binary's contents
relocate "com.github.luben", "dhcomgithubluben"
relocate "libzstd-jni", "libzstd-jni_dh"
relocate "zstd-jni", "zstd-jni_dh"
transform(NativeTransformer) {
rootDir = project.rootDir
matchFiles { it.contains("libzstd-jni") && !it.contains("aix/ppc64") }
mapPaths { it.replace("libzstd-jni", "libzstd-jni_dh") }
relocateNative "com/github/luben", "dhcomgithubluben"
relocateNative "com_github_luben", "dhcomgithubluben"
}
// JOML
@@ -166,6 +166,10 @@ public abstract class AbstractModInitializer
//noinspection unchecked
ModAccessorInjector.INSTANCE.bind((Class<? extends IModAccessor>) accessorClass, accessorConstructor.get());
}
else
{
LOGGER.debug("Skipping mod compatibility accessor for: ["+modId+"]");
}
}
private void initConfig()
@@ -614,15 +614,6 @@ public class ChunkWrapper implements IChunkWrapper
//===============//
// other methods //
//===============//
@Override
public boolean isStillValid() { return this.wrappedLevel.tryGetChunk(this.chunkPos) == this; }
//================//
// base overrides //
//================//
@@ -203,19 +203,13 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
return;
}
if (!GLProxy.hasInstance())
{
// rendering setup hasn't finished
return;
}
#if MC_VER < MC_1_19_2
player.sendMessage(new TextComponent(string), getPlayer().getUUID());
#elif MC_VER < MC_1_21_9
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false);
#else
GLProxy.getInstance().queueRunningOnRenderThread(() ->
GLProxy.queueRunningOnRenderThread(() ->
{
player.displayClientMessage(net.minecraft.network.chat.Component.translatable(string), /*isOverlay*/false);
});
@@ -293,7 +287,7 @@ public class MinecraftClientWrapper implements IMinecraftClientWrapper, IMinecra
@Override
public void crashMinecraft(String errorMessage, Throwable exception)
{
LOGGER.error(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...", exception);
LOGGER.fatal(ModInfo.READABLE_NAME + " had the following error: [" + errorMessage + "]. Crashing Minecraft...", exception);
CrashReport report = new CrashReport(errorMessage, exception);
#if MC_VER < MC_1_20_4
Minecraft.crash(report);
@@ -259,7 +259,7 @@ public class ClientLevelWrapper implements IClientLevelWrapper
#if MC_VER <= MC_1_21_10
return this.level.dimension().location().toString();
#else
return this.level.dimension().identifier().getPath();
return this.level.dimension().identifier().toString();
#endif
}
@@ -49,6 +49,12 @@ import net.minecraft.world.level.chunk.ChunkStatus;
import net.minecraft.world.level.chunk.status.ChunkStatus;
#endif
#if MC_VER <= MC_1_21_10
#else
import net.minecraft.world.level.ChunkPos;
import net.minecraft.server.level.ChunkHolder;
#endif
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.Nullable;
@@ -187,7 +193,7 @@ public class ServerLevelWrapper implements IServerLevelWrapper
#if MC_VER <= MC_1_21_10
return this.level.dimension().location().toString();
#else
return this.level.dimension().identifier().getPath();
return this.level.dimension().identifier().toString();
#endif
}
@@ -226,6 +232,7 @@ public class ServerLevelWrapper implements IServerLevelWrapper
@Override
public IChunkWrapper tryGetChunk(DhChunkPos pos)
{
#if MC_VER < MC_1_21_11
if (!this.level.hasChunk(pos.getX(), pos.getZ()))
{
return null;
@@ -238,6 +245,24 @@ public class ServerLevelWrapper implements IServerLevelWrapper
}
return new ChunkWrapper(chunk, this);
#else
// directly hitting the chunkMap is required otherwise MC will run this on the main server thread,
// causing lag
ChunkHolder chunkHolder = this.level.getChunkSource().chunkMap.getVisibleChunkIfPresent(new ChunkPos(pos.getX(), pos.getZ()).toLong());
if (chunkHolder == null)
{
return null;
}
ChunkAccess chunk = chunkHolder.getChunkIfPresent(ChunkStatus.FULL);
if (chunk == null)
{
return null;
}
return new ChunkWrapper(chunk, this);
#endif
}
@Override
@@ -28,6 +28,7 @@ import com.seibel.distanthorizons.common.wrappers.world.ServerLevelWrapper;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.chunkFileHandling.ChunkFileReader;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.mimicObject.*;
import com.seibel.distanthorizons.common.wrappers.worldGeneration.params.GlobalWorldGenParams;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.generation.DhLightingEngine;
import com.seibel.distanthorizons.core.level.IDhServerLevel;
import com.seibel.distanthorizons.core.config.Config;
@@ -37,6 +38,7 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
import com.seibel.distanthorizons.core.util.ExceptionUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.util.gridList.ArrayGridList;
import com.seibel.distanthorizons.core.util.objects.UncheckedInterruptedException;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.ChunkLightStorage;
@@ -100,6 +102,15 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
public static final long EXCEPTION_TIMER_RESET_TIME = TimeUnit.NANOSECONDS.convert(1, TimeUnit.SECONDS);
public static final int EXCEPTION_COUNTER_TRIGGER = 20;
/**
* Used to revert the ignore logic in {@link SharedApi} so
* that a given chunk pos can be handled again.
* A timer is used so we don't have to inject into MC's code and it works sell enough
* most of the time.
* If a chunk does get through due the timeout not being long enough that isn't the end of the world.
*/
private static final int MS_TO_IGNORE_CHUNK_AFTER_COMPLETION = 5_000;
private final IDhServerLevel dhServerLevel;
@@ -107,6 +118,8 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
public final InternalServerGenerator internalServerGenerator;
public final ChunkFileReader chunkFileReader;
private final Timer chunkSaveIgnoreTimer = TimerUtil.CreateTimer("ChunkSaveIgnoreTimer");
public final LinkedBlockingQueue<GenerationEvent> generationEventQueue = new LinkedBlockingQueue<>();
@@ -575,6 +588,11 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
ProtoChunk protoChunk = ((ProtoChunk) chunk);
protoChunk.setLightEngine(region.getLightEngine());
}
// usually ignoring the chunk's position is unnecessary,
// but this improves performance if a chunk update event does sneak through
SharedApi.CHUNK_UPDATE_QUEUE_MANAGER.addPosToIgnore(chunkWrapper.getChunkPos());
});
@@ -684,6 +702,24 @@ public final class BatchGenerationEnvironment implements IBatchGeneratorEnvironm
this.dhServerLevel.updateBeaconBeamsForChunkPos(centerChunkWrapper.getChunkPos(), activeBeamList);
}
}
for (int i = 0; i < iChunkWrapperList.size(); i++)
{
ChunkWrapper chunkWrapper = (ChunkWrapper) iChunkWrapperList.get(i);
if (chunkWrapper == null)
{
continue;
}
// give MC a few seconds to save the chunk before
// we can process update events there again
this.chunkSaveIgnoreTimer.schedule(new TimerTask()
{
@Override
public void run() { SharedApi.CHUNK_UPDATE_QUEUE_MANAGER.removePosToIgnore(chunkWrapper.getChunkPos()); }
}, MS_TO_IGNORE_CHUNK_AFTER_COMPLETION);
}
}
}
private static <T> ArrayGridList<T> GetCutoutFrom(ArrayGridList<T> total, int border) { return new ArrayGridList<>(total, border, total.gridSize - border); }
@@ -51,7 +51,7 @@ public class InternalServerGenerator
/**
* Used to revert the ignore logic in {@link SharedApi} so
* that given chunk pos can be handled again.
* that a given chunk pos can be handled again.
* A timer is used so we don't have to inject into MC's code and it works sell enough
* most of the time.
* If a chunk does get through due the timeout not being long enough that isn't the end of the world.
@@ -24,6 +24,9 @@ accessible method net/minecraft/server/level/ChunkMap tick (Ljava/util/function/
accessible field net/minecraft/server/level/ServerLevel entityManager Lnet/minecraft/world/level/entity/PersistentEntitySectionManager;
accessible field net/minecraft/server/level/ChunkMap mainThreadExecutor Lnet/minecraft/util/thread/BlockableEventLoop;
# getting existing chunks outside the main thread
accessible method net/minecraft/server/level/ChunkMap getVisibleChunkIfPresent (J)Lnet/minecraft/server/level/ChunkHolder;
# lod generation from save file
accessible field net/minecraft/world/level/chunk/storage/SimpleRegionStorage worker Lnet/minecraft/world/level/chunk/storage/IOWorker;
accessible field net/minecraft/world/level/chunk/storage/IOWorker storage Lnet/minecraft/world/level/chunk/storage/RegionFileStorage;
@@ -113,14 +113,6 @@ public class FabricClientProxy implements AbstractModInitializer.IEventProxy
//=============//
// tick events //
//=============//
ClientTickEvents.START_CLIENT_TICK.register((client) -> { ClientApi.INSTANCE.clientTickEvent(); });
//==============//
// chunk events //
//==============//
@@ -25,6 +25,7 @@ import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.NativeDialogUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.*;
@@ -38,7 +39,6 @@ import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.MinecraftServer;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.util.tinyfd.TinyFileDialogs;
#if MC_VER >= MC_1_19_2
import net.fabricmc.fabric.api.command.v2.CommandRegistrationCallback;
@@ -103,7 +103,7 @@ public class FabricMain extends AbstractModInitializer implements ClientModIniti
String indiumMissingMessage = ModInfo.READABLE_NAME + " needs Indium to work with Sodium.\nPlease download Indium from https://modrinth.com/mod/indium";
LOGGER.fatal(indiumMissingMessage);
TinyFileDialogs.tinyfd_messageBox(ModInfo.READABLE_NAME, indiumMissingMessage, "ok", "error", false);
NativeDialogUtil.showDialog(ModInfo.READABLE_NAME, indiumMissingMessage, "ok", "error");
IMinecraftClientWrapper mc = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
String errorMessage = "loading Distant Horizons. Distant Horizons requires Indium in order to run with Sodium.";
@@ -107,21 +107,6 @@ public class ForgeClientProxy implements AbstractModInitializer.IEventProxy
//=============//
// tick events //
//=============//
@SubscribeEvent
public void clientTickEvent(TickEvent.ClientTickEvent event)
{
if (event.phase == TickEvent.Phase.START)
{
ClientApi.INSTANCE.clientTickEvent();
}
}
//==============//
// world events //
//==============//
@@ -22,17 +22,16 @@ package com.seibel.distanthorizons.forge;
import com.mojang.brigadier.CommandDispatcher;
import com.seibel.distanthorizons.common.AbstractModInitializer;
import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.common.wrappers.gui.GetConfigScreen;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IPluginPacketSender;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IModChecker;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor;
import com.seibel.distanthorizons.forge.wrappers.modAccessor.ModChecker;
import com.seibel.distanthorizons.forge.wrappers.modAccessor.OptifineAccessor;
import com.seibel.distanthorizons.forge.wrappers.modAccessor.OculusAccessor;
import net.minecraft.commands.CommandSourceStack;
import net.minecraft.server.MinecraftServer;
@@ -104,6 +103,7 @@ public class ForgeMain extends AbstractModInitializer
protected void initializeModCompat()
{
this.tryCreateModCompatAccessor("optifine", IOptifineAccessor.class, OptifineAccessor::new);
this.tryCreateModCompatAccessor("oculus", IIrisAccessor.class, OculusAccessor::new);
#if MC_VER < MC_1_17_1
ModLoadingContext.get().registerExtensionPoint(ExtensionPoint.CONFIGGUIFACTORY,
@@ -0,0 +1,57 @@
/*
* This file is part of the Distant Horizons mod
* licensed under the GNU LGPL v3 License.
*
* Copyright (C) 2020 James Seibel
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, version 3.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package com.seibel.distanthorizons.forge.wrappers.modAccessor;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor;
public class OculusAccessor implements IIrisAccessor
{
protected static final DhLogger LOGGER = new DhLoggerBuilder().build();
public OculusAccessor()
{
LOGGER.warn("Partial Oculus support enabled. Some DH features may be disabled or behave strangely, use Iris instead if possible.");
}
@Override
public String getModName()
{
return "oculus";
}
@Override
public boolean isShaderPackInUse()
{
// assume shaders are always active
return true;
}
@Override
public boolean isRenderingShadowPass()
{
return false;
}
}
+1 -1
View File
@@ -5,7 +5,7 @@ org.gradle.caching=true
# Mod Info
mod_name=DistantHorizons
mod_version=2.4.0-b
mod_version=2.4.3-b
api_version=5.0.0
maven_group=com.seibel.distanthorizons
mod_readable_name=Distant Horizons
@@ -73,29 +73,6 @@ public class NeoforgeClientProxy implements AbstractModInitializer.IEventProxy
//=============//
// tick events //
//=============//
#if MC_VER < MC_1_20_6
@SubscribeEvent
public void clientTickEvent(TickEvent.ClientTickEvent event)
{
if (event.phase == TickEvent.Phase.START)
{
ClientApi.INSTANCE.clientTickEvent();
}
}
#else
@SubscribeEvent
public void clientTickEvent(ClientTickEvent.Pre event)
{
ClientApi.INSTANCE.clientTickEvent();
}
#endif
//==============//
// world events //
//==============//
@@ -33,9 +33,9 @@ public class IrisAccessor implements IIrisAccessor
{
public IrisAccessor()
{
#if MC_VER == MC_1_21_11
throw new UnsupportedOperationException("Iris isn't supported on this version of DH. When this version of DH was created Iris wasn't available for Neoforge yet.");
#endif
//#if MC_VER == MC_1_21_11
//throw new UnsupportedOperationException("Iris isn't supported on this version of DH. When this version of DH was created Iris wasn't available for Neoforge yet.");
//#endif
}
+14
View File
@@ -12,15 +12,29 @@ How to add a library's natives:
Example:
```groovy
// Relocate the namespace (Java side)
relocate "org.sqlite", "dh_sqlite", {
// (Specific to SQLite's relocation)
// Make sure that native paths are not changed before steps below
exclude "org/sqlite/native/**"
}
// Shadow also replaces strings inside the Java code
// See the library's source code to find strings used to call into the native code
// This also includes native library paths, if you use mapPaths {} below they will likely need adjustment as well
relocate "jdbc:sqlite", "jdbc:dh_sqlite"
transform(NativeTransformer) {
// NativeTransformer configuration
rootDir = project.rootDir
// Match native libraries
matchFiles { it.startsWith("org/sqlite") }
// Replace paths with ones that won't overlap with other mods
// Libraries are the ones choosing the path to use for natives; check the source code to see which paths are acceptable.
mapPaths { it.replace("org/sqlite", "dh_sqlite") }
// Replace native strings, e.g. used in calls back to Java
// They must be of the same length or shorter!
relocateNative "org/sqlite", "dh_sqlite"
// Rename native methods used when calling from Java
relocateNative "org_sqlite", "dh_1sqlite"
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.