Compare commits

...

16 Commits

Author SHA1 Message Date
James Seibel aabb90ada6 remove dev from version number 2025-12-15 09:00:15 -06:00
James Seibel 963a8dc53f comment LevelWrapper getDimensionName() 2025-12-15 08:55:40 -06:00
James Seibel aa6d69385b Move GC warning into the log 2025-12-15 08:44:06 -06:00
James Seibel f42c9cf8fb Improve initial library check error handling 2025-12-14 22:29:08 -06:00
James Seibel 92e0011c8d Fix auto update success dialog 2025-12-14 21:50:56 -06:00
James Seibel c20d95a7c7 improve spacing for self updater version log 2025-12-14 21:21:45 -06:00
James Seibel 353aa1ed2c maybe improve ZStd version check 2025-12-14 21:20:42 -06:00
James Seibel 5aa43ebcc8 hide LODs when underwater 2025-12-14 17:22:35 -06:00
James Seibel b6145461b6 add note to ignored block CSV 2025-12-14 17:02:53 -06:00
James Seibel 478e431076 up version number 2.4.1 -> 2.4.2-dev 2025-12-14 17:00:34 -06:00
James Seibel 6feb7f1b42 remove dev from version number 2025-12-14 13:46:04 -06:00
James Seibel 016fc66293 Print a warning if G1GC is used
G1GC is known to cause stuttering
2025-12-13 16:46:59 -06:00
James Seibel 6d3e30d425 add Zstd decompress lib check in initalizer 2025-12-13 15:48:05 -06:00
James Seibel 5be5c5a5bc replace client ticks with a timer
Prevents DH loading issues when MC ticks are paused
2025-12-13 11:19:33 -06:00
James Seibel ed5aeb8951 minor texture setup reformatting 2025-12-13 10:43:01 -06:00
James Seibel 7f0ddadf26 up version number 2.4.0 -> 2.4.1-dev 2025-12-13 10:20:44 -06:00
17 changed files with 209 additions and 235 deletions
@@ -38,7 +38,7 @@ public final class ModInfo
public static final String NAME = "DistantHorizons"; public static final String NAME = "DistantHorizons";
/** Human-readable version of NAME */ /** Human-readable version of NAME */
public static final String READABLE_NAME = "Distant Horizons"; public static final String READABLE_NAME = "Distant Horizons";
public static final String VERSION = "2.4.0-b"; public static final String VERSION = "2.4.2-b";
/** Returns true if the current build is an unstable developer build, false otherwise. */ /** Returns true if the current build is an unstable developer build, false otherwise. */
public static final boolean IS_DEV_BUILD = VERSION.toLowerCase().contains("dev"); public static final boolean IS_DEV_BUILD = VERSION.toLowerCase().contains("dev");
@@ -20,36 +20,41 @@
package com.seibel.distanthorizons.core; package com.seibel.distanthorizons.core;
import com.github.luben.zstd.ZstdOutputStream; import com.github.luben.zstd.ZstdOutputStream;
import com.seibel.distanthorizons.core.config.Config;
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.render.renderer.generic.GenericRenderObjectFactory; import com.seibel.distanthorizons.core.render.renderer.generic.GenericRenderObjectFactory;
import com.seibel.distanthorizons.core.sql.DatabaseUpdater; import com.seibel.distanthorizons.core.sql.DatabaseUpdater;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.world.DhApiWorldProxy; import com.seibel.distanthorizons.core.world.DhApiWorldProxy;
import com.seibel.distanthorizons.core.api.external.methods.config.DhApiConfig; import com.seibel.distanthorizons.core.api.external.methods.config.DhApiConfig;
import com.seibel.distanthorizons.core.api.external.methods.data.DhApiTerrainDataRepo; import com.seibel.distanthorizons.core.api.external.methods.data.DhApiTerrainDataRepo;
import com.seibel.distanthorizons.api.DhApi; import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.core.render.DhApiRenderProxy; import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.coreapi.util.StringUtil;
import net.jpountz.lz4.LZ4FrameOutputStream; import net.jpountz.lz4.LZ4FrameOutputStream;
import org.apache.logging.log4j.LogManager;
import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.sqlite.SQLiteJDBCLoader; import org.sqlite.SQLiteJDBCLoader;
import org.sqlite.util.OSInfo;
import org.tukaani.xz.XZOutputStream; import org.tukaani.xz.XZOutputStream;
import java.awt.*; import java.lang.management.GarbageCollectorMXBean;
import java.io.File; import java.lang.management.ManagementFactory;
import java.util.List;
/** Handles first time Core setup. */ /** Handles first time Core setup. */
public class Initializer public class Initializer
{ {
private static final DhLogger LOGGER = new DhLoggerBuilder().build(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
public static void init() public static void init()
{ {
LOGGER.info("Running library validation...");
// confirm that all referenced libraries are available to use // confirm that all referenced libraries are available to use
try try
{ {
@@ -57,6 +62,17 @@ public class Initializer
// will throw an error (not an exception) // will throw an error (not an exception)
Class<?> lz4Compressor = LZ4FrameOutputStream.class; Class<?> lz4Compressor = LZ4FrameOutputStream.class;
Class<?> zstdCompressor = ZstdOutputStream.class; Class<?> zstdCompressor = ZstdOutputStream.class;
{
byte[] testCompressByteArray = new byte[1024];
for (int i = 0; i < testCompressByteArray.length; i++)
{
testCompressByteArray[i] = (byte) (i % 126);
}
byte[] compressedBytes = com.github.luben.zstd.Zstd.compress(testCompressByteArray);
com.github.luben.zstd.Zstd.decompress(compressedBytes);
}
Class<?> lzmaCompressor = XZOutputStream.class; Class<?> lzmaCompressor = XZOutputStream.class;
//Class<?> networking = ByteBuf.class; //Class<?> networking = ByteBuf.class;
Class<?> config = com.electronwill.nightconfig.core.Config.class; Class<?> config = com.electronwill.nightconfig.core.Config.class;
@@ -73,9 +89,7 @@ public class Initializer
} }
catch (Throwable e) catch (Throwable e)
{ {
LOGGER.fatal("Critical programmer error: One or more libraries aren't present. Error: [" + e.getMessage() + "].", e); MC_CLIENT.crashMinecraft("Distant Horizons critical setup error: One or more libraries are either in-accessible, corrupted, or overwritten by another mod. Error: [" + e.getMessage() + "].", e);
// throwing here should crash the game, notifying the developer that something is wrong
throw new RuntimeException(e);
} }
// confirm the resource directory is present // confirm the resource directory is present
@@ -89,8 +103,7 @@ public class Initializer
} }
catch (Exception e) catch (Exception e)
{ {
LOGGER.fatal("Critical programmer error: Can't read SQL Scripts resource folder is either missing or malformed. Error: [" + e.getMessage() + "]."); MC_CLIENT.crashMinecraft("Critical programmer error: Can't read SQL Scripts resource folder is either missing or malformed. Error: [" + e.getMessage() + "].", e);
throw new RuntimeException(e);
} }
// This code has been disabled since it can cause Mac // This code has been disabled since it can cause Mac
@@ -121,6 +134,43 @@ public class Initializer
LOGGER.error("Programmer Error: No ["+IWrapperFactory.class.getSimpleName()+"] assigned to the DhApi."); LOGGER.error("Programmer Error: No ["+IWrapperFactory.class.getSimpleName()+"] assigned to the DhApi.");
} }
// log a warning if G1GC is being used
// (this garbage collector is known to cause stuttering)
{
boolean g1GcInUse = false;
StringBuilder garbageCollectorNames = new StringBuilder();
List<GarbageCollectorMXBean> gcMxBeans = ManagementFactory.getGarbageCollectorMXBeans();
for (GarbageCollectorMXBean gcMxBean : gcMxBeans)
{
if (!garbageCollectorNames.toString().isEmpty())
{
garbageCollectorNames.append(", ");
}
garbageCollectorNames.append(gcMxBean.getName());
// "G1 Young Generation" // "G1 Concurrent GC" // "G1 Old Generation"
if (gcMxBean.getName().toLowerCase().contains("g1 "))
{
g1GcInUse = true;
}
}
LOGGER.info("Garbage collectors: ["+garbageCollectorNames+"]");
if (g1GcInUse
&& Config.Common.Logging.Warning.logGarbageCollectorWarning.get())
{
LOGGER.warn(
"Distant Horizons: G1 Garbage collector detected. \n" +
"This garbage collector can cause FPS stuttering. \n" +
"It's recommended to use a concurrent garbage collector \n" +
"like ZGC (Java 21+) or Shenandoah (Java 8 through 17) for a smoother experience. \n" +
"");
}
}
} }
} }
@@ -56,6 +56,8 @@ import org.jetbrains.annotations.Nullable;
import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFW;
import java.io.File; import java.io.File;
import java.lang.management.GarbageCollectorMXBean;
import java.lang.management.ManagementFactory;
import java.util.*; import java.util.*;
import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.LinkedBlockingQueue;
@@ -93,6 +95,7 @@ public class ClientApi
private boolean isDevBuildMessagePrinted = false; private boolean isDevBuildMessagePrinted = false;
private boolean lowMemoryWarningPrinted = false; private boolean lowMemoryWarningPrinted = false;
private boolean highVanillaRenderDistanceWarningPrinted = false; private boolean highVanillaRenderDistanceWarningPrinted = false;
private boolean g1GarbageCollectorWarningPrinted = false;
private long lastStaticWarningMessageSentMsTime = 0L; private long lastStaticWarningMessageSentMsTime = 0L;
@@ -318,35 +321,6 @@ public class ClientApi
//============//
// clint tick //
//============//
@Deprecated
public void clientTickEvent()
{
IProfilerWrapper profiler = MC_CLIENT.getProfiler();
profiler.push("DH-ClientTick");
try
{
IDhClientWorld clientWorld = SharedApi.tryGetDhClientWorld();
if (clientWorld != null)
{
clientWorld.clientTick();
}
}
catch (Exception e)
{
// handle errors here to prevent blowing up a mixin or API up stream
LOGGER.error("Unexpected error in ClientApi.clientTickEvent(), error: "+e.getMessage(), e);
}
profiler.pop();
}
//============// //============//
// networking // // networking //
//============// //============//
@@ -674,7 +648,8 @@ public class ClientApi
{ {
// dev build // dev build
if (ModInfo.IS_DEV_BUILD if (ModInfo.IS_DEV_BUILD
&& !this.isDevBuildMessagePrinted && MC_CLIENT.playerExists()) && !this.isDevBuildMessagePrinted
&& MC_CLIENT.playerExists())
{ {
this.isDevBuildMessagePrinted = true; this.isDevBuildMessagePrinted = true;
this.lastStaticWarningMessageSentMsTime = System.currentTimeMillis(); this.lastStaticWarningMessageSentMsTime = System.currentTimeMillis();
@@ -720,10 +695,11 @@ public class ClientApi
if (!this.highVanillaRenderDistanceWarningPrinted if (!this.highVanillaRenderDistanceWarningPrinted
&& Config.Common.Logging.Warning.showHighVanillaRenderDistanceWarning.get()) && Config.Common.Logging.Warning.showHighVanillaRenderDistanceWarning.get())
{ {
this.highVanillaRenderDistanceWarningPrinted = true;
// DH generally doesn't need a vanilla render distance above 12 // DH generally doesn't need a vanilla render distance above 12
if (MC_RENDER.getRenderDistance() > 12) if (MC_RENDER.getRenderDistance() > 12)
{ {
this.highVanillaRenderDistanceWarningPrinted = true;
this.lastStaticWarningMessageSentMsTime = System.currentTimeMillis(); this.lastStaticWarningMessageSentMsTime = System.currentTimeMillis();
String message = String message =
@@ -33,13 +33,12 @@ import com.seibel.distanthorizons.core.config.types.enums.*;
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.pos.DhSectionPos; import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.util.NativeDialogUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.util.tinyfd.TinyFileDialogs;
import java.awt.*;
import java.io.File; import java.io.File;
import java.util.*; import java.util.*;
import java.util.List; import java.util.List;
@@ -786,6 +785,11 @@ public class Config
+ "A comma separated list of block resource locations that won't be rendered by DH. \n" + "A comma separated list of block resource locations that won't be rendered by DH. \n"
+ "Air is always included in this list. \n" + "Air is always included in this list. \n"
+ "Requires a restart to change. \n" + "Requires a restart to change. \n"
+ "\n"
+ "Note:\n"
+ "If you see gaps, or holes you may have to change\n"
+ "worldCompression to ["+EDhApiWorldCompressionMode.MERGE_SAME_BLOCKS+"] and re-generate the LODs.\n"
+ "Black spots may happen occur to block lighting being zero for covered blocks.\n"
+ "") + "")
.build(); .build();
@@ -1243,23 +1247,17 @@ public class Config
.setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) // no GUI renderer set up currently .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) // no GUI renderer set up currently
.build(); .build();
public static ConfigUIButton uiButtonTest = new ConfigUIButton(() -> public static ConfigUIButton uiButtonTest = new ConfigUIButton(() ->
{ {
// running on a separate thread is necessary to prevent locking // running on a separate thread is necessary to prevent locking
new Thread(() -> new Thread(() -> onButtonPressed()).start();
{
if (!GraphicsEnvironment.isHeadless())
{
LOGGER.info("Attempting to show tinyfd message box...");
boolean buttonPress = TinyFileDialogs.tinyfd_messageBox("Button pressed!", "UITester dialog", "ok", "info", false);
LOGGER.info("dialog returned with ["+(buttonPress ? "TRUE" : "FALSE")+"]");
}
else
{
LOGGER.info("button pressed!");
}
}).start();
}); });
public static void onButtonPressed()
{
LOGGER.info("Attempting to show tinyfd message box...");
boolean buttonPress = NativeDialogUtil.showDialog("Button pressed!", "UITester dialog", "ok", "info");
LOGGER.info("dialog returned with ["+(buttonPress ? "TRUE" : "FALSE")+"]");
}
public static ConfigCategory categoryTest = new ConfigCategory.Builder().set(CategoryTest.class).build(); public static ConfigCategory categoryTest = new ConfigCategory.Builder().set(CategoryTest.class).build();
@@ -1661,6 +1659,15 @@ public class Config
+ "") + "")
.build(); .build();
public static ConfigEntry<Boolean> logGarbageCollectorWarning = new ConfigEntry.Builder<Boolean>()
.set(true)
.comment(""
+ "If enabled, a message will be logged if the garbage \n"
+ "collector Java is currently using is known \n"
+ "to cause stutters and/or issues. \n"
+ "")
.build();
} }
} }
@@ -23,6 +23,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO;
import com.seibel.distanthorizons.core.sql.repo.FullDataSourceV2Repo; import com.seibel.distanthorizons.core.sql.repo.FullDataSourceV2Repo;
import com.seibel.distanthorizons.core.util.NativeDialogUtil;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.jar.gui.BaseJFrame; import com.seibel.distanthorizons.core.jar.gui.BaseJFrame;
import com.seibel.distanthorizons.core.jar.gui.cusomJObject.JBox; import com.seibel.distanthorizons.core.jar.gui.cusomJObject.JBox;
@@ -31,7 +32,6 @@ import com.seibel.distanthorizons.core.jar.installer.WebDownloader;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.apache.logging.log4j.core.LoggerContext; import org.apache.logging.log4j.core.LoggerContext;
import org.lwjgl.util.tinyfd.TinyFileDialogs;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@@ -441,7 +441,7 @@ public class JarMain
installMod.addActionListener(e -> { installMod.addActionListener(e -> {
if (minecraftDirPop.getSelectedFile() == null) if (minecraftDirPop.getSelectedFile() == null)
{ {
TinyFileDialogs.tinyfd_messageBox(ModInfo.READABLE_NAME, "Please select your install directory", "ok", "warning", false); NativeDialogUtil.showDialog(ModInfo.READABLE_NAME, "Please select your install directory", "ok", "warning");
return; return;
} }
@@ -455,11 +455,11 @@ public class JarMain
ModInfo.NAME + "-" + ModrinthGetter.releaseNames.get(downloadID.get()) + ".jar" ModInfo.NAME + "-" + ModrinthGetter.releaseNames.get(downloadID.get()) + ".jar"
).toFile()); ).toFile());
TinyFileDialogs.tinyfd_messageBox(ModInfo.READABLE_NAME, "Installation done. \nYou can now close the installer", "ok", "info", false); NativeDialogUtil.showDialog(ModInfo.READABLE_NAME, "Installation done. \nYou can now close the installer", "ok", "info");
} }
catch (Exception f) catch (Exception f)
{ {
TinyFileDialogs.tinyfd_messageBox(ModInfo.READABLE_NAME, "Download failed. Check your internet connection \nStacktrace: " + f.getMessage(), "error", "info", false); NativeDialogUtil.showDialog(ModInfo.READABLE_NAME, "Download failed. Check your internet connection \nStacktrace: " + f.getMessage(), "error", "info");
} }
}); });
frame.add(installMod); frame.add(installMod);
@@ -29,12 +29,12 @@ import com.seibel.distanthorizons.core.jar.installer.ModrinthGetter;
import com.seibel.distanthorizons.core.jar.installer.WebDownloader; import com.seibel.distanthorizons.core.jar.installer.WebDownloader;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.util.NativeDialogUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants; import com.seibel.distanthorizons.core.wrapperInterfaces.IVersionConstants;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.coreapi.util.StringUtil; import com.seibel.distanthorizons.coreapi.util.StringUtil;
import com.seibel.distanthorizons.coreapi.util.jar.DeleteOnUnlock; import com.seibel.distanthorizons.coreapi.util.jar.DeleteOnUnlock;
import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.lwjgl.util.tinyfd.TinyFileDialogs;
import javax.swing.*; import javax.swing.*;
import java.awt.*; import java.awt.*;
@@ -174,7 +174,7 @@ public class SelfUpdater
if (!GitlabGetter.INSTANCE.getDownloads(pipeline.get("id")).containsKey(mcVersion)) if (!GitlabGetter.INSTANCE.getDownloads(pipeline.get("id")).containsKey(mcVersion))
{ {
LOGGER.warn("Minecraft version ["+ mcVersion +"] is not findable on Gitlab, findable versions are ["+ StringUtil.join(",", GitlabGetter.INSTANCE.getDownloads(pipeline.get("id")).keySet().toArray()) +"]."); LOGGER.warn("Minecraft version ["+ mcVersion +"] is not findable on Gitlab, findable versions are ["+ StringUtil.join(", ", GitlabGetter.INSTANCE.getDownloads(pipeline.get("id")).keySet().toArray()) +"].");
return false; return false;
} }
@@ -258,14 +258,13 @@ public class SelfUpdater
deleteOldJarOnJvmShutdown = true; deleteOldJarOnJvmShutdown = true;
// TODO one of these messages contains something TinyFd doesn't like, find it and fix it String successMessage = "Distant Horizons successfully updated. It will apply on game`s relaunch";
String successMessage = "Distant Horizons successfully updated. It will apply on game's relaunch";
LOGGER.info(successMessage); LOGGER.info(successMessage);
new Thread(() -> new Thread(() ->
{ {
try try
{ {
TinyFileDialogs.tinyfd_messageBox(ModInfo.READABLE_NAME, successMessage, "ok", "info", false); NativeDialogUtil.showDialog(ModInfo.READABLE_NAME, successMessage, "ok", "info");
} }
catch (Exception ignore) { } catch (Exception ignore) { }
}).start(); }).start();
@@ -288,7 +287,7 @@ public class SelfUpdater
LOGGER.error(failMessage, e); LOGGER.error(failMessage, e);
try try
{ {
TinyFileDialogs.tinyfd_messageBox(ModInfo.READABLE_NAME, failMessage, "ok", "error", false); NativeDialogUtil.showDialog(ModInfo.READABLE_NAME, failMessage, "ok", "error");
} }
catch (Exception ignore) { } catch (Exception ignore) { }
@@ -386,7 +385,7 @@ public class SelfUpdater
{ {
try try
{ {
TinyFileDialogs.tinyfd_messageBox(ModInfo.READABLE_NAME, successMessage, "ok", "info", false); NativeDialogUtil.showDialog(ModInfo.READABLE_NAME, successMessage, "ok", "info");
} }
catch (Exception ignore) { } catch (Exception ignore) { }
}).start(); }).start();
@@ -424,7 +423,7 @@ public class SelfUpdater
LOGGER.error(failMessage, e); LOGGER.error(failMessage, e);
try try
{ {
TinyFileDialogs.tinyfd_messageBox(ModInfo.READABLE_NAME, failMessage, "ok", "error", false); NativeDialogUtil.showDialog(ModInfo.READABLE_NAME, failMessage, "ok", "error");
} }
catch (Exception ignore) { } catch (Exception ignore) { }
@@ -96,16 +96,19 @@ public class DhFadeRenderer
} }
this.fadeTexture = GL32.glGenTextures(); this.fadeTexture = GL32.glGenTextures();
GLMC.glBindTexture(this.fadeTexture); {
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16, width, height, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_SHORT_4_4_4_4, (ByteBuffer) null); GLMC.glBindTexture(this.fadeTexture);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR); GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16, width, height, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_SHORT_4_4_4_4, (ByteBuffer) null);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR); GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR);
// disable mip-mapping since DH is just going to draw straight to the screen
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0); // disable mip-mapping since DH is just going to draw straight to the screen
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0); GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0);
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0);
}
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.fadeTexture, 0); GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.fadeTexture, 0);
} }
@@ -88,15 +88,17 @@ public class FogRenderer
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fogFramebuffer); GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.fogFramebuffer);
this.fogTexture = GLMC.glGenTextures(); this.fogTexture = GLMC.glGenTextures();
GLMC.glBindTexture(this.fogTexture); {
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16, width, height, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_SHORT_4_4_4_4, (ByteBuffer) null); GLMC.glBindTexture(this.fogTexture);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR); GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_RGBA16, width, height, 0, GL32.GL_RGBA, GL32.GL_UNSIGNED_SHORT_4_4_4_4, (ByteBuffer) null);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR); GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR);
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.fogTexture, 0); GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR);
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.fogTexture, 0);
// disable mip-mapping since DH is just going to draw straight to the screen
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0); // disable mip-mapping since DH is just going to draw straight to the screen
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0); GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0);
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0);
}
} }
@@ -88,14 +88,16 @@ public class SSAORenderer
GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.ssaoFramebuffer); GLMC.glBindFramebuffer(GL32.GL_FRAMEBUFFER, this.ssaoFramebuffer);
this.ssaoTexture = GLMC.glGenTextures(); this.ssaoTexture = GLMC.glGenTextures();
GLMC.glBindTexture(this.ssaoTexture); {
GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_R16F, width, height, 0, GL32.GL_RED, GL32.GL_HALF_FLOAT, (ByteBuffer) null); GLMC.glBindTexture(this.ssaoTexture);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR); GL32.glTexImage2D(GL32.GL_TEXTURE_2D, 0, GL32.GL_R16F, width, height, 0, GL32.GL_RED, GL32.GL_HALF_FLOAT, (ByteBuffer) null);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR); GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MIN_FILTER, GL32.GL_LINEAR);
GL32.glTexParameteri(GL32.GL_TEXTURE_2D, GL32.GL_TEXTURE_MAG_FILTER, GL32.GL_LINEAR);
// disable mip-mapping since DH is just going to draw straight to the screen
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0); // disable mip-mapping since DH is just going to draw straight to the screen
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0); GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_BASE_LEVEL, 0);
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0);
}
GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.ssaoTexture, 0); GL32.glFramebufferTexture2D(GL32.GL_FRAMEBUFFER, GL32.GL_COLOR_ATTACHMENT0, GL32.GL_TEXTURE_2D, this.ssaoTexture, 0);
} }
@@ -178,6 +178,14 @@ public class FogShader extends AbstractShaderRenderer
float farFogMax = Config.Client.Advanced.Graphics.Fog.farFogMax.get().floatValue(); float farFogMax = Config.Client.Advanced.Graphics.Fog.farFogMax.get().floatValue();
float farFogDensity = Config.Client.Advanced.Graphics.Fog.farFogDensity.get().floatValue(); float farFogDensity = Config.Client.Advanced.Graphics.Fog.farFogDensity.get().floatValue();
// override fog if underwater
if (MC_RENDER.isFogStateSpecial())
{
// hide everything behind fog
farFogStart = 0.0f;
farFogEnd = 0.0f;
}
this.shader.setUniform(this.uFarFogStart, farFogStart); this.shader.setUniform(this.uFarFogStart, farFogStart);
this.shader.setUniform(this.uFarFogLength, farFogEnd - farFogStart); this.shader.setUniform(this.uFarFogLength, farFogEnd - farFogStart);
this.shader.setUniform(this.uFarFogMin, farFogMin); this.shader.setUniform(this.uFarFogMin, farFogMin);
@@ -0,0 +1,30 @@
package com.seibel.distanthorizons.core.util;
import org.lwjgl.util.tinyfd.TinyFileDialogs;
/**
* Should be used instead of the direct call to {@link TinyFileDialogs}
* so we can run additional validation and/or string cleanup.
* Otherwise, we may get error messages back.
*
* @see TinyFileDialogs
*/
public class NativeDialogUtil
{
/**
* @param dialogType the dialog type. One of:<br><table><tr><td>"ok"</td><td>"okcancel"</td><td>"yesno"</td><td>"yesnocancel"</td></tr></table>
* @param iconType the icon type. One of:<br><table><tr><td>"info"</td><td>"warning"</td><td>"error"</td><td>"question"</td></tr></table>
*/
public static boolean showDialog(String title, String message, String dialogType, String iconType)
{
// Tinyfd doesn't support the following characters, attempting to display them will cause the message
// to be replaced with an error message
String unsafeCharsRegex = "['\"`]";
title = title.replaceAll(unsafeCharsRegex, "`");
message = message.replaceAll(unsafeCharsRegex, "`");
return TinyFileDialogs.tinyfd_messageBox(title, message, dialogType, iconType, false);
}
}
@@ -1,102 +0,0 @@
/*
* 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.core.util.objects;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.logging.DhLogger;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import java.util.concurrent.ExecutorService;
public class EventLoop implements AutoCloseable
{
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private final boolean PAUSE_ON_ERROR = ModInfo.IS_DEV_BUILD;
private final ExecutorService executorService;
private final Runnable runnable;
/** the future related to the given runnable */
private CompletableFuture<Void> runnableFuture;
private boolean isRunning = true;
public EventLoop(ExecutorService executorService, Runnable runnable)
{
this.executorService = executorService;
this.runnable = runnable;
}
public void tick()
{
if (runnableFuture != null && runnableFuture.isDone())
{
try
{
runnableFuture.join();
}
catch (CompletionException ce)
{
LOGGER.error("Uncaught exception in event loop", ce.getCause());
if (PAUSE_ON_ERROR)
{
isRunning = false;
}
}
catch (Exception e)
{
LOGGER.error("Exception in event loop", e);
if (PAUSE_ON_ERROR)
{
isRunning = false;
}
}
finally
{
runnableFuture = null;
}
}
if (runnableFuture == null && isRunning)
{
runnableFuture = CompletableFuture.runAsync(runnable, executorService);
}
}
public void close()
{
if (runnableFuture != null)
{
runnableFuture.cancel(true);
}
runnableFuture = null;
executorService.shutdown();
}
public boolean isRunning() { return runnableFuture != null && !runnableFuture.isDone(); }
}
@@ -21,27 +21,21 @@ package com.seibel.distanthorizons.core.world;
import com.seibel.distanthorizons.core.api.internal.ClientApi; import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.level.DhClientServerLevel; import com.seibel.distanthorizons.core.level.DhClientServerLevel;
import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.util.objects.EventLoop;
import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.LodUtil;
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 com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.*;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLevel> implements IDhClientWorld public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLevel> implements IDhClientWorld
{ {
private final Set<DhClientServerLevel> dhLevels = Collections.synchronizedSet(new HashSet<>()); private final Set<DhClientServerLevel> dhLevels = Collections.synchronizedSet(new HashSet<>());
public ExecutorService dhTickerThread = ThreadUtil.makeSingleThreadPool("Client Server World Ticker", 2); private final Timer clientTickTimer = TimerUtil.CreateTimer("ClientTickTimer");
public EventLoop eventLoop = new EventLoop(this.dhTickerThread, this::_clientTick); //TODO: Rate-limit the loop
@@ -53,6 +47,15 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
{ {
super(EWorldEnvironment.CLIENT_SERVER); super(EWorldEnvironment.CLIENT_SERVER);
LOGGER.info("Started DhWorld of type " + this.environment); LOGGER.info("Started DhWorld of type " + this.environment);
this.clientTickTimer.scheduleAtFixedRate(new TimerTask()
{
@Override
public void run()
{
DhClientServerWorld.this.dhLevels.forEach(DhClientServerLevel::clientTick);
}
}, 0, IDhClientWorld.TICK_RATE_IN_MS);
} }
@@ -136,19 +139,6 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
} }
} }
private void _clientTick()
{
//LOGGER.info("Client world tick with {} levels", levels.size());
this.dhLevels.forEach(DhClientServerLevel::clientTick);
}
@Override
public void clientTick()
{
//LOGGER.info("Client world tick");
this.eventLoop.tick();
}
//================// //================//
@@ -194,8 +184,8 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
this.dhLevelByLevelWrapper.clear(); this.dhLevelByLevelWrapper.clear();
this.eventLoop.close(); this.clientTickTimer.cancel();
LOGGER.info("Closed DhWorld of type " + this.environment); LOGGER.info("Closed DhWorld of type [" + this.environment + "].");
} }
} }
@@ -24,17 +24,17 @@ import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
import com.seibel.distanthorizons.core.level.DhClientLevel; import com.seibel.distanthorizons.core.level.DhClientLevel;
import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.level.IDhLevel;
import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState; import com.seibel.distanthorizons.core.multiplayer.client.ClientNetworkState;
import com.seibel.distanthorizons.core.util.ThreadUtil; import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.util.objects.EventLoop;
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 org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
{ {
@@ -42,8 +42,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
public final ClientOnlySaveStructure saveStructure; public final ClientOnlySaveStructure saveStructure;
public final ClientNetworkState networkState = new ClientNetworkState(); public final ClientNetworkState networkState = new ClientNetworkState();
public final ExecutorService dhTickerThread = ThreadUtil.makeSingleThreadPool("Client World Ticker"); private final Timer clientTickTimer = TimerUtil.CreateTimer("ClientTickTimer");
public final EventLoop eventLoop = new EventLoop(this.dhTickerThread, this::_clientTick);
@@ -59,6 +58,15 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
this.levels = new ConcurrentHashMap<>(); this.levels = new ConcurrentHashMap<>();
LOGGER.info("Started DhWorld of type " + this.environment); LOGGER.info("Started DhWorld of type " + this.environment);
this.clientTickTimer.scheduleAtFixedRate(new TimerTask()
{
@Override
public void run()
{
DhClientWorld.this.levels.values().forEach(DhClientLevel::clientTick);
}
}, 0, IDhClientWorld.TICK_RATE_IN_MS);
} }
@@ -127,11 +135,6 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
} }
} }
private void _clientTick() { this.levels.values().forEach(DhClientLevel::clientTick); }
@Override
public void clientTick() { this.eventLoop.tick(); }
@Override @Override
public void addDebugMenuStringsToList(List<String> messageList) public void addDebugMenuStringsToList(List<String> messageList)
{ {
@@ -143,7 +146,6 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
public void close() public void close()
{ {
this.networkState.close(); this.networkState.close();
this.dhTickerThread.shutdownNow();
ArrayList<CompletableFuture<Void>> closeFutures = new ArrayList<>(); ArrayList<CompletableFuture<Void>> closeFutures = new ArrayList<>();
for (DhClientLevel dhClientLevel : this.levels.values()) for (DhClientLevel dhClientLevel : this.levels.values())
@@ -175,7 +177,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
} }
this.levels.clear(); this.levels.clear();
this.eventLoop.close(); this.clientTickTimer.cancel();
LOGGER.info("Closed DhWorld of type [" + this.environment + "]."); LOGGER.info("Closed DhWorld of type [" + this.environment + "].");
} }
@@ -24,7 +24,8 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
public interface IDhClientWorld extends IDhWorld public interface IDhClientWorld extends IDhWorld
{ {
void clientTick(); /** how long in between client ticks in milliseconds */
long TICK_RATE_IN_MS = 100L;
default IDhClientLevel getOrLoadClientLevel(ILevelWrapper levelWrapper) { return (IDhClientLevel) this.getOrLoadLevel(levelWrapper); } default IDhClientLevel getOrLoadClientLevel(ILevelWrapper levelWrapper) { return (IDhClientLevel) this.getOrLoadLevel(levelWrapper); }
default IDhClientLevel getClientLevel(ILevelWrapper levelWrapper) { return (IDhClientLevel) this.getLevel(levelWrapper); } default IDhClientLevel getClientLevel(ILevelWrapper levelWrapper) { return (IDhClientLevel) this.getLevel(levelWrapper); }
@@ -52,6 +52,10 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable
@Override @Override
IDimensionTypeWrapper getDimensionType(); IDimensionTypeWrapper getDimensionType();
/**
* Includes both the namespace and name. <br>
* example: "minecraft@@overworld"
*/
@Override @Override
String getDimensionName(); String getDimensionName();
@@ -733,6 +733,8 @@
"Show Slow World Gen Warnings", "Show Slow World Gen Warnings",
"distanthorizons.config.common.logging.warning.showModCompatibilityWarningsOnStartup": "distanthorizons.config.common.logging.warning.showModCompatibilityWarningsOnStartup":
"Show Mod Compatibility Warnings", "Show Mod Compatibility Warnings",
"distanthorizons.config.common.logging.warning.logGarbageCollectorWarning":
"Log Garbage Collector Warning",