Compare commits

...

44 Commits

Author SHA1 Message Date
James Seibel e9a044308f remove dev from version number 2025-12-18 09:35:07 -06:00
James Seibel 1aabc0c792 remove chunkWrapper.isStillValid() 2025-12-18 09:35:02 -06:00
James Seibel b1b0642fbe LodRenderSection commenting/regions 2025-12-17 09:32:12 -06:00
James Seibel eecb28d11f Fix GLProxy error in multiplayer
Make some GLProxy methods static to prevent setup order issues
2025-12-17 09:02:07 -06:00
James Seibel 90564f2537 fix javadoc in LevelWrapper 2025-12-16 16:39:03 -06:00
James Seibel ded0b979cf Merge branch 'main' of gitlab.com:distant-horizons-team/distant-horizons-core 2025-12-16 14:45:57 -06:00
James Seibel ed9cc5485c Add SSAO fade out distance 2025-12-16 14:45:53 -06:00
s809 cbd5974657 Fix packet handle errors not showing on F3 screen 2025-12-17 00:15:55 +05:00
James Seibel 0e5fba58ab minor shader program refactor 2025-12-16 09:13:22 -06:00
James Seibel 2943e63382 slight light engine optimization 2025-12-15 14:37:15 -06:00
James Seibel 30564aade7 up version number 2.4.2 -> 2.4.3-dev 2025-12-15 10:17:28 -06:00
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
James Seibel a2c61ed278 up version number 2.3.7 -> 2.4.0 2025-12-13 10:19:50 -06:00
James Seibel 99eb4ac8a1 Fix infinite loop in DhSectionPos 2025-12-13 09:10:12 -06:00
James Seibel c75902d9d6 debug particle cleanup 2025-12-13 08:50:15 -06:00
James Seibel 1743949ba5 fix GeneratedFullDataSourceProvider not adding update listener 2025-12-13 08:49:45 -06:00
James Seibel a74a37a0e8 world gen queue refactoring 2025-12-13 08:49:31 -06:00
James Seibel 4ed7941288 fix missing localization 2025-12-12 07:45:12 -06:00
James Seibel ec59a5f754 comment cleanup and enum renaming for API use 2025-12-11 07:35:37 -06:00
James Seibel 895e04b7cc Remove unused wrapper functions and refactor 2025-12-10 18:50:35 -06:00
James Seibel 8f0930fa02 Allow world gen limits on singleplayer 2025-12-10 07:09:29 -06:00
James Seibel c1c4328fa5 rename API getSoftCache -> createSoftCahe 2025-12-09 20:57:27 -06:00
James Seibel 91240e4f7a disable mip-mapping on textures
necessary to fix MC 1.21.11 rendering
2025-12-09 20:57:09 -06:00
James Seibel 17c61a97cc revert long windows filepath char 2025-12-09 07:21:40 -06:00
James Seibel b78b852ffb Merge branch 'batchGenRefactor' 2025-12-09 07:16:18 -06:00
James Seibel 5edc73cc03 enable long file paths for the config file 2025-12-06 12:28:22 -06:00
James Seibel 6fcfc9379e Fix repo unit tests 2025-12-06 12:27:53 -06:00
James Seibel 149fbccfa5 Merge branch 'batchGenRefactor' 2025-12-06 12:19:17 -06:00
James Seibel 4e9559f230 enable long file paths on windows for the DB 2025-12-02 07:07:17 -06:00
87 changed files with 820 additions and 772 deletions
@@ -39,10 +39,6 @@ package com.seibel.distanthorizons.api.enums;
*/ */
public enum EDhApiDetailLevel public enum EDhApiDetailLevel
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/** /**
* detail level: 0 <Br> * detail level: 0 <Br>
* width in Blocks: 1 * width in Blocks: 1
@@ -28,10 +28,6 @@ package com.seibel.distanthorizons.api.enums.config;
*/ */
public enum EDhApiBlocksToAvoid public enum EDhApiBlocksToAvoid
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
NONE(false), NONE(false),
NON_COLLIDING(true); NON_COLLIDING(true);
@@ -23,6 +23,7 @@ package com.seibel.distanthorizons.api.enums.config;
* UNCOMPRESSED <br> * UNCOMPRESSED <br>
* LZ4 <br> * LZ4 <br>
* Z_STD <br> * Z_STD <br>
* Z_STD_STREAM <br>
* LZMA2 <br><br> * LZMA2 <br><br>
* *
* Note: speed and compression ratios are examples * Note: speed and compression ratios are examples
@@ -33,10 +34,6 @@ package com.seibel.distanthorizons.api.enums.config;
*/ */
public enum EDhApiDataCompressionMode public enum EDhApiDataCompressionMode
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/** /**
* Should only be used internally and for unit testing. <br><br> * Should only be used internally and for unit testing. <br><br>
* *
@@ -57,28 +54,32 @@ public enum EDhApiDataCompressionMode
LZ4(1), LZ4(1),
/** /**
* Decent speed and good compression. <br><br> * Great speed and good compression. <br><br>
* *
* Read Speed: 2.1 MS / DTO <br>
* Write Speed: 4.9 MS / DTO <br>
* Compression ratio: 0.2606 <br>
*/
Z_STD_BLOCK(4),
/**
* Similar to {@link EDhApiDataCompressionMode#Z_STD_BLOCK}
* except slower. <br><br>
*
* This option is only provided for legacy support when processing old databases. <br><br>
*
* Read Speed: 9.31 MS / DTO <br> * Read Speed: 9.31 MS / DTO <br>
* Write Speed: 15.13 MS / DTO <br> * Write Speed: 15.13 MS / DTO <br>
* Compression ratio: 0.2606 <br> * Compression ratio: 0.2606 <br>
*/ */
Z_STD(4),
/**
* Similar to {@link EDhApiDataCompressionMode#Z_STD}
* except slower.
* <br>
* This option is only provided for legacy support when processing old databases.
*/
@Deprecated @Deprecated
@DisallowSelectingViaConfigGui @DisallowSelectingViaConfigGui
Z_STD_STREAM(2), Z_STD_STREAM(2),
/** /**
* Extremely slow, but very good compression. <br><br> * Extremely slow, but very good compression. <br>
* * Often causes whole computer stuttering due to memory bandwidth saturation. <br><br>
*
* Read Speed: 13.29 MS / DTO <br> * Read Speed: 13.29 MS / DTO <br>
* Write Speed: 70.95 MS / DTO <br> * Write Speed: 70.95 MS / DTO <br>
* Compression ratio: 0.2068 <br> * Compression ratio: 0.2068 <br>
@@ -44,9 +44,9 @@ public enum EDhApiGpuUploadMethod
/** Fast rendering but may stutter when uploading. */ /** Fast rendering but may stutter when uploading. */
SUB_DATA(false, false), SUB_DATA(false, false),
/** Don't upload, only should be used for debugging */ ///** Don't upload, only should be used for debugging */
@Deprecated // TODO remove before release //@Deprecated
NONE(false, false), //NONE(false, false),
/** /**
* May end up storing buffers in System memory. <br> * May end up storing buffers in System memory. <br>
@@ -29,10 +29,6 @@ package com.seibel.distanthorizons.api.enums.config;
*/ */
public enum EDhApiGrassSideRendering public enum EDhApiGrassSideRendering
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
AS_GRASS, AS_GRASS,
FADE_TO_DIRT, FADE_TO_DIRT,
AS_DIRT; AS_DIRT;
@@ -31,11 +31,6 @@ package com.seibel.distanthorizons.api.enums.config;
*/ */
public enum EDhApiHorizontalQuality public enum EDhApiHorizontalQuality
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
// Note: any quadraticBase less than 2.0f has issues with DetailDistanceUtil, and will always return the lowest detail level. // Note: any quadraticBase less than 2.0f has issues with DetailDistanceUtil, and will always return the lowest detail level.
// So for now we are limiting the lowest value to 2.0 // So for now we are limiting the lowest value to 2.0
// LOWEST was originally 1.0f and LOW was 1.5f // LOWEST was originally 1.0f and LOW was 1.5f
@@ -29,10 +29,6 @@ package com.seibel.distanthorizons.api.enums.config;
*/ */
public enum EDhApiLodShading public enum EDhApiLodShading
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/** /**
* Uses Minecraft's shading for LODs. <Br> * Uses Minecraft's shading for LODs. <Br>
* This means if Minecraft's shading is disabled DH's shading will be as well. * This means if Minecraft's shading is disabled DH's shading will be as well.
@@ -33,10 +33,6 @@ package com.seibel.distanthorizons.api.enums.config;
*/ */
public enum EDhApiMcRenderingFadeMode public enum EDhApiMcRenderingFadeMode
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/** /**
* No fading is done, there will be a pronounced border between * No fading is done, there will be a pronounced border between
* Minecraft and Distant Horizons. <br> * Minecraft and Distant Horizons. <br>
@@ -35,11 +35,6 @@ package com.seibel.distanthorizons.api.enums.config;
*/ */
public enum EDhApiServerFolderNameMode public enum EDhApiServerFolderNameMode
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/** Only use the server name */ /** Only use the server name */
NAME_ONLY, NAME_ONLY,
@@ -34,11 +34,6 @@ package com.seibel.distanthorizons.api.enums.config;
@Deprecated // not currently in use, if the config this enum represents is re-implemented, the deprecated flag can be removed @Deprecated // not currently in use, if the config this enum represents is re-implemented, the deprecated flag can be removed
public enum EDhApiVanillaOverdraw public enum EDhApiVanillaOverdraw
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/** /**
* Don't draw LODs where a minecraft chunk could be. * Don't draw LODs where a minecraft chunk could be.
* Use Overdraw Offset to tweak the border thickness. * Use Overdraw Offset to tweak the border thickness.
@@ -28,10 +28,6 @@ package com.seibel.distanthorizons.api.enums.config;
*/ */
public enum EDhApiWorldCompressionMode public enum EDhApiWorldCompressionMode
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/** /**
* Every block/biome change is recorded in the database. <br> * Every block/biome change is recorded in the database. <br>
* This is what DH 2.0 and 2.0.1 all used by default and will store a lot of data. * This is what DH 2.0 and 2.0.1 all used by default and will store a lot of data.
@@ -35,10 +35,6 @@ import com.seibel.distanthorizons.api.enums.config.DisallowSelectingViaConfigGui
*/ */
public enum EDhApiQualityPreset public enum EDhApiQualityPreset
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
@DisallowSelectingViaConfigGui @DisallowSelectingViaConfigGui
CUSTOM, CUSTOM,
@@ -34,10 +34,6 @@ import com.seibel.distanthorizons.api.enums.config.DisallowSelectingViaConfigGui
*/ */
public enum EDhApiThreadPreset public enum EDhApiThreadPreset
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
@DisallowSelectingViaConfigGui @DisallowSelectingViaConfigGui
CUSTOM, CUSTOM,
@@ -33,11 +33,6 @@ package com.seibel.distanthorizons.api.enums.rendering;
*/ */
public enum EDhApiDebugRendering public enum EDhApiDebugRendering
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/** LODs are rendered normally */ /** LODs are rendered normally */
OFF, OFF,
@@ -33,10 +33,6 @@ package com.seibel.distanthorizons.api.enums.rendering;
@Deprecated @Deprecated
public enum EDhApiFogDrawMode public enum EDhApiFogDrawMode
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/** /**
* Use whatever Fog setting Optifine is using. * Use whatever Fog setting Optifine is using.
* If Optifine isn't installed this defaults to {@link EDhApiFogDrawMode#FOG_ENABLED}. * If Optifine isn't installed this defaults to {@link EDhApiFogDrawMode#FOG_ENABLED}.
@@ -30,11 +30,6 @@ package com.seibel.distanthorizons.api.enums.rendering;
*/ */
public enum EDhApiFogFalloff public enum EDhApiFogFalloff
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
LINEAR(0), LINEAR(0),
EXPONENTIAL(1), EXPONENTIAL(1),
EXPONENTIAL_SQUARED(2); EXPONENTIAL_SQUARED(2);
@@ -33,11 +33,6 @@ package com.seibel.distanthorizons.api.enums.rendering;
*/ */
public enum EDhApiHeightFogDirection public enum EDhApiHeightFogDirection
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
ABOVE_CAMERA (true, true, false), ABOVE_CAMERA (true, true, false),
BELOW_CAMERA (true, false, true), BELOW_CAMERA (true, false, true),
ABOVE_AND_BELOW_CAMERA (true, true, true), ABOVE_AND_BELOW_CAMERA (true, true, true),
@@ -29,11 +29,6 @@ package com.seibel.distanthorizons.api.enums.rendering;
*/ */
public enum EDhApiRendererMode public enum EDhApiRendererMode
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
DEFAULT, DEFAULT,
DEBUG, DEBUG,
DISABLED; DISABLED;
@@ -29,11 +29,6 @@ package com.seibel.distanthorizons.api.enums.rendering;
*/ */
public enum EDhApiTransparency public enum EDhApiTransparency
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
DISABLED(false, false), DISABLED(false, false),
FAKE(true, true), FAKE(true, true),
COMPLETE(true, false); COMPLETE(true, false);
@@ -34,11 +34,6 @@ package com.seibel.distanthorizons.api.enums.worldGeneration;
*/ */
public enum EDhApiDistantGeneratorMode public enum EDhApiDistantGeneratorMode
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
/** Don't generate any new terrain, just generate LODs for already generated chunks. */ /** Don't generate any new terrain, just generate LODs for already generated chunks. */
PRE_EXISTING_ONLY((byte) 1), PRE_EXISTING_ONLY((byte) 1),
@@ -31,11 +31,6 @@ package com.seibel.distanthorizons.api.enums.worldGeneration;
*/ */
public enum EDhApiDistantGeneratorProgressDisplayLocation public enum EDhApiDistantGeneratorProgressDisplayLocation
{ {
// Reminder:
// when adding items up the API minor version
// when removing items up the API major version
OVERLAY, OVERLAY,
CHAT, CHAT,
LOG, LOG,
@@ -161,9 +161,16 @@ public interface IDhApiTerrainDataRepo
//=========// //=========//
/** /**
* Creates a new cache you manage that can be used to speed up repeat
* read operations. <br>
* Without a cache each operation must: hit the backing database file,
* decompress it, and parse it; which is a fairly slow process. <br>
*
* @return a {@link IDhApiTerrainDataCache} backed by {@link java.lang.ref.SoftReference}'s. * @return a {@link IDhApiTerrainDataCache} backed by {@link java.lang.ref.SoftReference}'s.
* @since API 3.0.0 * @since API 5.0.0
*/ */
IDhApiTerrainDataCache getSoftCache(); IDhApiTerrainDataCache createSoftCache();
} }
@@ -31,7 +31,7 @@ import java.util.List;
* Contains a list of {@link DhApiTerrainDataPoint} representing the blocks in a Minecraft chunk. * Contains a list of {@link DhApiTerrainDataPoint} representing the blocks in a Minecraft chunk.
* *
* @author Builderb0y, James Seibel * @author Builderb0y, James Seibel
* @version 2024-7-21 * @version 2025-12-11
* @since API 2.0.0 * @since API 2.0.0
* *
* @see IDhApiWrapperFactory * @see IDhApiWrapperFactory
@@ -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.3.7-b-dev"; public static final String VERSION = "2.4.3-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" +
"");
}
}
} }
} }
@@ -521,7 +521,7 @@ public class DhApiTerrainDataRepo implements IDhApiTerrainDataRepo
//=============// //=============//
@Override @Override
public IDhApiTerrainDataCache getSoftCache() { return new DhApiTerrainDataCache(); } public IDhApiTerrainDataCache createSoftCache() { return new DhApiTerrainDataCache(); }
@@ -23,16 +23,13 @@ import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode; import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass; import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*; import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
import com.seibel.distanthorizons.core.api.external.methods.data.DhApiTerrainDataRepo;
import com.seibel.distanthorizons.core.api.internal.rendering.DhRenderState; import com.seibel.distanthorizons.core.api.internal.rendering.DhRenderState;
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.network.messages.MessageRegistry; import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.render.DhApiRenderProxy; import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
import com.seibel.distanthorizons.core.render.renderer.VanillaFadeRenderer; import com.seibel.distanthorizons.core.render.renderer.*;
import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
import com.seibel.distanthorizons.core.render.renderer.RenderParams;
import com.seibel.distanthorizons.core.util.TimerUtil; import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.util.objects.Pair; import com.seibel.distanthorizons.core.util.objects.Pair;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
@@ -46,9 +43,7 @@ import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel; import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
import com.seibel.distanthorizons.core.render.glObject.GLProxy; import com.seibel.distanthorizons.core.render.glObject.GLProxy;
import com.seibel.distanthorizons.core.render.renderer.TestRenderer;
import com.seibel.distanthorizons.core.world.AbstractDhWorld; import com.seibel.distanthorizons.core.world.AbstractDhWorld;
import com.seibel.distanthorizons.core.world.DhClientServerWorld;
import com.seibel.distanthorizons.core.world.DhClientWorld; import com.seibel.distanthorizons.core.world.DhClientWorld;
import com.seibel.distanthorizons.core.world.IDhClientWorld; import com.seibel.distanthorizons.core.world.IDhClientWorld;
import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper;
@@ -61,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;
@@ -98,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;
@@ -323,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 //
//============// //============//
@@ -410,8 +379,11 @@ public class ClientApi
try try
{ {
// make sure the GLProxy is created for future use
GLProxy.getInstance();
// these tasks always need to be called, regardless of whether the renderer is enabled or not to prevent memory leaks // these tasks always need to be called, regardless of whether the renderer is enabled or not to prevent memory leaks
GLProxy.getInstance().runRenderThreadTasks(); GLProxy.runRenderThreadTasks();
} }
catch (Exception e) catch (Exception e)
{ {
@@ -679,7 +651,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();
@@ -725,10 +698,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 =
@@ -9,6 +9,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.network.event.internal.CloseInternalEvent; import com.seibel.distanthorizons.core.network.event.internal.CloseInternalEvent;
import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage; import com.seibel.distanthorizons.core.network.messages.base.LevelInitMessage;
import com.seibel.distanthorizons.core.network.session.NetworkSession; import com.seibel.distanthorizons.core.network.session.NetworkSession;
import com.seibel.distanthorizons.core.render.glObject.GLProxy;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
@@ -89,7 +90,7 @@ public class ClientPluginChannelApi
LOGGER.info("Server level key received: [" + msg.levelKey + "]."); LOGGER.info("Server level key received: [" + msg.levelKey + "].");
MC.executeOnRenderThread(() -> GLProxy.queueRunningOnRenderThread(() ->
{ {
IClientLevelWrapper clientLevel = MC.getWrappedClientLevel(true); IClientLevelWrapper clientLevel = MC.getWrappedClientLevel(true);
IServerKeyedClientLevel existingKeyedClientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel(); IServerKeyedClientLevel existingKeyedClientLevel = KEYED_CLIENT_LEVEL_MANAGER.getServerKeyedLevel();
@@ -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;
@@ -123,7 +122,6 @@ public class Config
{ {
// common config links need to have their destination // common config links need to have their destination
// since they aren't part of "client" config class // since they aren't part of "client" config class
// TODO determine their destination programically instead of hard coding the value
public static ConfigUIComment advancedHeader = new ConfigUIComment.Builder().setParentConfigClass(Advanced.class).build(); public static ConfigUIComment advancedHeader = new ConfigUIComment.Builder().setParentConfigClass(Advanced.class).build();
@@ -169,6 +167,20 @@ public class Config
public static ConfigCategory culling = new ConfigCategory.Builder().set(Culling.class).build(); public static ConfigCategory culling = new ConfigCategory.Builder().set(Culling.class).build();
public static ConfigUISpacer cullingSpacer = new ConfigUISpacer.Builder().build(); public static ConfigUISpacer cullingSpacer = new ConfigUISpacer.Builder().build();
public static ConfigEntry<Boolean> overrideVanillaGraphicsSettings = new ConfigEntry.Builder<Boolean>()
.set(true)
.comment("" +
"If true some vanilla graphics settings will be automatically changed \n" +
"during DH setup to provide a better experience. \n" +
" \n" +
"IE disabling vanilla clouds (which render on top of DH LODs), \n" +
" and chunk fading (DH already fades MC chunks) \n" +
"")
.build();
public static ConfigUISpacer overrideVanillaSpacer = new ConfigUISpacer.Builder().build();
public static ConfigCategory experimental = new ConfigCategory.Builder().set(Experimental.class).build(); public static ConfigCategory experimental = new ConfigCategory.Builder().set(Experimental.class).build();
@@ -260,7 +272,7 @@ public class Config
public static ConfigEntry<Double> lodBias = new ConfigEntry.Builder<Double>() public static ConfigEntry<Double> lodBias = new ConfigEntry.Builder<Double>()
.setMinDefaultMax(0d, 0d, null) .setMinDefaultMax(0d, 0d, null)
.comment("" .comment(""
+ "What the value should vanilla Minecraft's texture LodBias be? \n" + "What value should vanilla Minecraft's texture LodBias be? \n"
+ "If set to 0 the mod wont overwrite vanilla's default (which so happens to also be 0)") + "If set to 0 the mod wont overwrite vanilla's default (which so happens to also be 0)")
.build(); .build();
@@ -407,6 +419,14 @@ public class Config
"") "")
.build(); .build();
public static ConfigEntry<Integer> fadeDistanceInBlocks = new ConfigEntry.Builder<Integer>()
.setMinDefaultMax(0, 1_600, 30_000_000)
.comment("" +
"The distance in blocks from the camera where the SSAO will fade out to. \n"+
"This is done to prevent banding and noise at extreme distances. \n"+
"")
.build();
} }
public static class GenericRendering public static class GenericRendering
@@ -773,6 +793,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();
@@ -1230,23 +1255,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();
@@ -1362,6 +1381,37 @@ public class Config
+ "") + "")
.build(); .build();
public static ConfigEntry<Integer> generationCenterChunkX = new ConfigEntry.Builder<Integer>()
.setChatCommandName("generation.bounds.centerChunk.x")
.setAppearance(EConfigEntryAppearance.ONLY_IN_FILE)
.setMinDefaultMax(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)
.comment("" +
"The center X chunk position that the world gen max radius is centered around. \n" +
"")
.build();
public static ConfigEntry<Integer> generationCenterChunkZ = new ConfigEntry.Builder<Integer>()
.setChatCommandName("generation.bounds.centerChunk.z")
.setAppearance(EConfigEntryAppearance.ONLY_IN_FILE)
.setMinDefaultMax(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)
.comment("" +
"The center Z chunk position that the world gen max radius is centered around. \n" +
"")
.build();
public static ConfigEntry<Integer> generationMaxChunkRadius = new ConfigEntry.Builder<Integer>()
.setChatCommandName("generation.bounds.radiusInChunks")
.setAppearance(EConfigEntryAppearance.ONLY_IN_FILE)
.setMinDefaultMax(0, 0, Integer.MAX_VALUE)
.comment("" +
"The max radius in chunks around the central point where world generation is allowed. \n" +
"If this value is set to 0, generation bounds are disabled and the render distance will be used. \n" +
"\n" +
"This should only be set if you have a pre-generated world that has a very limited size. \n" +
"Setting this on a normal MC world will prevent the world generator from filling \n" +
"out your render distance. \n" +
"")
.build();
} }
public static class LodBuilding public static class LodBuilding
@@ -1387,7 +1437,7 @@ public class Config
.build(); .build();
public static ConfigEntry<EDhApiDataCompressionMode> dataCompression = new ConfigEntry.Builder<EDhApiDataCompressionMode>() public static ConfigEntry<EDhApiDataCompressionMode> dataCompression = new ConfigEntry.Builder<EDhApiDataCompressionMode>()
.set(EDhApiDataCompressionMode.Z_STD) .set(EDhApiDataCompressionMode.Z_STD_BLOCK)
// only visible via the API since there is no reason to use any compressor except ZStandard as of 2025-11-24 // only visible via the API since there is no reason to use any compressor except ZStandard as of 2025-11-24
.setAppearance(EConfigEntryAppearance.ONLY_IN_API) .setAppearance(EConfigEntryAppearance.ONLY_IN_API)
.build(); .build();
@@ -1617,6 +1667,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();
} }
} }
@@ -1686,32 +1745,6 @@ public class Config
"") "")
.build(); .build();
public static ConfigEntry<Integer> generationBoundsX = new ConfigEntry.Builder<Integer>()
.setChatCommandName("generation.bounds.x")
.setAppearance(EConfigEntryAppearance.ONLY_IN_FILE)
.setMinDefaultMax(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)
.comment("" +
"Defines the X-coordinate of the central point for generation boundaries, in blocks. \n" +
"")
.build();
public static ConfigEntry<Integer> generationBoundsZ = new ConfigEntry.Builder<Integer>()
.setChatCommandName("generation.bounds.z")
.setAppearance(EConfigEntryAppearance.ONLY_IN_FILE)
.setMinDefaultMax(Integer.MIN_VALUE, 0, Integer.MAX_VALUE)
.comment("" +
"Defines the Z-coordinate of the central point for generation boundaries, in blocks. \n" +
"")
.build();
public static ConfigEntry<Integer> generationBoundsRadius = new ConfigEntry.Builder<Integer>()
.setChatCommandName("generation.bounds.radius")
.setAppearance(EConfigEntryAppearance.ONLY_IN_FILE)
.setMinDefaultMax(0, 0, Integer.MAX_VALUE)
.comment("" +
"Defines the radius around the central point within which generation is allowed, in blocks. \n" +
"If this value is set to 0, generation bounds are disabled." +
"")
.build();
// Real-time updates // Real-time updates
public static ConfigEntry<Boolean> enableRealTimeUpdates = new ConfigEntry.Builder<Boolean>() public static ConfigEntry<Boolean> enableRealTimeUpdates = new ConfigEntry.Builder<Boolean>()
@@ -24,12 +24,14 @@ import com.seibel.distanthorizons.core.config.ConfigHandler;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.config.types.AbstractConfigBase; import com.seibel.distanthorizons.core.config.types.AbstractConfigBase;
import com.seibel.distanthorizons.core.config.types.ConfigEntry; import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import com.seibel.distanthorizons.core.jar.EPlatform;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.ModInfo;
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 java.io.File;
import java.io.IOException; import java.io.IOException;
import java.nio.file.Files; import java.nio.file.Files;
import java.nio.file.Path; import java.nio.file.Path;
@@ -29,13 +29,14 @@ import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.RenderDataPointUtil; import com.seibel.distanthorizons.core.util.RenderDataPointUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView; import com.seibel.distanthorizons.core.dataObjects.render.columnViews.ColumnArrayView;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.coreapi.util.MathUtil; import com.seibel.distanthorizons.coreapi.util.MathUtil;
import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.longs.LongArrayList;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
public class ColumnBox public class ColumnBox
{ {
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
/** /**
* if the skylight has this value that means * if the skylight has this value that means
@@ -122,7 +123,7 @@ public class ColumnBox
&& !isTopTransparent; && !isTopTransparent;
if (!skipTop) if (!skipTop)
{ {
builder.addQuadUp(minX, maxY, minZ, width, width, ColorUtil.applyShade(color, MC.getShade(EDhDirection.UP)), irisBlockMaterialId, skyLightTop, blockLight); builder.addQuadUp(minX, maxY, minZ, width, width, ColorUtil.applyShade(color, MC_RENDER.getShade(EDhDirection.UP)), irisBlockMaterialId, skyLightTop, blockLight);
} }
} }
@@ -133,7 +134,7 @@ public class ColumnBox
&& !isBottomTransparent; && !isBottomTransparent;
if (!skipBottom) if (!skipBottom)
{ {
builder.addQuadDown(minX, minY, minZ, width, width, ColorUtil.applyShade(color, MC.getShade(EDhDirection.DOWN)), irisBlockMaterialId, skyLightBot, blockLight); builder.addQuadDown(minX, minY, minZ, width, width, ColorUtil.applyShade(color, MC_RENDER.getShade(EDhDirection.DOWN)), irisBlockMaterialId, skyLightBot, blockLight);
} }
} }
@@ -263,7 +264,7 @@ public class ColumnBox
// no adjacent data // // no adjacent data //
//==================// //==================//
color = ColorUtil.applyShade(color, MC.getShade(direction)); color = ColorUtil.applyShade(color, MC_RENDER.getShade(direction));
if (adjColumnView.size == 0 if (adjColumnView.size == 0
|| RenderDataPointUtil.hasZeroHeight(adjColumnView.get(0))) || RenderDataPointUtil.hasZeroHeight(adjColumnView.get(0)))
@@ -107,7 +107,7 @@ public class LodBufferContainer implements AutoCloseable
// upload on MC's render thread // upload on MC's render thread
GLProxy.getInstance().queueRunningOnRenderThread(() -> GLProxy.queueRunningOnRenderThread(() ->
{ {
try try
{ {
@@ -295,7 +295,7 @@ public class LodBufferContainer implements AutoCloseable
{ {
this.buffersUploaded = false; this.buffersUploaded = false;
GLProxy.getInstance().queueRunningOnRenderThread(() -> GLProxy.queueRunningOnRenderThread(() ->
{ {
for (GLVertexBuffer buffer : this.vbos) for (GLVertexBuffer buffer : this.vbos)
{ {
@@ -32,6 +32,7 @@ import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; 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.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
import com.seibel.distanthorizons.coreapi.util.MathUtil; import com.seibel.distanthorizons.coreapi.util.MathUtil;
import org.lwjgl.system.MemoryUtil; import org.lwjgl.system.MemoryUtil;
@@ -44,7 +45,7 @@ import org.lwjgl.system.MemoryUtil;
public class LodQuadBuilder public class LodQuadBuilder
{ {
private static final DhLogger LOGGER = new DhLoggerBuilder().build(); private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private final ArrayList<BufferQuad>[] opaqueQuads = (ArrayList<BufferQuad>[]) new ArrayList[6]; private final ArrayList<BufferQuad>[] opaqueQuads = (ArrayList<BufferQuad>[]) new ArrayList[6];
@@ -379,7 +380,7 @@ public class LodQuadBuilder
// for horizontal and bottom faces of grass blocks, use the dirt color to // for horizontal and bottom faces of grass blocks, use the dirt color to
// prevent green cliff walls // prevent green cliff walls
color = this.clientLevelWrapper.getDirtBlockColor(); color = this.clientLevelWrapper.getDirtBlockColor();
color = ColorUtil.applyShade(color, MC.getShade(quad.direction)); color = ColorUtil.applyShade(color, MC_RENDER.getShade(quad.direction));
} }
} }
} }
@@ -398,31 +399,33 @@ public class LodQuadBuilder
} }
private void putVertex(ByteBuffer bb, short x, short y, short z, int color, byte normalIndex, byte irisBlockMaterialId, byte skylight, byte blocklight, int mx, int my, int mz) private void putVertex(ByteBuffer bb, short x, short y, short z, int color, byte normalIndex, byte irisBlockMaterialId, byte skylight, byte blocklight, int mx, int my, int mz)
{ {
skylight %= 16;
blocklight %= 16;
bb.putShort(x); bb.putShort(x);
bb.putShort(y); bb.putShort(y);
bb.putShort(z); bb.putShort(z);
short meta = 0; short meta = 0;
meta |= (skylight | (blocklight << 4)); {
byte mirco = 0; skylight %= 16;
// mirco offset which is a xyz 2bit value blocklight %= 16;
// 0b00 = no offset meta |= (short) (skylight | (blocklight << 4));
// 0b01 = positive offset
// 0b11 = negative offset byte mircoOffset = 0;
// format is: 0b00zzyyxx // mirco offset which is a xyz 2bit value
if (mx != 0) mirco |= mx > 0 ? 0b01 : 0b11; // 0b00 = no offset
if (my != 0) mirco |= my > 0 ? 0b0100 : 0b1100; // 0b01 = positive offset
if (mz != 0) mirco |= mz > 0 ? 0b010000 : 0b110000; // 0b11 = negative offset
meta |= mirco << 8; // format is: 0b00zzyyxx
if (mx != 0) { mircoOffset |= (byte) (mx > 0 ? 0b01 : 0b11); }
if (my != 0) { mircoOffset |= (byte) (my > 0 ? 0b0100 : 0b1100); }
if (mz != 0) { mircoOffset |= (byte) (mz > 0 ? 0b010000 : 0b110000); }
meta |= (short) (mircoOffset << 8);
}
bb.putShort(meta); bb.putShort(meta);
byte r = (byte) ColorUtil.getRed(color); byte r = (byte) ColorUtil.getRed(color);
byte g = (byte) ColorUtil.getGreen(color); byte g = (byte) ColorUtil.getGreen(color);
byte b = (byte) ColorUtil.getBlue(color); byte b = (byte) ColorUtil.getBlue(color);
byte a = this.doTransparency ? (byte) ColorUtil.getAlpha(color) : (byte) 255; // TODO should this be called here or happen somewhere else? byte a = this.doTransparency ? (byte) ColorUtil.getAlpha(color) : (byte) 255;
bb.put(r); bb.put(r);
bb.put(g); bb.put(g);
bb.put(b); bb.put(b);
@@ -0,0 +1,12 @@
package com.seibel.distanthorizons.core.enums;
/**
* TODO
* might be deprecated in the future? in that case we'll probably want a wrapper
* function to handle colors for new MC versions
*
* source: https://minecraft.wiki/w/Formatting_codes
*/
public class EMinecraftColor
{
}
@@ -82,7 +82,8 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im
// constructor // // constructor //
//=============// //=============//
public GeneratedFullDataSourceProvider(IDhLevel level, ISaveStructure saveStructure) throws SQLException, IOException { super(level, saveStructure); } public GeneratedFullDataSourceProvider(IDhLevel level, ISaveStructure saveStructure) throws SQLException, IOException
{ this(level, saveStructure, null); }
public GeneratedFullDataSourceProvider(IDhLevel level, ISaveStructure saveStructure, @Nullable File saveDirOverride) throws SQLException, IOException public GeneratedFullDataSourceProvider(IDhLevel level, ISaveStructure saveStructure, @Nullable File saveDirOverride) throws SQLException, IOException
{ {
super(level, saveStructure, saveDirOverride); super(level, saveStructure, saveDirOverride);
@@ -227,7 +227,7 @@ public class FullDataUpdatePropagatorV2 implements IDebugRenderable, AutoCloseab
parentDataSource.applyToParent = true; parentDataSource.applyToParent = true;
} }
this.dataUpdater.updateDataSource(parentDataSource, false); this.dataUpdater.updateDataSource(parentDataSource);
} }
} }
} }
@@ -328,7 +328,7 @@ public class FullDataUpdatePropagatorV2 implements IDebugRenderable, AutoCloseab
childDataSource.applyToChildren = true; childDataSource.applyToChildren = true;
} }
this.dataUpdater.updateDataSource(childDataSource, false); this.dataUpdater.updateDataSource(childDataSource);
} }
} }
} }
@@ -63,7 +63,7 @@ public class FullDataUpdaterV2 implements IDebugRenderable, AutoCloseable
/** /**
* Can be used if you don't want to lock the current thread * Can be used if you don't want to lock the current thread
* Otherwise the sync version {@link FullDataUpdaterV2#updateDataSource(FullDataSourceV2, boolean)} may be a better choice. * Otherwise the sync version {@link FullDataUpdaterV2#updateDataSource(FullDataSourceV2)} may be a better choice.
*/ */
public CompletableFuture<Void> updateDataSourceAsync(@NotNull FullDataSourceV2 inputDataSource) public CompletableFuture<Void> updateDataSourceAsync(@NotNull FullDataSourceV2 inputDataSource)
{ {
@@ -86,7 +86,7 @@ public class FullDataUpdaterV2 implements IDebugRenderable, AutoCloseable
{ {
try try
{ {
this.updateDataSource(inputDataSource, true); this.updateDataSource(inputDataSource);
} }
catch (Exception e) catch (Exception e)
{ {
@@ -107,7 +107,7 @@ public class FullDataUpdaterV2 implements IDebugRenderable, AutoCloseable
} }
/** After this method returns the inputData will be written to file. */ /** After this method returns the inputData will be written to file. */
public void updateDataSource(@NotNull FullDataSourceV2 inputData, boolean lockOnUpdatePos) public void updateDataSource(@NotNull FullDataSourceV2 inputData)
{ {
if (this.isShutdownRef.get()) if (this.isShutdownRef.get())
{ {
@@ -117,25 +117,20 @@ public class FullDataUpdaterV2 implements IDebugRenderable, AutoCloseable
long updatePos = inputData.getPos(); long updatePos = inputData.getPos();
boolean methodLocked = false;
// a lock is necessary to prevent two threads from writing to the same position at once, // a lock is necessary to prevent two threads from writing to the same position at once,
// if that happens only the second update will apply and the LOD will end up with hole(s) // if that happens only the second update will apply and the LOD will end up with hole(s)
ReentrantLock updateLock = this.updateLockProvider.getLock(updatePos); ReentrantLock updateLock = this.updateLockProvider.getLock(updatePos);
try try
{ {
if (lockOnUpdatePos) updateLock.lock();
{ this.lockedPosSet.add(updatePos);
methodLocked = true;
updateLock.lock();
this.lockedPosSet.add(updatePos);
}
// get or create the data source // get or create the data source
try (FullDataSourceV2 recipientDataSource = this.provider.get(updatePos)) try (FullDataSourceV2 recipientDataSource = this.provider.get(updatePos))
{ {
if (recipientDataSource != null) if (recipientDataSource != null) // will be null if the repo was shut down
{ {
boolean dataModified = recipientDataSource.updateFromDataSource(inputData); boolean dataModified = recipientDataSource.updateFromDataSource(inputData);
if (dataModified) if (dataModified)
@@ -170,11 +165,8 @@ public class FullDataUpdaterV2 implements IDebugRenderable, AutoCloseable
} }
finally finally
{ {
if (methodLocked) updateLock.unlock();
{ this.lockedPosSet.remove(updatePos);
updateLock.unlock();
this.lockedPosSet.remove(updatePos);
}
} }
} }
@@ -118,15 +118,19 @@ public class DhLightingEngine
* @param centerChunk the chunk we want to apply lighting to * @param centerChunk the chunk we want to apply lighting to
* @param nearbyChunkList should also contain centerChunk * @param nearbyChunkList should also contain centerChunk
* @param maxSkyLight should be a value between 0 and 15 * @param maxSkyLight should be a value between 0 and 15
*
* @return the number of light positions iterated over, can be used for profiling.
*/ */
private void lightChunk( private int lightChunk(
@NotNull IChunkWrapper centerChunk, @NotNull ArrayList<IChunkWrapper> nearbyChunkList, @NotNull IChunkWrapper centerChunk, @NotNull ArrayList<IChunkWrapper> nearbyChunkList,
int maxSkyLight, boolean updateBlockLight, boolean updateSkyLight) int maxSkyLight, boolean updateBlockLight, boolean updateSkyLight)
{ {
DhChunkPos centerChunkPos = centerChunk.getChunkPos(); DhChunkPos centerChunkPos = centerChunk.getChunkPos();
AdjacentChunkHolder adjacentChunkHolder = new AdjacentChunkHolder(centerChunk); AdjacentChunkHolder adjacentChunkHolder = new AdjacentChunkHolder(centerChunk);
// how many positions we've walked over, can be used for profiling/debugging
int posIterations = 0;
// try-finally to handle the stableArray resources // try-finally to handle the stableArray resources
StableLightPosStack blockLightWorldPosQueue = null; StableLightPosStack blockLightWorldPosQueue = null;
StableLightPosStack skyLightWorldPosQueue = null; StableLightPosStack skyLightWorldPosQueue = null;
@@ -245,13 +249,15 @@ public class DhLightingEngine
} }
} }
// block light // block light
if (updateBlockLight) if (updateBlockLight)
{ {
// done to prevent a rare issue where the light values are incorrectly set to -1 // done to prevent a rare issue where the light values are incorrectly set to -1
centerChunk.clearDhBlockLighting(); centerChunk.clearDhBlockLighting();
this.propagateChunkLightPosList(blockLightWorldPosQueue, adjacentChunkHolder, posIterations += this.propagateChunkLightPosList(blockLightWorldPosQueue, adjacentChunkHolder,
(neighbourChunk, relBlockPos) -> neighbourChunk.getDhBlockLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ()), (neighbourChunk, relBlockPos) -> neighbourChunk.getDhBlockLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ()),
(neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhBlockLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ(), newLightValue), (neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhBlockLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ(), newLightValue),
true); true);
@@ -262,7 +268,7 @@ public class DhLightingEngine
{ {
centerChunk.clearDhSkyLighting(); centerChunk.clearDhSkyLighting();
this.propagateChunkLightPosList(skyLightWorldPosQueue, adjacentChunkHolder, posIterations += this.propagateChunkLightPosList(skyLightWorldPosQueue, adjacentChunkHolder,
(neighbourChunk, relBlockPos) -> neighbourChunk.getDhSkyLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ()), (neighbourChunk, relBlockPos) -> neighbourChunk.getDhSkyLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ()),
(neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhSkyLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ(), newLightValue), (neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhSkyLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ(), newLightValue),
false); false);
@@ -287,10 +293,12 @@ public class DhLightingEngine
{ {
centerChunk.setIsDhSkyLightCorrect(true); centerChunk.setIsDhSkyLightCorrect(true);
} }
return posIterations;
} }
/** Applies each {@link LightPos} from the queue to the given set of {@link IChunkWrapper}'s. */ /** Applies each {@link LightPos} from the queue to the given set of {@link IChunkWrapper}'s. */
private void propagateChunkLightPosList( private int propagateChunkLightPosList(
StableLightPosStack lightPosQueue, AdjacentChunkHolder adjacentChunkHolder, StableLightPosStack lightPosQueue, AdjacentChunkHolder adjacentChunkHolder,
IGetLightFunc getLightFunc, ISetLightFunc setLightFunc, IGetLightFunc getLightFunc, ISetLightFunc setLightFunc,
boolean propagatingBlockLights) boolean propagatingBlockLights)
@@ -320,66 +328,89 @@ public class DhLightingEngine
IBlockStateWrapper previousBlockState = null; IBlockStateWrapper previousBlockState = null;
// update each light position int iterations = 0;
while (!lightPosQueue.isEmpty())
// update each light level
for (int currentLightLevel = LodUtil.MAX_MC_LIGHT; currentLightLevel >= LodUtil.MIN_MC_LIGHT; currentLightLevel--)
{ {
// since we don't care about the order the positions are processed, // Walking down from the top light level to the bottom can reduce iterating over
// we can grab the last position instead of the first for a slight performance increase (this way the array doesn't need to be shifted over every loop) // the same positions multiple times.
lightPosQueue.popMutate(lightPos); // At best this seems to behave at roughly 2x the speed of just blindly putting light pos
// in a queue and at worse slightly faster than the blind queue.
int lightValue = lightPos.lightValue; lightPos.lightValue = currentLightLevel;
// update each light position
// propagate the lighting in each cardinal direction, IE: -x, +x, -y, +y, -z, +z while (!lightPosQueue.isLightLevelEmpty(currentLightLevel))
for (EDhDirection direction : EDhDirection.ALL) // since this is an array instead of an ArrayList this advanced for-loop shouldn't cause any GC issues
{ {
lightPos.mutateOffset(direction, neighbourBlockPos); // since we don't care about the order the positions are processed,
neighbourBlockPos.mutateToChunkRelativePos(relNeighbourBlockPos); // we can grab the last position instead of the first for a slight performance increase (this way the array doesn't need to be shifted over every loop)
lightPosQueue.popMutate(lightPos, currentLightLevel);
iterations++;
int lightValue = lightPos.lightValue;
// only continue if the light position is inside one of our chunks // propagate the lighting in each cardinal direction, IE: -x, +x, -y, +y, -z, +z
IChunkWrapper neighbourChunk = adjacentChunkHolder.getByBlockPos(neighbourBlockPos.getX(), neighbourBlockPos.getZ()); for (EDhDirection direction : EDhDirection.ALL) // since this is an array instead of an ArrayList this advanced for-loop shouldn't cause any GC issues
if (neighbourChunk == null)
{ {
// the light pos is outside our generator's range, ignore it lightPos.mutateOffset(direction, neighbourBlockPos);
continue; neighbourBlockPos.mutateToChunkRelativePos(relNeighbourBlockPos);
}
if (relNeighbourBlockPos.getY() < neighbourChunk.getMinNonEmptyHeight()
|| relNeighbourBlockPos.getY() >= neighbourChunk.getExclusiveMaxBuildHeight())
{
// the light pos is outside the chunk's min/max height,
// this can happen if given a chunk that hasn't finished generating
continue;
}
int currentBlockLight = getLightFunc.getLight(neighbourChunk, relNeighbourBlockPos);
if (currentBlockLight >= (lightValue - 1))
{
// short circuit for when the light value at this position
// is already greater-than what we could set it
continue;
}
IBlockStateWrapper neighbourBlockState = neighbourChunk.getBlockState(relNeighbourBlockPos, mcBlockPos, previousBlockState);
previousBlockState = neighbourBlockState;
// Math.max(1, ...) is used so that the propagated light level always drops by at least 1, preventing infinite cycles.
int targetLevel = lightValue - Math.max(1, neighbourBlockState.getOpacity());
if (targetLevel > currentBlockLight)
{
// this position is darker than the new light value, update/set it
setLightFunc.setLight(neighbourChunk, relNeighbourBlockPos, targetLevel);
// now that light has been propagated to this blockPos
// we need to queue it up so its neighbours can be propagated as well // only continue if the light position is inside one of our chunks
lightPosQueue.push(neighbourBlockPos.getX(), neighbourBlockPos.getY(), neighbourBlockPos.getZ(), targetLevel); IChunkWrapper neighbourChunk = adjacentChunkHolder.getByBlockPos(neighbourBlockPos.getX(), neighbourBlockPos.getZ());
if (neighbourChunk == null)
{
// the light pos is outside our generator's range, ignore it
continue;
}
if (relNeighbourBlockPos.getY() < neighbourChunk.getMinNonEmptyHeight()
|| relNeighbourBlockPos.getY() >= neighbourChunk.getExclusiveMaxBuildHeight())
{
// the light pos is outside the chunk's min/max height,
// this can happen if given a chunk that hasn't finished generating
continue;
}
int currentBlockLight = getLightFunc.getLight(neighbourChunk, relNeighbourBlockPos);
if (currentBlockLight >= (lightValue - 1))
{
// short circuit for when the light value at this position
// is already greater-than what we could set it
continue;
}
IBlockStateWrapper neighbourBlockState = neighbourChunk.getBlockState(relNeighbourBlockPos, mcBlockPos, previousBlockState);
previousBlockState = neighbourBlockState;
// Math.max(1, ...) is used so that the propagated light level always drops by at least 1, preventing infinite cycles.
int targetLightLevel = lightValue - Math.max(1, neighbourBlockState.getOpacity());
if (targetLightLevel > currentBlockLight)
{
// this position is darker than the new light value, update/set it
setLightFunc.setLight(neighbourChunk, relNeighbourBlockPos, targetLightLevel);
// now that light has been propagated to this blockPos
// we need to queue it up so its neighbours can be propagated as well
lightPosQueue.push(neighbourBlockPos.getX(), neighbourBlockPos.getY(), neighbourBlockPos.getZ(), targetLightLevel);
}
} }
} }
} }
for (int currentLightLevel = LodUtil.MAX_MC_LIGHT; currentLightLevel >= LodUtil.MIN_MC_LIGHT; currentLightLevel--)
{
if (!lightPosQueue.isLightLevelEmpty(currentLightLevel))
{
LodUtil.assertNotReach("Non empty light pos queue for light level ["+currentLightLevel+"] after light engine running");
}
}
// can be enabled if troubleshooting lighting issues // can be enabled if troubleshooting lighting issues
if (RENDER_BLOCK_LIGHT_WIREFRAME if (RENDER_BLOCK_LIGHT_WIREFRAME
@@ -395,6 +426,7 @@ public class DhLightingEngine
// propagation complete // propagation complete
return iterations;
} }
@@ -748,16 +780,24 @@ public class DhLightingEngine
private static final Queue<StableLightPosStack> lightArrayCache = new ArrayDeque<>(); private static final Queue<StableLightPosStack> lightArrayCache = new ArrayDeque<>();
/** the index of the last item in the array, -1 if empty */ /** the index of the last item in the array, -1 if empty */
private int index = -1; private int[] indexByLightLevel = new int[LodUtil.MAX_MC_LIGHT + 1];
/** x, y, z, and lightValue. */ /** x, y, z */
public static final int INTS_PER_LIGHT_POS = 4; public static final int INTS_PER_LIGHT_POS = 3;
/** private final IntArrayList[] lightPositionsByLightLevel = new IntArrayList[LodUtil.MAX_MC_LIGHT + 1];
* When tested with a normal 1.20 world James saw a maximum of 36,709 block and 2,355 sky lights,
* so 40,000 should be a good starting point that can contain most lighting tasks.
*/ public StableLightPosStack()
private final IntArrayList lightPositions = new IntArrayList(40_000 * INTS_PER_LIGHT_POS); {
for (int i = 0; i < this.lightPositionsByLightLevel.length; i++)
{
// When tested with a normal 1.20 world James saw a maximum of 36,709 block and 2,355 sky lights,
// so 40,000 should be a good starting point that can contain most lighting tasks.
this.lightPositionsByLightLevel[i] = new IntArrayList(40_000 * INTS_PER_LIGHT_POS);
this.indexByLightLevel[i] = -1;
}
}
@@ -804,45 +844,56 @@ public class DhLightingEngine
// stack methods // // stack methods //
//===============// //===============//
public boolean isEmpty() { return this.index == -1; } public boolean isLightLevelEmpty(int lightLevel) { return this.indexByLightLevel[lightLevel] == -1; }
public int size() { return this.index+1; } //public int size() { return this.index+1; }
public void push(int blockX, int blockY, int blockZ, int lightValue) public void push(int blockX, int blockY, int blockZ, int lightLevel)
{ {
this.index++; IntArrayList lightPositions = this.lightPositionsByLightLevel[lightLevel];
int subIndex = this.index * INTS_PER_LIGHT_POS;
if (subIndex < this.lightPositions.size()) this.indexByLightLevel[lightLevel]++;
int subIndex = this.indexByLightLevel[lightLevel] * INTS_PER_LIGHT_POS;
if (subIndex < lightPositions.size())
{ {
this.lightPositions.set(subIndex, blockX); lightPositions.set(subIndex, blockX);
this.lightPositions.set(subIndex + 1, blockY); lightPositions.set(subIndex + 1, blockY);
this.lightPositions.set(subIndex + 2, blockZ); lightPositions.set(subIndex + 2, blockZ);
this.lightPositions.set(subIndex + 3, lightValue);
} }
else else
{ {
// add a new pos // add a new pos
this.lightPositions.add(blockX); lightPositions.add(blockX);
this.lightPositions.add(blockY); lightPositions.add(blockY);
this.lightPositions.add(blockZ); lightPositions.add(blockZ);
this.lightPositions.add(lightValue);
} }
} }
/** mutates the given {@link LightPos} to match the next {@link LightPos} in the queue. */ /** mutates the given {@link LightPos} to match the next {@link LightPos} in the queue. */
public void popMutate(LightPos pos) public void popMutate(LightPos pos, int lightLevel)
{ {
int subIndex = this.index * INTS_PER_LIGHT_POS; int subIndex = this.indexByLightLevel[lightLevel] * INTS_PER_LIGHT_POS;
IntArrayList lightPositions = this.lightPositionsByLightLevel[lightLevel];
pos.setX(this.lightPositions.getInt(subIndex)); pos.setX(lightPositions.getInt(subIndex));
pos.setY(this.lightPositions.getInt(subIndex + 1)); pos.setY(lightPositions.getInt(subIndex + 1));
pos.setZ(this.lightPositions.getInt(subIndex + 2)); pos.setZ(lightPositions.getInt(subIndex + 2));
pos.lightValue = this.lightPositions.getInt(subIndex + 3);
this.index--; this.indexByLightLevel[lightLevel]--;
} }
@Override @Override
public String toString() { return this.index + "/" + (this.lightPositions.size() / INTS_PER_LIGHT_POS); } public String toString()
{
StringBuilder builder = new StringBuilder();
for (int i = 0; i < this.indexByLightLevel.length; i++)
{
builder.append("light: ").append(i)
.append(" size: ").append(this.indexByLightLevel[i]).append("/").append(this.lightPositionsByLightLevel[i].size() / INTS_PER_LIGHT_POS).append("\n");
}
return builder.toString();
}
} }
@@ -56,10 +56,8 @@ public class PregenManager
return pregenState; return pregenState;
} }
MC_SERVER.setPreventAutoPause(true);
pregenState.whenComplete((result, throwable) -> { pregenState.whenComplete((result, throwable) -> {
this.pregenFuture.set(null); this.pregenFuture.set(null);
MC_SERVER.setPreventAutoPause(false);
}); });
pregenState.fillPendingQueue(); pregenState.fillPendingQueue();
@@ -11,6 +11,7 @@ import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.WorldGenUtil;
import com.seibel.distanthorizons.core.util.objects.RollingAverage; import com.seibel.distanthorizons.core.util.objects.RollingAverage;
import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLogger;
@@ -110,12 +111,14 @@ public class RemoteWorldRetrievalQueue extends AbstractFullDataNetworkRequestQue
@Override @Override
protected boolean isSectionAllowedToGenerate(long sectionPos, DhBlockPos2D targetPos) protected boolean isSectionAllowedToGenerate(long sectionPos, DhBlockPos2D targetPos)
{ {
if (this.networkState.sessionConfig.getGenerationBoundsRadius() > 0) if (this.networkState.sessionConfig.getGenerationMaxChunkRadius() > 0)
{ {
if (DhSectionPos.getChebyshevSignedBlockDistance(sectionPos, new DhBlockPos2D( boolean posInRange = WorldGenUtil.isPosInWorldGenRange(
this.networkState.sessionConfig.getGenerationBoundsX(), sectionPos,
this.networkState.sessionConfig.getGenerationBoundsZ() this.networkState.sessionConfig.getGenerationCenterChunkX(), this.networkState.sessionConfig.getGenerationCenterChunkZ(),
)) > this.networkState.sessionConfig.getGenerationBoundsRadius()) this.networkState.sessionConfig.getGenerationMaxChunkRadius()
);
if (!posInRange)
{ {
return false; return false;
} }
@@ -210,7 +210,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
while (!this.isGeneratorBusy() while (!this.isGeneratorBusy()
&& taskStarted) && taskStarted)
{ {
taskStarted = this.startNextWorldGenTask(this.generationTargetPos); taskStarted = this.tryStartNextWorldGenTask(this.generationTargetPos);
} }
} }
catch (Exception e) catch (Exception e)
@@ -240,7 +240,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
* @param targetPos the position to center the generation around * @param targetPos the position to center the generation around
* @return false if no tasks were found to generate * @return false if no tasks were found to generate
*/ */
private boolean startNextWorldGenTask(DhBlockPos2D targetPos) private boolean tryStartNextWorldGenTask(DhBlockPos2D targetPos)
{ {
if (this.waitingTasks.isEmpty()) if (this.waitingTasks.isEmpty())
{ {
@@ -249,23 +249,23 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
Mapper closestTaskMap = this.waitingTasks.reduceEntries(1024, TaskDistancePair closestTaskPair = this.waitingTasks.reduceEntries(1024,
entry -> new Mapper(entry.getValue(), DhSectionPos.getSectionBBoxPos(entry.getValue().pos).getCenterBlockPos().toPos2D().chebyshevDist(targetPos.toPos2D())), entry -> new TaskDistancePair(entry.getValue(), DhSectionPos.getSectionBBoxPos(entry.getValue().pos).getCenterBlockPos().toPos2D().chebyshevDist(targetPos.toPos2D())),
(aMapper, bMapper) -> aMapper.dist < bMapper.dist ? aMapper : bMapper); (TaskDistancePair aTaskPair, TaskDistancePair bTaskPair) -> (aTaskPair.dist < bTaskPair.dist) ? aTaskPair : bTaskPair);
if (closestTaskMap == null) if (closestTaskPair == null)
{ {
// FIXME concurrency issue // FIXME concurrency issue
return false; return false;
} }
WorldGenTask closestTask = closestTaskMap.task; WorldGenTask closestTask = closestTaskPair.task;
// remove the task we found, we are going to start it and don't want to run it multiple times // remove the task we found, we are going to start it and don't want to run it multiple times
this.waitingTasks.remove(closestTask.pos, closestTask); this.waitingTasks.remove(closestTask.pos, closestTask);
// do we need to modify this task to generate it? // do we need to modify this task to generate it?
if (this.canGeneratePos(closestTask.pos)) if (this.canGenerateDetailLevel(DhSectionPos.getDetailLevel(closestTask.pos)))
{ {
// detail level is correct for generation, start generation // detail level is correct for generation, start generation
@@ -276,11 +276,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
{ {
// no task exists for this position, start one // no task exists for this position, start one
InProgressWorldGenTaskGroup newTaskGroup = new InProgressWorldGenTaskGroup(closestTaskGroup); InProgressWorldGenTaskGroup newTaskGroup = new InProgressWorldGenTaskGroup(closestTaskGroup);
boolean taskStarted = this.tryStartingWorldGenTaskGroup(newTaskGroup); this.startWorldGenTaskGroup(newTaskGroup);
if (!taskStarted)
{
//LOGGER.trace("Unable to start task: "+closestTask.pos+", skipping. Task position may have already been generated.");
}
} }
else else
{ {
@@ -289,7 +285,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
// the newly selected task, we cannot use it, // the newly selected task, we cannot use it,
// as some chunks may have already been written into. // as some chunks may have already been written into.
//LOGGER.trace("A task already exists for this position, todo: "+closestTask.pos); //LOGGER.warn("A task already exists for this position, todo: "+DhSectionPos.toString(closestTask.pos));
} }
// a task has been started // a task has been started
@@ -321,8 +317,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
return true; return true;
} }
} }
/** @return true if the task was started, false otherwise */ private void startWorldGenTaskGroup(InProgressWorldGenTaskGroup newTaskGroup)
private boolean tryStartingWorldGenTaskGroup(InProgressWorldGenTaskGroup newTaskGroup)
{ {
byte taskDetailLevel = newTaskGroup.group.dataDetail; byte taskDetailLevel = newTaskGroup.group.dataDetail;
long taskPos = newTaskGroup.group.pos; long taskPos = newTaskGroup.group.pos;
@@ -375,7 +370,6 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
}); });
this.inProgressGenTasksByLodPos.put(taskPos, newTaskGroup); this.inProgressGenTasksByLodPos.put(taskPos, newTaskGroup);
return true;
} }
private CompletableFuture<Void> startGenerationEvent( private CompletableFuture<Void> startGenerationEvent(
long requestPos, long requestPos,
@@ -689,9 +683,9 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
// helper methods // // helper methods //
//================// //================//
private boolean canGeneratePos(long taskPos) private boolean canGenerateDetailLevel(byte taskDetailLevel)
{ {
byte requestedDetailLevel = (byte) (DhSectionPos.getDetailLevel(taskPos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL); byte requestedDetailLevel = (byte) (taskDetailLevel - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
return (this.highestDataDetail <= requestedDetailLevel && requestedDetailLevel <= this.lowestDataDetail); return (this.highestDataDetail <= requestedDetailLevel && requestedDetailLevel <= this.lowestDataDetail);
} }
@@ -701,11 +695,12 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb
// helper classes // // helper classes //
//================// //================//
private static class Mapper private static class TaskDistancePair
{ {
public final WorldGenTask task; public final WorldGenTask task;
public final int dist; public final int dist;
public Mapper(WorldGenTask task, int dist)
public TaskDistancePair(WorldGenTask task, int dist)
{ {
this.task = task; this.task = task;
this.dist = dist; this.dist = dist;
@@ -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) { }
@@ -21,6 +21,7 @@ import com.seibel.distanthorizons.core.network.messages.requests.CancelMessage;
import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D;
import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.WorldGenUtil;
import com.seibel.distanthorizons.core.util.math.Vec3d; import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
@@ -147,16 +148,15 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I
return; return;
} }
if (Config.Server.generationBoundsRadius.get() > 0) boolean posInRange = WorldGenUtil.isPosInWorldGenRange(
message.sectionPos,
Config.Common.WorldGenerator.generationCenterChunkX.get(), Config.Common.WorldGenerator.generationCenterChunkZ.get(),
Config.Common.WorldGenerator.generationMaxChunkRadius.get()
);
if (!posInRange)
{ {
if (DhSectionPos.getChebyshevSignedBlockDistance(message.sectionPos, new DhBlockPos2D( message.sendResponse(new RequestOutOfRangeException("Section out of allowed bounds"));
serverPlayerState.sessionConfig.getGenerationBoundsX(), return;
serverPlayerState.sessionConfig.getGenerationBoundsZ()
)) > Config.Server.generationBoundsRadius.get())
{
message.sendResponse(new RequestOutOfRangeException("Section out of allowed bounds"));
return;
}
} }
if (!Config.Server.Experimental.enableNSizedGeneration.get() && DhSectionPos.getDetailLevel(message.sectionPos) != DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL) if (!Config.Server.Experimental.enableNSizedGeneration.get() && DhSectionPos.getDetailLevel(message.sectionPos) != DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL)
@@ -276,7 +276,7 @@ public class DhLogger implements IConfigListener
if (mc_client != null) if (mc_client != null)
{ {
mc_client.logToChat(level, msgStr); this.logToChat(level, msgStr);
messageLogged = true; messageLogged = true;
} }
} }
@@ -296,6 +296,41 @@ public class DhLogger implements IConfigListener
} }
private static boolean loggingLevelIsLessSpecificThan(Level thisLoggingLevel, Level requestedLogLevel) private static boolean loggingLevelIsLessSpecificThan(Level thisLoggingLevel, Level requestedLogLevel)
{ return thisLoggingLevel.intLevel() >= requestedLogLevel.intLevel(); } { return thisLoggingLevel.intLevel() >= requestedLogLevel.intLevel(); }
/** Sends the given message to chat with a formatted prefix and color based on the log level. */
private void logToChat(Level logLevel, String message)
{
String prefix = "[" + ModInfo.READABLE_NAME + "] ";
if (logLevel == Level.ERROR)
{
prefix += "\u00A74";
}
else if (logLevel == Level.WARN)
{
prefix += "\u00A76";
}
else if (logLevel == Level.INFO)
{
prefix += "\u00A7f";
}
else if (logLevel == Level.DEBUG)
{
prefix += "\u00A77";
}
else if (logLevel == Level.TRACE)
{
prefix += "\u00A78";
}
else
{
prefix += "\u00A7f";
}
prefix += "\u00A7l\u00A7u";
prefix += logLevel.name();
prefix += ":\u00A7r ";
mc_client.sendChatMessage(prefix + message);
}
@@ -33,9 +33,9 @@ public class SessionConfig implements INetworkObject
registerConfigEntry(Config.Common.WorldGenerator.enableDistantGeneration, Boolean::logicalAnd); registerConfigEntry(Config.Common.WorldGenerator.enableDistantGeneration, Boolean::logicalAnd);
registerConfigEntry(Config.Server.maxGenerationRequestDistance, Math::min); registerConfigEntry(Config.Server.maxGenerationRequestDistance, Math::min);
registerConfigEntry(Config.Server.generationBoundsX, (x, y) -> y); registerConfigEntry(Config.Common.WorldGenerator.generationCenterChunkX, (x, y) -> y);
registerConfigEntry(Config.Server.generationBoundsZ, (x, y) -> y); registerConfigEntry(Config.Common.WorldGenerator.generationCenterChunkZ, (x, y) -> y);
registerConfigEntry(Config.Server.generationBoundsRadius, (x, y) -> y); registerConfigEntry(Config.Common.WorldGenerator.generationMaxChunkRadius, (x, y) -> y);
registerConfigEntry(Config.Server.generationRequestRateLimit, Math::min); registerConfigEntry(Config.Server.generationRequestRateLimit, Math::min);
registerConfigEntry(Config.Server.enableRealTimeUpdates, Boolean::logicalAnd); registerConfigEntry(Config.Server.enableRealTimeUpdates, Boolean::logicalAnd);
@@ -68,9 +68,9 @@ public class SessionConfig implements INetworkObject
public boolean isDistantGenerationEnabled() { return this.getValue(Config.Common.WorldGenerator.enableDistantGeneration); } public boolean isDistantGenerationEnabled() { return this.getValue(Config.Common.WorldGenerator.enableDistantGeneration); }
public int getMaxGenerationRequestDistance() { return this.getValue(Config.Server.maxGenerationRequestDistance); } public int getMaxGenerationRequestDistance() { return this.getValue(Config.Server.maxGenerationRequestDistance); }
public Integer getGenerationBoundsX() { return this.getValue(Config.Server.generationBoundsX); } public Integer getGenerationCenterChunkX() { return this.getValue(Config.Common.WorldGenerator.generationCenterChunkX); }
public Integer getGenerationBoundsZ() { return this.getValue(Config.Server.generationBoundsZ); } public Integer getGenerationCenterChunkZ() { return this.getValue(Config.Common.WorldGenerator.generationCenterChunkZ); }
public Integer getGenerationBoundsRadius() { return this.getValue(Config.Server.generationBoundsRadius); } public Integer getGenerationMaxChunkRadius() { return this.getValue(Config.Common.WorldGenerator.generationMaxChunkRadius); }
public int getGenerationRequestRateLimit() { return this.getValue(Config.Server.generationRequestRateLimit); } public int getGenerationRequestRateLimit() { return this.getValue(Config.Server.generationRequestRateLimit); }
public boolean isRealTimeUpdatesEnabled() { return this.getValue(Config.Server.enableRealTimeUpdates); } public boolean isRealTimeUpdatesEnabled() { return this.getValue(Config.Server.enableRealTimeUpdates); }
@@ -102,8 +102,8 @@ public class ServerPlayerState implements Closeable
private void sendConfigMessage() private void sendConfigMessage()
{ {
double coordinateScale = this.getServerPlayer().getLevel().getDimensionType().getCoordinateScale(); double coordinateScale = this.getServerPlayer().getLevel().getDimensionType().getCoordinateScale();
this.sessionConfig.constrainValue(Config.Server.generationBoundsX, (int) (Config.Server.generationBoundsX.get() / coordinateScale)); this.sessionConfig.constrainValue(Config.Common.WorldGenerator.generationCenterChunkX, (int) (Config.Common.WorldGenerator.generationCenterChunkX.get() / coordinateScale));
this.sessionConfig.constrainValue(Config.Server.generationBoundsZ, (int) (Config.Server.generationBoundsZ.get() / coordinateScale)); this.sessionConfig.constrainValue(Config.Common.WorldGenerator.generationCenterChunkZ, (int) (Config.Common.WorldGenerator.generationCenterChunkZ.get() / coordinateScale));
this.networkSession.sendMessage(new SessionConfigMessage(this.sessionConfig)); this.networkSession.sendMessage(new SessionConfigMessage(this.sessionConfig));
} }
@@ -92,7 +92,8 @@ public class NetworkSession extends AbstractNetworkEventSource
{ {
LOGGER.error("Failed to handle the message. New messages will be ignored.", e); LOGGER.error("Failed to handle the message. New messages will be ignored.", e);
LOGGER.error("Message: ["+message+"]"); LOGGER.error("Message: ["+message+"]");
this.close();
this.close(e);
} }
} }
@@ -281,17 +281,24 @@ public class DhSectionPos
+ Math.abs(getCenterBlockPosZ(pos) - blockPos.z); + Math.abs(getCenterBlockPosZ(pos) - blockPos.z);
} }
/** see: {@link DhSectionPos#getChebyshevSignedBlockDistance(long, int, int)} */
public static int getChebyshevSignedBlockDistance(long pos, DhBlockPos blockPos)
{ return getChebyshevSignedBlockDistance(pos, blockPos.getX(), blockPos.getZ()); }
/** see: {@link DhSectionPos#getChebyshevSignedBlockDistance(long, int, int)} */
public static int getChebyshevSignedBlockDistance(long pos, DhBlockPos2D blockPos)
{ return getChebyshevSignedBlockDistance(pos, blockPos.x, blockPos.z); }
/** /**
* Returns the signed distance from a given block to a given section. <br> * Returns the signed distance from a given block to a given section. <br>
* Essentially acts like a distance from the block to the nearest edge of the section, * Essentially acts like a distance from the block to the nearest edge of the section,
* except inside the section it's negative. <br> * except inside the section it's negative. <br>
* Useful for detail level insensitive distance comparisons. * Useful for detail level insensitive distance comparisons.
*/ */
public static int getChebyshevSignedBlockDistance(long pos, DhBlockPos2D blockPos) public static int getChebyshevSignedBlockDistance(long pos, int blockPosX, int blockPosZ)
{ {
return Math.max( return Math.max(
Math.abs(getCenterBlockPosX(pos) - blockPos.x), Math.abs(getCenterBlockPosX(pos) - blockPosX),
Math.abs(getCenterBlockPosZ(pos) - blockPos.z) Math.abs(getCenterBlockPosZ(pos) - blockPosZ)
) - getBlockWidth(pos) / 2; ) - getBlockWidth(pos) / 2;
} }
@@ -762,7 +762,6 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
{ {
// walk through each node // walk through each node
Iterator<QuadNode<LodRenderSection>> nodeIterator = this.nodeIterator(); Iterator<QuadNode<LodRenderSection>> nodeIterator = this.nodeIterator();
ArrayList<CompletableFuture<Void>> renderDataBuildFutures = new ArrayList<>();
while (nodeIterator.hasNext()) while (nodeIterator.hasNext())
{ {
QuadNode<LodRenderSection> quadNode = nodeIterator.next(); QuadNode<LodRenderSection> quadNode = nodeIterator.next();
@@ -42,7 +42,7 @@ import com.seibel.distanthorizons.core.render.renderer.DebugRenderer;
import com.seibel.distanthorizons.core.render.renderer.generic.BeaconRenderHandler; import com.seibel.distanthorizons.core.render.renderer.generic.BeaconRenderHandler;
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo; import com.seibel.distanthorizons.core.sql.repo.BeaconBeamRepo;
import com.seibel.distanthorizons.core.util.PerfRecorder; import com.seibel.distanthorizons.core.util.WorldGenUtil;
import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker; import com.seibel.distanthorizons.core.util.threading.PriorityTaskPicker;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
@@ -133,6 +133,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
//=============// //=============//
// constructor // // constructor //
//=============// //=============//
//region constructor
public LodRenderSection( public LodRenderSection(
long pos, long pos,
@@ -153,11 +154,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus); DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showRenderSectionStatus);
} }
//endregion constructor
//======================================// //======================================//
// render data generation and uploading // // render data generation and uploading //
//======================================// //======================================//
//region render data uploading
/** @return true if the upload started, false if it wasn't able to for any reason */ /** @return true if the upload started, false if it wasn't able to for any reason */
public synchronized boolean uploadRenderDataToGpuAsync() public synchronized boolean uploadRenderDataToGpuAsync()
@@ -314,7 +318,10 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
} }
}); });
} }
/** async is done so each thread can run without waiting on others */ /**
* async is done so each thread can run without waiting on others
* @param direction the direction to load relative to the given position, null will return the given position
*/
private CompletableFuture<ColumnRenderSource> getRenderSourceForPosAsync(long pos, @Nullable EDhDirection direction) private CompletableFuture<ColumnRenderSource> getRenderSourceForPosAsync(long pos, @Nullable EDhDirection direction)
{ {
if (direction != null) if (direction != null)
@@ -400,11 +407,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
}); });
} }
//endregion render data uploading
//========================//
// getters and properties // //====================//
//========================// // enabling rendering //
//====================//
//region enabling rendering
public boolean canRender() { return this.bufferContainer != null; } public boolean canRender() { return this.bufferContainer != null; }
@@ -439,11 +449,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
public boolean gpuUploadInProgress() { return this.getAndBuildRenderDataFuture != null; } public boolean gpuUploadInProgress() { return this.getAndBuildRenderDataFuture != null; }
//endregion enabling rendering
//=================================// //=================================//
// full data retrieval (world gen) // // full data retrieval (world gen) //
//=================================// //=================================//
//region full data retrieval
public boolean isFullyGenerated() public boolean isFullyGenerated()
{ {
@@ -528,6 +541,18 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
} }
long pos = missingGenerationPos.removeLong(i); long pos = missingGenerationPos.removeLong(i);
boolean posInRange = WorldGenUtil.isPosInWorldGenRange(
pos,
Config.Common.WorldGenerator.generationCenterChunkX.get(), Config.Common.WorldGenerator.generationCenterChunkZ.get(),
Config.Common.WorldGenerator.generationMaxChunkRadius.get()
);
if (!posInRange)
{
continue;
}
boolean positionQueued = (this.fullDataSourceProvider.queuePositionForRetrieval(pos) != null); boolean positionQueued = (this.fullDataSourceProvider.queuePositionForRetrieval(pos) != null);
if (!positionQueued) if (!positionQueued)
{ {
@@ -539,11 +564,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
} }
} }
//endregion full data retrieval
//=================// //=================//
// beacon handling // // beacon handling //
//=================// //=================//
//region beacon handling
/** gets the active beacon list and stops/starts beacon rendering as necessary */ /** gets the active beacon list and stops/starts beacon rendering as necessary */
private void getAndRefreshRenderingBeacons() private void getAndRefreshRenderingBeacons()
@@ -617,11 +645,14 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
} }
} }
//endregion beacon handling
//==============// //==============//
// base methods // // base methods //
//==============// //==============//
//region base methods
@Override @Override
public void debugRender(DebugRenderer debugRenderer) public void debugRender(DebugRenderer debugRenderer)
@@ -708,6 +739,8 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
} }
//endregion base methods
} }
@@ -56,13 +56,13 @@ public class GLProxy
public static final Set<String> LOGGED_GL_MESSAGES = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>()); public static final Set<String> LOGGED_GL_MESSAGES = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>());
private static final ConcurrentLinkedQueue<Runnable> RENDER_THREAD_RUNNABLE_QUEUE = new ConcurrentLinkedQueue<>();
private static GLProxy instance = null; private static GLProxy instance = null;
private final ConcurrentLinkedQueue<Runnable> renderThreadRunnableQueue = new ConcurrentLinkedQueue<>();
/** Minecraft's GL capabilities */ /** Minecraft's GL capabilities */
public final GLCapabilities glCapabilities; public final GLCapabilities glCapabilities;
@@ -231,7 +231,7 @@ public class GLProxy
return uploadOverride; return uploadOverride;
} }
public boolean runningOnRenderThread() public static boolean runningOnRenderThread()
{ {
long currentContext = GLFW.glfwGetCurrentContext(); long currentContext = GLFW.glfwGetCurrentContext();
return currentContext != 0L; // if the context isn't null, it's the MC context return currentContext != 0L; // if the context isn't null, it's the MC context
@@ -243,12 +243,12 @@ public class GLProxy
// Worker Thread Runnables // // Worker Thread Runnables //
//=========================// //=========================//
public void queueRunningOnRenderThread(Runnable renderCall) public static void queueRunningOnRenderThread(Runnable renderCall)
{ {
StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace(); StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
this.renderThreadRunnableQueue.add(() -> this.runOpenGlCall(renderCall, stackTrace)); RENDER_THREAD_RUNNABLE_QUEUE.add(() -> runOpenGlCall(renderCall, stackTrace));
} }
private void runOpenGlCall(Runnable renderCall, StackTraceElement[] stackTrace) private static void runOpenGlCall(Runnable renderCall, StackTraceElement[] stackTrace)
{ {
try try
{ {
@@ -266,11 +266,11 @@ public class GLProxy
* Doesn't do any thread/GL Context validation. * Doesn't do any thread/GL Context validation.
* Running this outside of the render thread may cause crashes or other issues. * Running this outside of the render thread may cause crashes or other issues.
*/ */
public void runRenderThreadTasks() public static void runRenderThreadTasks()
{ {
long startTime = System.nanoTime(); long startTime = System.nanoTime();
Runnable runnable = this.renderThreadRunnableQueue.poll(); Runnable runnable = RENDER_THREAD_RUNNABLE_QUEUE.poll();
while(runnable != null) while(runnable != null)
{ {
runnable.run(); runnable.run();
@@ -283,7 +283,7 @@ public class GLProxy
break; break;
} }
runnable = this.renderThreadRunnableQueue.poll(); runnable = RENDER_THREAD_RUNNABLE_QUEUE.poll();
} }
} }
@@ -100,7 +100,7 @@ public class GLBuffer implements AutoCloseable
protected void create(boolean asBufferStorage) protected void create(boolean asBufferStorage)
{ {
if (!GLProxy.getInstance().runningOnRenderThread()) if (!GLProxy.runningOnRenderThread())
{ {
LodUtil.assertNotReach("Thread ["+Thread.currentThread()+"] tried to create a GLBuffer outside the MC render thread."); LodUtil.assertNotReach("Thread ["+Thread.currentThread()+"] tried to create a GLBuffer outside the MC render thread.");
} }
@@ -151,7 +151,7 @@ public class GLBuffer implements AutoCloseable
BUFFER_ID_TO_PHANTOM.remove(id); BUFFER_ID_TO_PHANTOM.remove(id);
} }
GLProxy.getInstance().queueRunningOnRenderThread(() -> GLProxy.queueRunningOnRenderThread(() ->
{ {
// destroy the buffer if it exists, // destroy the buffer if it exists,
// the buffer may not exist if the destroy method is called twice // the buffer may not exist if the destroy method is called twice
@@ -198,8 +198,8 @@ public class GLBuffer implements AutoCloseable
switch (uploadMethod) switch (uploadMethod)
{ {
case NONE: //case NONE:
return; // return;
case AUTO: case AUTO:
LodUtil.assertNotReach("GpuUploadMethod AUTO must be resolved before call to uploadBuffer()!"); LodUtil.assertNotReach("GpuUploadMethod AUTO must be resolved before call to uploadBuffer()!");
case BUFFER_STORAGE: case BUFFER_STORAGE:
@@ -44,7 +44,7 @@ public class QuadElementBuffer extends GLElementBuffer
public int getCapacity() public int getCapacity()
{ {
return super.getSize() / GLEnums.getTypeSize(getType()); return super.getSize() / GLEnums.getTypeSize(this.getType());
} }
private static void buildBufferByte(int quadCount, ByteBuffer buffer) private static void buildBufferByte(int quadCount, ByteBuffer buffer)
@@ -140,7 +140,6 @@ public class QuadElementBuffer extends GLElementBuffer
return; return;
} }
int vertexCount = quadCount * 4; // 4 vertices per quad int vertexCount = quadCount * 4; // 4 vertices per quad
GLProxy gl = GLProxy.getInstance();
if (vertexCount < 255) if (vertexCount < 255)
{ // Reserve 1 for the reset index { // Reserve 1 for the reset index
@@ -158,7 +157,7 @@ public class QuadElementBuffer extends GLElementBuffer
ByteBuffer buffer = MemoryUtil.memAlloc(this.indicesCount * GLEnums.getTypeSize(this.type)); ByteBuffer buffer = MemoryUtil.memAlloc(this.indicesCount * GLEnums.getTypeSize(this.type));
buildBuffer(quadCount, buffer, this.type); buildBuffer(quadCount, buffer, this.type);
if (!gl.bufferStorageSupported) if (!GLProxy.getInstance().bufferStorageSupported)
{ {
this.bind(); this.bind();
@@ -95,7 +95,7 @@ public class ShaderProgram
for (int i = 0; i < attributes.length; i++) for (int i = 0; i < attributes.length; i++)
{ {
GL32.glBindAttribLocation(id, i, attributes[i]); GL32.glBindAttribLocation(this.id, i, attributes[i]);
} }
GL32.glLinkProgram(this.id); GL32.glLinkProgram(this.id);
@@ -25,6 +25,10 @@ public class DHDepthTexture
GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_S, GL13C.GL_CLAMP_TO_EDGE); GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_S, GL13C.GL_CLAMP_TO_EDGE);
GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_T, GL13C.GL_CLAMP_TO_EDGE); GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_T, GL13C.GL_CLAMP_TO_EDGE);
// 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);
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0);
GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, 0); GL43C.glBindTexture(GL43C.GL_TEXTURE_2D, 0);
} }
@@ -46,7 +46,7 @@ public class DhColorTexture
this.id = GL43C.glGenTextures(); this.id = GL43C.glGenTextures();
boolean isPixelFormatInteger = builder.internalFormat.getPixelFormat().isInteger(); boolean isPixelFormatInteger = builder.internalFormat.getPixelFormat().isInteger();
this.setupTexture(this.id, builder.width, builder.height, !isPixelFormatInteger); this.setupTexture(this.id, builder.width, builder.height, !isPixelFormatInteger); // this binds the texture
// Clean up after ourselves // Clean up after ourselves
// This is strictly defensive to ensure that other buggy code doesn't tamper with our textures // This is strictly defensive to ensure that other buggy code doesn't tamper with our textures
@@ -67,6 +67,10 @@ public class DhColorTexture
GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MAG_FILTER, allowsLinear ? GL11C.GL_LINEAR : GL11C.GL_NEAREST); GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_MAG_FILTER, allowsLinear ? GL11C.GL_LINEAR : GL11C.GL_NEAREST);
GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_S, GL13C.GL_CLAMP_TO_EDGE); GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_S, GL13C.GL_CLAMP_TO_EDGE);
GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_T, GL13C.GL_CLAMP_TO_EDGE); GL43C.glTexParameteri(GL11C.GL_TEXTURE_2D, GL11C.GL_TEXTURE_WRAP_T, GL13C.GL_CLAMP_TO_EDGE);
// 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);
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0);
} }
private void resizeTexture(int texture, int width, int height) private void resizeTexture(int texture, int width, int height)
@@ -59,8 +59,10 @@ public final class VertexPointer
/** Always aligned to 4 bytes */ /** Always aligned to 4 bytes */
public static VertexPointer addUnsignedBytePointer(boolean normalized, boolean useInteger) { return new VertexPointer(1, GL32.GL_UNSIGNED_BYTE, normalized, 4, useInteger); } public static VertexPointer addUnsignedBytePointer(boolean normalized, boolean useInteger) { return new VertexPointer(1, GL32.GL_UNSIGNED_BYTE, normalized, 4, useInteger); }
/** aligned to 4 bytes */ /** aligned to 4 bytes */
public static VertexPointer addUnsignedBytesPointer(int elementCount, boolean normalized, boolean useInteger) { return new VertexPointer(elementCount, GL32.GL_UNSIGNED_BYTE, normalized, _align(elementCount), useInteger); } public static VertexPointer addUnsignedBytesPointer(int elementCount, boolean normalized, boolean useInteger)
public static VertexPointer addUnsignedShortsPointer(int elementCount, boolean normalized, boolean useInteger) { return new VertexPointer(elementCount, GL32.GL_UNSIGNED_SHORT, normalized, _align(elementCount * 2), useInteger); } { return new VertexPointer(elementCount, GL32.GL_UNSIGNED_BYTE, normalized, _align(elementCount), useInteger); }
public static VertexPointer addUnsignedShortsPointer(int elementCount, boolean normalized, boolean useInteger)
{ return new VertexPointer(elementCount, GL32.GL_UNSIGNED_SHORT, normalized, _align(elementCount * 2), useInteger); }
public static VertexPointer addShortsPointer(int elementCount, boolean normalized, boolean useInteger) { return new VertexPointer(elementCount, GL32.GL_SHORT, normalized, _align(elementCount * 2), useInteger); } public static VertexPointer addShortsPointer(int elementCount, boolean normalized, boolean useInteger) { return new VertexPointer(elementCount, GL32.GL_SHORT, normalized, _align(elementCount * 2), useInteger); }
public static VertexPointer addIntPointer(boolean normalized, boolean useInteger) { return new VertexPointer(1, GL32.GL_INT, normalized, 4, useInteger); } public static VertexPointer addIntPointer(boolean normalized, boolean useInteger) { return new VertexPointer(1, GL32.GL_INT, normalized, 4, useInteger); }
public static VertexPointer addIVec2Pointer(boolean normalized, boolean useInteger) { return new VertexPointer(2, GL32.GL_INT, normalized, 8, useInteger); } public static VertexPointer addIVec2Pointer(boolean normalized, boolean useInteger) { return new VertexPointer(2, GL32.GL_INT, normalized, 8, useInteger); }
@@ -38,7 +38,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRen
import com.seibel.distanthorizons.core.util.math.Mat4f; import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Vec3d; import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.math.Vec3f; import com.seibel.distanthorizons.core.util.math.Vec3f;
import org.apache.logging.log4j.LogManager;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
@@ -213,7 +212,7 @@ public class DebugRenderer
// particle rendering // particle rendering
BoxParticle head = null; BoxParticle head = null;
while ((head = this.particles.poll()) != null && head.isDead(System.nanoTime())) while ((head = this.particles.poll()) != null && head.isDead())
{ /* remove dead particles */ } { /* remove dead particles */ }
if (head != null) if (head != null)
{ {
@@ -311,52 +310,50 @@ public class DebugRenderer
public static final class BoxParticle implements Comparable<BoxParticle> public static final class BoxParticle implements Comparable<BoxParticle>
{ {
public Box box; public Box box;
public long startTime; public long startMsTime;
public long duration; public long durationInMs;
public float yChange; public float yChange;
public BoxParticle(Box box, long startTime, long duration, float yChange) private BoxParticle(Box box, long startMsTime, long durationInMs, float yChange)
{ {
this.box = box; this.box = box;
this.startTime = startTime; this.startMsTime = startMsTime;
this.duration = duration; this.durationInMs = durationInMs;
this.yChange = yChange; this.yChange = yChange;
} }
public BoxParticle(Box box, long nanoSecondDuratoin, float yChange) { this(box, System.nanoTime(), nanoSecondDuratoin, yChange); } public BoxParticle(Box box, double secondDuration, float yChange) { this(box, System.currentTimeMillis(), (long) (secondDuration * 1_000), yChange); }
public BoxParticle(Box box, double secondDuration, float yChange) { this(box, System.nanoTime(), (long) (secondDuration * 1000000000), yChange); }
@Override @Override
public int compareTo(@NotNull BoxParticle particle) public int compareTo(@NotNull BoxParticle particle)
{ {
return Long.compare(this.startTime + this.duration, particle.startTime + particle.duration); return Long.compare(this.startMsTime + this.durationInMs, particle.startMsTime + particle.durationInMs);
} }
public Box getBox() public Box getBox()
{ {
long now = System.nanoTime(); long nowMs = System.currentTimeMillis();
float percent = (now - this.startTime) / (float) this.duration; float percent = (nowMs - this.startMsTime) / (float) this.durationInMs;
percent = (float) Math.pow(percent, 4); percent = (float) Math.pow(percent, 4);
float yDiff = this.yChange * percent; float yDiff = this.yChange * percent;
return new Box(new Vec3f(this.box.minPos.x, this.box.minPos.y + yDiff, this.box.minPos.z), new Vec3f(this.box.maxPos.x, this.box.maxPos.y + yDiff, this.box.maxPos.z), this.box.color); return new Box(new Vec3f(this.box.minPos.x, this.box.minPos.y + yDiff, this.box.minPos.z), new Vec3f(this.box.maxPos.x, this.box.maxPos.y + yDiff, this.box.maxPos.z), this.box.color);
} }
public boolean isDead(long time) { return (time - this.startTime) > this.duration; } public boolean isDead() { return (System.currentTimeMillis() - this.startMsTime) > this.durationInMs; }
} }
public static final class BoxWithLife implements IDebugRenderable, Closeable public static final class BoxWithLife implements IDebugRenderable, Closeable
{ {
public Box box; public Box box;
public BoxParticle particaleOnClose; public BoxParticle particleOnClose;
public BoxWithLife(Box box, long ns, float yChange, Color deathColor) public BoxWithLife(Box box, long ns, float yChange, Color deathColor)
{ {
this.box = box; this.box = box;
this.particaleOnClose = new BoxParticle(new Box(box.minPos, box.maxPos, deathColor), -1, ns, yChange); this.particleOnClose = new BoxParticle(new Box(box.minPos, box.maxPos, deathColor), -1, ns, yChange);
register(this, null); register(this, null);
} }
@@ -366,7 +363,7 @@ public class DebugRenderer
public BoxWithLife(Box box, double s, float yChange, Color deathColor) public BoxWithLife(Box box, double s, float yChange, Color deathColor)
{ {
this.box = box; this.box = box;
this.particaleOnClose = new BoxParticle(new Box(box.minPos, box.maxPos, deathColor), s, yChange); this.particleOnClose = new BoxParticle(new Box(box.minPos, box.maxPos, deathColor), s, yChange);
} }
public BoxWithLife(Box box, double s, float yChange) { this(box, s, yChange, box.color); } public BoxWithLife(Box box, double s, float yChange) { this(box, s, yChange, box.color); }
@@ -377,7 +374,7 @@ public class DebugRenderer
@Override @Override
public void close() public void close()
{ {
makeParticle(new BoxParticle(this.particaleOnClose.getBox(), System.nanoTime(), this.particaleOnClose.duration, this.particaleOnClose.yChange)); makeParticle(new BoxParticle(this.particleOnClose.getBox(), System.nanoTime(), this.particleOnClose.durationInMs, this.particleOnClose.yChange));
unregister(this, null); unregister(this, null);
} }
@@ -31,6 +31,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLW
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL43C;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -95,11 +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);
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);
} }
@@ -64,7 +64,7 @@ public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShade
public int uNoiseDropoff = -1; public int uNoiseDropoff = -1;
// Debug Uniform // Debug Uniform
public int uWhiteWorld = -1; public int uIsWhiteWorld = -1;
@@ -102,7 +102,7 @@ public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShade
this.uNoiseDropoff = this.getUniformLocation("uNoiseDropoff"); this.uNoiseDropoff = this.getUniformLocation("uNoiseDropoff");
// Debug Uniform // Debug Uniform
this.uWhiteWorld = this.getUniformLocation("uWhiteWorld"); this.uIsWhiteWorld = this.getUniformLocation("uIsWhiteWorld");
// TODO: Add better use of the LODFormat thing // TODO: Add better use of the LODFormat thing
@@ -192,7 +192,7 @@ public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShade
this.setUniform(this.uNoiseDropoff, Config.Client.Advanced.Graphics.NoiseTexture.noiseDropoff.get()); this.setUniform(this.uNoiseDropoff, Config.Client.Advanced.Graphics.NoiseTexture.noiseDropoff.get());
// Debug // Debug
this.setUniform(this.uWhiteWorld, Config.Client.Advanced.Debugging.enableWhiteWorld.get()); this.setUniform(this.uIsWhiteWorld, Config.Client.Advanced.Debugging.enableWhiteWorld.get());
// Clip Uniform // Clip Uniform
float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocksForFading(renderParameters.partialTicks); float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocksForFading(renderParameters.partialTicks);
@@ -27,6 +27,7 @@ import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL43C;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -87,11 +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);
GL43C.glTexParameteri(GL43C.GL_TEXTURE_2D, GL43C.GL_TEXTURE_MAX_LEVEL, 0);
}
} }
@@ -42,6 +42,7 @@ import com.seibel.distanthorizons.core.render.renderer.shaders.*;
import com.seibel.distanthorizons.core.util.math.Mat4f; import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.math.Vec3d; import com.seibel.distanthorizons.core.util.math.Vec3d;
import com.seibel.distanthorizons.core.util.objects.SortedArraySet; import com.seibel.distanthorizons.core.util.objects.SortedArraySet;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper;
@@ -69,6 +70,7 @@ public class LodRenderer
.maxCountPerSecond(4) .maxCountPerSecond(4)
.build(); .build();
private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class); private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class); private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
private static final IIrisAccessor IRIS_ACCESSOR = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class); private static final IIrisAccessor IRIS_ACCESSOR = ModAccessorInjector.INSTANCE.get(IIrisAccessor.class);
@@ -174,6 +176,13 @@ public class LodRenderer
return; return;
} }
// only do this once, that way they can still be reverted if desired
if (Config.Client.Advanced.Graphics.overrideVanillaGraphicsSettings.get())
{
MC.disableVanillaClouds();
MC.disableVanillaChunkFadeIn();
}
this.renderObjectsCreated = true; this.renderObjectsCreated = true;
} }
@@ -484,12 +493,8 @@ public class LodRenderer
return false; return false;
} }
if (!GLProxy.hasInstance()) // GLProxy should have already been created by this point, but just in case create it now
{ GLProxy.getInstance();
// shouldn't normally happen, but just in case
LOGGER.warn("Renderer setup called but GLProxy has not yet been setup!");
return false;
}
@@ -27,6 +27,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLW
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.util.math.Mat4f; import com.seibel.distanthorizons.core.util.math.Mat4f;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL43C;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
@@ -87,10 +88,17 @@ 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);
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);
} }
@@ -337,7 +337,7 @@ public class RenderableBoxGroup
@Override @Override
public void close() public void close()
{ {
GLProxy.getInstance().queueRunningOnRenderThread(() -> GLProxy.queueRunningOnRenderThread(() ->
{ {
if (this.instanceChunkPosVbo != 0) if (this.instanceChunkPosVbo != 0)
{ {
@@ -164,10 +164,11 @@ public class FogShader extends AbstractShaderRenderer
// Fog uniforms // Fog uniforms
this.shader.setUniform(this.uFogColor, MC_RENDER.isFogStateSpecial() ? this.getSpecialFogColor(partialTicks) : this.getFogColor(partialTicks)); this.shader.setUniform(this.uFogColor, this.getFogColor(partialTicks));
this.shader.setUniform(this.uFogScale, 1.f / lodDrawDistance); this.shader.setUniform(this.uFogScale, 1.f / lodDrawDistance);
this.shader.setUniform(this.uFogVerticalScale, 1.f / MC.getWrappedClientLevel().getMaxHeight()); this.shader.setUniform(this.uFogVerticalScale, 1.f / MC.getWrappedClientLevel().getMaxHeight());
this.shader.setUniform(this.uFullFogMode, MC_RENDER.isFogStateSpecial() ? 1 : 0); // only used for debugging
this.shader.setUniform(this.uFullFogMode, 0); // 1 = render everything with fog color // 7 = use debug rendering
// fog config // fog config
@@ -177,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);
@@ -229,7 +238,6 @@ public class FogShader extends AbstractShaderRenderer
return fogColor; return fogColor;
} }
private Color getSpecialFogColor(float partialTicks) { return MC_RENDER.getSpecialFogColor(partialTicks); }
public void setProjectionMatrix(Mat4f projectionMatrix) public void setProjectionMatrix(Mat4f projectionMatrix)
{ {
@@ -25,8 +25,10 @@ import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
import com.seibel.distanthorizons.core.render.renderer.LodRenderer; import com.seibel.distanthorizons.core.render.renderer.LodRenderer;
import com.seibel.distanthorizons.core.render.renderer.SSAORenderer; import com.seibel.distanthorizons.core.render.renderer.SSAORenderer;
import com.seibel.distanthorizons.core.render.renderer.ScreenQuad; import com.seibel.distanthorizons.core.render.renderer.ScreenQuad;
import com.seibel.distanthorizons.core.util.NumberUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f; import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.coreapi.util.MathUtil;
import org.lwjgl.opengl.GL32; import org.lwjgl.opengl.GL32;
/** /**
@@ -58,6 +60,7 @@ public class SSAOShader extends AbstractShaderRenderer
public int uMinLight; public int uMinLight;
public int uBias; public int uBias;
public int uDepthMap; public int uDepthMap;
public int uFadeDistanceInBlocks;
@@ -81,6 +84,7 @@ public class SSAOShader extends AbstractShaderRenderer
this.uMinLight = this.shader.getUniformLocation("uMinLight"); this.uMinLight = this.shader.getUniformLocation("uMinLight");
this.uBias = this.shader.getUniformLocation("uBias"); this.uBias = this.shader.getUniformLocation("uBias");
this.uDepthMap = this.shader.getUniformLocation("uDepthMap"); this.uDepthMap = this.shader.getUniformLocation("uDepthMap");
this.uFadeDistanceInBlocks = this.shader.getUniformLocation("uFadeDistanceInBlocks");
} }
@@ -120,6 +124,10 @@ public class SSAOShader extends AbstractShaderRenderer
this.shader.setUniform(this.uBias, bias.floatValue()); this.shader.setUniform(this.uBias, bias.floatValue());
GL32.glUniform1i(this.uDepthMap, 0); GL32.glUniform1i(this.uDepthMap, 0);
float fadeDistanceInBlocks = Config.Client.Advanced.Graphics.Ssao.fadeDistanceInBlocks.get().floatValue();
fadeDistanceInBlocks = MathUtil.clamp(0.0f, fadeDistanceInBlocks, Float.MAX_VALUE); // clamp to prevent accidentally setting a negative number
this.shader.setUniform(this.uFadeDistanceInBlocks, fadeDistanceInBlocks);
} }
@@ -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);
}
}
@@ -0,0 +1,32 @@
package com.seibel.distanthorizons.core.util;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.pos.DhSectionPos;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
public class WorldGenUtil
{
/** will always return true if a generation max radius isn't set */
public static boolean isPosInWorldGenRange(
long requestedPos,
int centerChunkX, int centerChunkZ,
int maxChunkRadius)
{
if (Config.Common.WorldGenerator.generationMaxChunkRadius.get() <= 0)
{
return true;
}
DhBlockPos centerBlockPos = new DhChunkPos(centerChunkX, centerChunkZ).centerBlockPos();
int blockDistanceFromCenter = DhSectionPos.getChebyshevSignedBlockDistance(requestedPos, centerBlockPos);
int maxBlockRadius = maxChunkRadius * LodUtil.CHUNK_WIDTH;
boolean requestInRadius = (blockDistanceFromCenter <= maxBlockRadius);
return requestInRadius;
}
}
@@ -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(); }
}
@@ -26,7 +26,6 @@ import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import it.unimi.dsi.fastutil.bytes.ByteArrayList; import it.unimi.dsi.fastutil.bytes.ByteArrayList;
import net.jpountz.lz4.LZ4FrameInputStream; import net.jpountz.lz4.LZ4FrameInputStream;
import org.apache.logging.log4j.LogManager;
import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.tukaani.xz.ResettableArrayCache; import org.tukaani.xz.ResettableArrayCache;
import org.tukaani.xz.XZInputStream; import org.tukaani.xz.XZInputStream;
@@ -61,7 +60,7 @@ public class DhDataInputStream extends DataInputStream
{ {
// Z_Std handling compression outside the stream provides a significant performance boost // Z_Std handling compression outside the stream provides a significant performance boost
ByteArrayInputStream byteArrayInputStream; ByteArrayInputStream byteArrayInputStream;
if (compressionMode == EDhApiDataCompressionMode.Z_STD) if (compressionMode == EDhApiDataCompressionMode.Z_STD_BLOCK)
{ {
byteArrayInputStream = new ByteArrayInputStream(Zstd.decompress(byteArray)); byteArrayInputStream = new ByteArrayInputStream(Zstd.decompress(byteArray));
} }
@@ -87,7 +86,7 @@ public class DhDataInputStream extends DataInputStream
return stream; return stream;
case LZ4: case LZ4:
return new LZ4FrameInputStream(stream); return new LZ4FrameInputStream(stream);
case Z_STD: case Z_STD_BLOCK:
// ZStd compression should be handled before this point // ZStd compression should be handled before this point
// just return the stream // just return the stream
return stream; return stream;
@@ -20,14 +20,12 @@
package com.seibel.distanthorizons.core.util.objects.dataStreams; package com.seibel.distanthorizons.core.util.objects.dataStreams;
import com.github.luben.zstd.Zstd; import com.github.luben.zstd.Zstd;
import com.github.luben.zstd.ZstdOutputStream;
import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import it.unimi.dsi.fastutil.bytes.ByteArrayList; import it.unimi.dsi.fastutil.bytes.ByteArrayList;
import net.jpountz.lz4.LZ4Factory; import net.jpountz.lz4.LZ4Factory;
import net.jpountz.lz4.LZ4FrameOutputStream; import net.jpountz.lz4.LZ4FrameOutputStream;
import net.jpountz.xxhash.XXHashFactory; import net.jpountz.xxhash.XXHashFactory;
import org.apache.logging.log4j.LogManager;
import com.seibel.distanthorizons.core.logging.DhLogger; import com.seibel.distanthorizons.core.logging.DhLogger;
import org.tukaani.xz.*; import org.tukaani.xz.*;
@@ -85,7 +83,7 @@ public class DhDataOutputStream extends DataOutputStream
//LZ4Factory.nativeInstance().fastCompressor(), //LZ4Factory.nativeInstance().fastCompressor(),
//XXHashFactory.nativeInstance().hash32(), //XXHashFactory.nativeInstance().hash32(),
LZ4FrameOutputStream.FLG.Bits.BLOCK_INDEPENDENCE); LZ4FrameOutputStream.FLG.Bits.BLOCK_INDEPENDENCE);
case Z_STD: case Z_STD_BLOCK:
// ZStd compression should be handled after the stream is closed // ZStd compression should be handled after the stream is closed
// just return the stream // just return the stream
return stream; return stream;
@@ -127,7 +125,7 @@ public class DhDataOutputStream extends DataOutputStream
this.outputByteArray.clear(); this.outputByteArray.clear();
if (this.compressionMode == EDhApiDataCompressionMode.Z_STD) if (this.compressionMode == EDhApiDataCompressionMode.Z_STD_BLOCK)
{ {
this.outputByteArray.addElements(0, Zstd.compress(this.wrappedByteStream.toByteArray(), 3)); this.outputByteArray.addElements(0, Zstd.compress(this.wrappedByteStream.toByteArray(), 3));
} }
@@ -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); }
@@ -126,8 +126,6 @@ public interface IChunkWrapper extends IBindable
IBiomeWrapper getBiome(int relX, int relY, int relZ); IBiomeWrapper getBiome(int relX, int relY, int relZ);
boolean isStillValid();
//========================// //========================//
@@ -32,33 +32,11 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindab
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import org.apache.logging.log4j.Level; import org.apache.logging.log4j.Level;
/**
* Contains everything related to the Minecraft object.
*
* @author James Seibel
* @version 2022-8-20
*/
public interface IMinecraftClientWrapper extends IBindable public interface IMinecraftClientWrapper extends IBindable
{ {
//================// //======================//
// helper methods // // multiplayer handling //
//================// //======================//
/**
* This should be called at the beginning of every frame to
* clear any Minecraft data that becomes out of date after a frame. <br> <br>
* <p>
* LightMaps and other time sensitive objects fall in this category. <br> <br>
* <p>
* This doesn't affect OpenGL objects in any way.
*/
void clearFrameObjectCache();
//=================//
// method wrappers //
//=================//
float getShade(EDhDirection lodDirection);
boolean hasSinglePlayerServer(); boolean hasSinglePlayerServer();
boolean clientConnectedToDedicatedServer(); boolean clientConnectedToDedicatedServer();
@@ -69,24 +47,26 @@ public interface IMinecraftClientWrapper extends IBindable
String getCurrentServerIp(); String getCurrentServerIp();
String getCurrentServerVersion(); String getCurrentServerVersion();
//=============//
// Simple gets //
//=============// //=================//
// player handling //
//=================//
boolean playerExists(); boolean playerExists();
UUID getPlayerUUID();
String getUsername();
// TODO returning null would be easier to understand but might make things harder to parse in some cases
/** @return (0,0,0) if no player is loaded */ /** @return (0,0,0) if no player is loaded */
DhBlockPos getPlayerBlockPos(); DhBlockPos getPlayerBlockPos();
// TODO returning null would be easier to understand but might make things harder to parse in some cases
/** @return (0,0) if no player is loaded */ /** @return (0,0) if no player is loaded */
DhChunkPos getPlayerChunkPos(); DhChunkPos getPlayerChunkPos();
//================//
// level handling //
//================//
/** /**
* Returns the level the client is currently in. <br> * Returns the level the client is currently in. <br>
* Returns null if the client isn't in a level. * Returns null if the client isn't in a level.
@@ -98,49 +78,37 @@ public interface IMinecraftClientWrapper extends IBindable
*/ */
IClientLevelWrapper getWrappedClientLevel(boolean bypassLevelKeyManager); IClientLevelWrapper getWrappedClientLevel(boolean bypassLevelKeyManager);
IProfilerWrapper getProfiler();
/** Returns all worlds available to the server */
ArrayList<ILevelWrapper> getAllServerWorlds(); //===========//
// messaging //
//===========//
void sendChatMessage(String string); void sendChatMessage(String string);
/** Will default to sending a chat message if not supported by the current MC version */
/**
* Will default to sending a chat message if not supported by
* the current MC version (1.19.2 and older).
*/
void sendOverlayMessage(String string); void sendOverlayMessage(String string);
/** Sends the given message to chat with a formatted prefix and color based on the log level. */
default void logToChat(Level logLevel, String message)
{ //==========================//
String prefix = "[" + ModInfo.READABLE_NAME + "] "; // vanilla option overrides //
if (logLevel == Level.ERROR) //==========================//
{
prefix += "\u00A74"; void disableVanillaClouds();
}
else if (logLevel == Level.WARN) void disableVanillaChunkFadeIn();
{
prefix += "\u00A76";
}
else if (logLevel == Level.INFO) //======//
{ // misc //
prefix += "\u00A7f"; //======//
}
else if (logLevel == Level.DEBUG) IProfilerWrapper getProfiler();
{
prefix += "\u00A77";
}
else if (logLevel == Level.TRACE)
{
prefix += "\u00A78";
}
else
{
prefix += "\u00A7f";
}
prefix += "\u00A7l\u00A7u";
prefix += logLevel.name();
prefix += ":\u00A7r ";
this.sendChatMessage(prefix + message);
}
/** /**
* Crashes Minecraft, displaying the given errorMessage <br> <br> * Crashes Minecraft, displaying the given errorMessage <br> <br>
@@ -150,15 +118,17 @@ public interface IMinecraftClientWrapper extends IBindable
* Error: <strong>ExceptionClass: exceptionErrorMessage</strong> <br> * Error: <strong>ExceptionClass: exceptionErrorMessage</strong> <br>
* Exit Code: -1 <br> * Exit Code: -1 <br>
*/ */
void crashMinecraft(String errorMessage, Throwable exception); //FIXME: Move to IMinecraftSharedWrapper void crashMinecraft(String errorMessage, Throwable exception);
//=============//
// mod support //
//=============//
/** used for Optifine */
Object getOptionsObject(); Object getOptionsObject();
/**
* Executes the given task on Minecraft's render thread.
* @deprecated use {@link GLProxy#runningOnRenderThread()} instead
*/
@Deprecated
void executeOnRenderThread(Runnable runnable);
} }
@@ -21,6 +21,7 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.minecraft;
import java.awt.Color; import java.awt.Color;
import com.seibel.distanthorizons.core.enums.EDhDirection;
import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper;
import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable;
import com.seibel.distanthorizons.core.util.math.Vec3d; import com.seibel.distanthorizons.core.util.math.Vec3d;
@@ -46,8 +47,6 @@ public interface IMinecraftRenderWrapper extends IBindable
Color getFogColor(float partialTicks); Color getFogColor(float partialTicks);
default Color getSpecialFogColor(float partialTicks) { return getFogColor(partialTicks); }
/** Unless you really need to know if the player is blind, use {@link IMinecraftRenderWrapper#isFogStateSpecial()} instead */ /** Unless you really need to know if the player is blind, use {@link IMinecraftRenderWrapper#isFogStateSpecial()} instead */
boolean isFogStateSpecial(); boolean isFogStateSpecial();
@@ -58,9 +57,6 @@ public interface IMinecraftRenderWrapper extends IBindable
/** Measured in chunks */ /** Measured in chunks */
int getRenderDistance(); int getRenderDistance();
int getScreenWidth();
int getScreenHeight();
boolean mcRendersToFrameBuffer(); boolean mcRendersToFrameBuffer();
boolean runningLegacyOpenGL(); boolean runningLegacyOpenGL();
@@ -82,5 +78,8 @@ public interface IMinecraftRenderWrapper extends IBindable
@Nullable @Nullable
ILightMapWrapper getLightmapWrapper(@NotNull ILevelWrapper level); ILightMapWrapper getLightmapWrapper(@NotNull ILevelWrapper level);
float getShade(EDhDirection lodDirection);
} }
@@ -23,7 +23,6 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindab
import java.io.File; import java.io.File;
//TODO: Maybe have IMCClientWrapper & IMCDedicatedWrapper extend this interface???
public interface IMinecraftSharedWrapper extends IBindable public interface IMinecraftSharedWrapper extends IBindable
{ {
boolean isDedicatedServer(); boolean isDedicatedServer();
@@ -32,6 +31,6 @@ public interface IMinecraftSharedWrapper extends IBindable
int getPlayerCount(); int getPlayerCount();
void setPreventAutoPause(boolean preventAutoPause);
} }
@@ -79,58 +79,6 @@ public abstract class AbstractOptifineAccessor implements IOptifineAccessor
// interface methods // // interface methods //
//===================// //===================//
//@Override
@Deprecated
public EDhApiFogDrawMode getFogDrawMode()
{
if (this.ofFogField == null)
{
// either optifine isn't installed,
// the variable name was changed, or
// the setup method wasn't called yet.
return EDhApiFogDrawMode.FOG_ENABLED;
}
int returnNum = 0;
try
{
returnNum = (int) this.ofFogField.get(this.mcOptionsObject);
}
catch (IllegalArgumentException | IllegalAccessException e)
{
LOGGER.error("Unable to get project fog draw mode from Optifine, error: ["+e.getMessage()+"].", e);
}
switch (returnNum)
{
default:
case 0: // optifine's "default" option,
// it should never be used, so default to fog Enabled
// normal options
case 1: // fast
case 2: // fancy
return EDhApiFogDrawMode.FOG_ENABLED;
case 3: // off
return EDhApiFogDrawMode.FOG_DISABLED;
}
}
@Override
public double getRenderResolutionMultiplier()
{
/*
* TODO remove comment when done, this is just here as reference
* Returns the percentage multiplier of the screen's current resolution. <br>
* 1.0 = 100% <br>
* 1.5 = 150% <br>
*/
// TODO
return 1.0;
}
public boolean getIsShaderActive() public boolean getIsShaderActive()
{ {
@@ -27,11 +27,4 @@ import java.util.HashSet;
public interface IOptifineAccessor extends IModAccessor public interface IOptifineAccessor extends IModAccessor
{ {
/**
* Returns the percentage multiplier of the screen's current resolution. <br>
* 1.0 = 100% <br>
* 1.5 = 150% <br>
*/
double getRenderResolutionMultiplier();
} }
@@ -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();
@@ -208,6 +208,10 @@
"Blur Radius", "Blur Radius",
"distanthorizons.config.client.advanced.graphics.ssao.blurRadius.@tooltip": "distanthorizons.config.client.advanced.graphics.ssao.blurRadius.@tooltip":
"The radius, measured in pixels, that blurring is calculated for the SSAO. \nHigher numbers will reduce banding at the cost of GPU performance.", "The radius, measured in pixels, that blurring is calculated for the SSAO. \nHigher numbers will reduce banding at the cost of GPU performance.",
"distanthorizons.config.client.advanced.graphics.ssao.fadeDistanceInBlocks":
"Fade Distance",
"distanthorizons.config.client.advanced.graphics.ssao.fadeDistanceInBlocks.@tooltip":
"The distance in blocks from the camera where the SSAO will fade out to. \nThis is done to prevent banding and noise at extreme distances.",
@@ -385,6 +389,11 @@
"distanthorizons.config.client.advanced.graphics.culling.ignoredRenderCaveBlockCsv.@tooltip": "distanthorizons.config.client.advanced.graphics.culling.ignoredRenderCaveBlockCsv.@tooltip":
"A comma separated list of block resource locations that shouldn't be rendered \nif they are in a 0 sky light underground area. \nNote: air is always included in this list.", "A comma separated list of block resource locations that shouldn't be rendered \nif they are in a 0 sky light underground area. \nNote: air is always included in this list.",
"distanthorizons.config.client.advanced.graphics.overrideVanillaGraphicsSettings":
"Override Vanilla Settings",
"distanthorizons.config.client.advanced.graphics.overrideVanillaGraphicsSettings.@tooltip":
"If true some vanilla graphics settings will be automatically changed \nduring DH setup to provide a better experience. \n\nIE disabling vanilla clouds (which render on top of DH LODs), \nand chunk fading (DH already fades MC chunks).",
"distanthorizons.config.client.advanced.graphics.experimental": "distanthorizons.config.client.advanced.graphics.experimental":
@@ -728,6 +737,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",
@@ -980,7 +991,7 @@
"Uncompressed", "Uncompressed",
"distanthorizons.config.enum.EDhApiDataCompressionMode.LZ4": "distanthorizons.config.enum.EDhApiDataCompressionMode.LZ4":
"Fastest/Big - LZ4", "Fastest/Big - LZ4",
"distanthorizons.config.enum.EDhApiDataCompressionMode.Z_STD": "distanthorizons.config.enum.EDhApiDataCompressionMode.Z_STD_BLOCK":
"Fastest/Small - Z_STD - Block", "Fastest/Small - Z_STD - Block",
"distanthorizons.config.enum.EDhApiDataCompressionMode.Z_STD_STREAM": "distanthorizons.config.enum.EDhApiDataCompressionMode.Z_STD_STREAM":
"Fast/Small - Z_STD - Stream", "Fast/Small - Z_STD - Stream",
+24 -9
View File
@@ -17,6 +17,7 @@ uniform float uMinLight;
uniform float uBias; uniform float uBias;
uniform mat4 uInvProj; uniform mat4 uInvProj;
uniform mat4 uProj; uniform mat4 uProj;
uniform float uFadeDistanceInBlocks;
const float EPSILON = 1.e-6; const float EPSILON = 1.e-6;
const float GOLDEN_ANGLE = 2.39996323; const float GOLDEN_ANGLE = 2.39996323;
@@ -99,16 +100,30 @@ void main()
{ {
vec3 viewPos = calcViewPosition(vec3(TexCoord, fragmentDepth)); vec3 viewPos = calcViewPosition(vec3(TexCoord, fragmentDepth));
#ifdef GL_ARB_derivative_control // fading is done to prevent banding/noise
// Get higher precision derivatives when available // at super far distance
vec3 viewNormal = cross(dFdxFine(viewPos.xyz), dFdyFine(viewPos.xyz)); float distanceFromCamera = length(viewPos);
#else float fadeDistance = uFadeDistanceInBlocks;
vec3 viewNormal = cross(dFdx(viewPos.xyz), dFdy(viewPos.xyz)); if (distanceFromCamera < fadeDistance)
#endif {
#ifdef GL_ARB_derivative_control
// Get higher precision derivatives when available
vec3 viewNormal = cross(dFdxFine(viewPos.xyz), dFdyFine(viewPos.xyz));
#else
vec3 viewNormal = cross(dFdx(viewPos.xyz), dFdy(viewPos.xyz));
#endif
viewNormal = normalize(viewNormal); viewNormal = normalize(viewNormal);
occlusion = GetSpiralOcclusion(TexCoord, viewPos, viewNormal);
occlusion = GetSpiralOcclusion(TexCoord, viewPos, viewNormal);
// linearly fade with distance
occlusion *= (fadeDistance - distanceFromCamera) / fadeDistance;
}
else
{
// we're out of range, no need to do any SSAO calculations
occlusion = 0.0;
}
} }
fragColor = vec4(vec3(1.0 - occlusion), 1.0); fragColor = vec4(vec3(1.0 - occlusion), 1.0);
@@ -8,13 +8,12 @@ out vec4 vertexColor;
out vec3 vertexWorldPos; out vec3 vertexWorldPos;
out float vertexYPos; out float vertexYPos;
uniform bool uWhiteWorld; uniform bool uIsWhiteWorld;
uniform mat4 uCombinedMatrix; uniform mat4 uCombinedMatrix;
uniform vec3 uModelOffset; uniform vec3 uModelOffset;
uniform float uWorldYOffset; uniform float uWorldYOffset;
uniform int uWorldSkyLight;
uniform sampler2D uLightMap; uniform sampler2D uLightMap;
uniform float uMircoOffset; uniform float uMircoOffset;
@@ -57,7 +56,7 @@ void main()
float light = (float(lights/16u)+0.5) / 16.0; float light = (float(lights/16u)+0.5) / 16.0;
vertexColor = vec4(texture(uLightMap, vec2(light, light2)).xyz, 1.0); vertexColor = vec4(texture(uLightMap, vec2(light, light2)).xyz, 1.0);
if (!uWhiteWorld) if (!uIsWhiteWorld)
{ {
vertexColor *= color; vertexColor *= color;
} }
@@ -27,7 +27,6 @@ import com.seibel.distanthorizons.coreapi.util.StringUtil;
import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.longs.LongArrayList;
//import org.apache.commons.compress.compressors.zstandard.ZstdCompressorOutputStream; //import org.apache.commons.compress.compressors.zstandard.ZstdCompressorOutputStream;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test;
import java.io.*; import java.io.*;
@@ -204,7 +203,7 @@ public class CompressionTest
public void Zstd() // middle of the road public void Zstd() // middle of the road
{ {
String compressorName = "Zstd"; String compressorName = "Zstd";
this.testCompressor(compressorName, EDhApiDataCompressionMode.Z_STD); this.testCompressor(compressorName, EDhApiDataCompressionMode.Z_STD_BLOCK);
} }
//@Test //@Test
@@ -321,7 +321,7 @@ public class DataSourceRepoTests
Assert.assertFalse(pooledDto.compressedWorldCompressionModeByteArray.isEmpty()); Assert.assertFalse(pooledDto.compressedWorldCompressionModeByteArray.isEmpty());
try (FullDataSourceV2 dataSource = pooledDto.createUnitTestDataSource(); try (FullDataSourceV2 dataSource = pooledDto.createUnitTestDataSource();
FullDataSourceV2DTO compressedDto = FullDataSourceV2DTO.CreateFromDataSource(dataSource, EDhApiDataCompressionMode.Z_STD)) FullDataSourceV2DTO compressedDto = FullDataSourceV2DTO.CreateFromDataSource(dataSource, EDhApiDataCompressionMode.Z_STD_BLOCK))
{ {
repo.save(compressedDto); repo.save(compressedDto);