diff --git a/Readme.md b/Readme.md index 71b9a1cb0..66dc761b3 100644 --- a/Readme.md +++ b/Readme.md @@ -1,10 +1,10 @@ -# +# This repo is for the Distant Horizons mod. The purpose of this submodule is to isolate code that isn't tied to a specific version of minecraft. This prevents us from having duplicate code; reducing errors and helping us port to different versions faster and easier. Check out the mod's main GitLab page here: -https://gitlab.com/jeseibel/distant-horizons +https://gitlab.com/distant-horizons-team/distant-horizons ## source code installation diff --git a/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiFogFalloff.java b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiFogFalloff.java index 3437cd00a..8fbe37a2a 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiFogFalloff.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiFogFalloff.java @@ -25,7 +25,7 @@ package com.seibel.distanthorizons.api.enums.rendering; * EXPONENTIAL_SQUARED
* * @author Leetom - * @version 2022-6-30 + * @version 2024-11-09 * @since API 2.0.0 */ public enum EDhApiFogFalloff @@ -35,8 +35,17 @@ public enum EDhApiFogFalloff // when removing items up the API major version - LINEAR, - EXPONENTIAL, - EXPONENTIAL_SQUARED, + LINEAR(0), + EXPONENTIAL(1), + EXPONENTIAL_SQUARED(2); + + + /** + * Stable version of {@link EDhApiFogFalloff#ordinal()} + * @since API 4.0.0 + */ + public final int value; + + EDhApiFogFalloff(int value) { this.value = value; } } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogMode.java b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogDirection.java similarity index 68% rename from api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogMode.java rename to api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogDirection.java index 81a847630..624b391f6 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogMode.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogDirection.java @@ -31,28 +31,28 @@ package com.seibel.distanthorizons.api.enums.rendering; * @version 2024-4-6 * @since API 2.0.0 */ -public enum EDhApiHeightFogMode +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), - BELOW_CAMERA(true, false, true), - ABOVE_AND_BELOW_CAMERA(true, true, true), - ABOVE_SET_HEIGHT(false, true, false), - BELOW_SET_HEIGHT(false, false, true), - ABOVE_AND_BELOW_SET_HEIGHT(false, true, true); + ABOVE_CAMERA (true, true, false), + BELOW_CAMERA (true, false, true), + ABOVE_AND_BELOW_CAMERA (true, true, true), + ABOVE_SET_HEIGHT (false, true, false), + BELOW_SET_HEIGHT (false, false, true), + ABOVE_AND_BELOW_SET_HEIGHT (false, true, true); public final boolean basedOnCamera; - public final boolean above; - public final boolean below; + public final boolean fogAppliesUp; + public final boolean fogAppliesDown; - EDhApiHeightFogMode(boolean basedOnCamera, boolean above, boolean below) + EDhApiHeightFogDirection(boolean basedOnCamera, boolean fogAppliesUp, boolean fogAppliesDown) { this.basedOnCamera = basedOnCamera; - this.above = above; - this.below = below; + this.fogAppliesUp = fogAppliesUp; + this.fogAppliesDown = fogAppliesDown; } } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogMixMode.java b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogMixMode.java index 56226f2da..c2e5e7f38 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogMixMode.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogMixMode.java @@ -20,10 +20,11 @@ package com.seibel.distanthorizons.api.enums.rendering; /** - * BASIC
- * IGNORE_HEIGHT
- * ADDITION
+ * SPHERICAL
+ * CYLINDRICAL
+ *
* MAX
+ * ADDITION
* MULTIPLY
* INVERSE_MULTIPLY
* LIMITED_ADDITION
@@ -37,14 +38,36 @@ package com.seibel.distanthorizons.api.enums.rendering; */ public enum EDhApiHeightFogMixMode { - BASIC, - IGNORE_HEIGHT, - ADDITION, - MAX, - MULTIPLY, - INVERSE_MULTIPLY, - LIMITED_ADDITION, - MULTIPLY_ADDITION, - INVERSE_MULTIPLY_ADDITION, - AVERAGE, + /** + * Basic just means the fog will be based on the fragment depth + * not on any special height calculation IE spherical fog.

+ * + * Not to be confused with {@link EDhApiHeightFogMixMode#CYLINDRICAL} + * which causes fog to only apply based on horizontal distance. + */ + SPHERICAL(0), + /** + * Fog is applied based on horizontal distance from the camera, + * IE cylindrical fog. + */ + CYLINDRICAL(1), + + MAX(2), + ADDITION(3), + MULTIPLY(4), + INVERSE_MULTIPLY(5), + LIMITED_ADDITION(6), + MULTIPLY_ADDITION(7), + INVERSE_MULTIPLY_ADDITION(8), + AVERAGE(9); + + + /** + * Stable version of {@link EDhApiFogFalloff#ordinal()} + * @since API 4.0.0 + */ + public final int value; + + EDhApiHeightFogMixMode(int value) { this.value = value; } + } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiHeightFogConfig.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiHeightFogConfig.java index 754cc01a9..f348f94ba 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiHeightFogConfig.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiHeightFogConfig.java @@ -21,7 +21,7 @@ package com.seibel.distanthorizons.api.interfaces.config.client; import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogFalloff; import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode; -import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMode; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogDirection; import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup; import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue; @@ -42,11 +42,14 @@ public interface IDhApiHeightFogConfig extends IDhApiConfigGroup /** Defines how the height fog mixes. */ IDhApiConfigValue heightFogMixMode(); - /** Defines how the height fog is drawn relative to the camera or world. */ - IDhApiConfigValue heightFogMode(); + /** + * Defines which direction height fog is drawn relative to the world. + * @since API 4.0.0 + */ + IDhApiConfigValue heightFogDirection(); /** - * Defines the height fog's base height if {@link IDhApiHeightFogConfig#heightFogMode()} + * Defines the height fog's base height if {@link IDhApiHeightFogConfig#heightFogDirection()} * is set to use a specific height. */ IDhApiConfigValue heightFogBaseHeight(); diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java index 658a483b0..53d1161f6 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java @@ -39,6 +39,13 @@ public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper /** @since API 4.0.0 */ String getDimensionName(); + + /** + * Returns a string intended to uniquely identify this level. + * + * @since API 4.0.0 + */ + String getDhIdentifier(); EDhApiLevelType getLevelType(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiHeightFogConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiHeightFogConfig.java index b65925172..05f3b3e43 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiHeightFogConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiHeightFogConfig.java @@ -21,7 +21,7 @@ package com.seibel.distanthorizons.core.api.external.methods.config.client; import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogFalloff; import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode; -import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMode; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogDirection; import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue; import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiHeightFogConfig; import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue; @@ -40,8 +40,8 @@ public class DhApiHeightFogConfig implements IDhApiHeightFogConfig { return new DhApiConfigValue(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMixMode); } @Override - public IDhApiConfigValue heightFogMode() - { return new DhApiConfigValue(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMode); } + public IDhApiConfigValue heightFogDirection() + { return new DhApiConfigValue(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDirection); } @Override public IDhApiConfigValue heightFogBaseHeight() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index e34b99959..8e5cde40a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -207,7 +207,7 @@ public class ClientApi { try { - LOGGER.info("Unloading client level [" + level + "]-["+level.getDimensionName()+"]."); + LOGGER.info("Unloading client level [" + level.getClass().getSimpleName() + "]-[" + level.getDhIdentifier() + "]."); if (level instanceof IServerKeyedClientLevel) { @@ -253,7 +253,7 @@ public class ClientApi try { - LOGGER.info("Loading client level [" + levelWrapper + "]-["+levelWrapper.getDimensionName()+"]."); + LOGGER.info("Loading client level [" + levelWrapper + "]-[" + levelWrapper.getDhIdentifier() + "]."); AbstractDhWorld world = SharedApi.getAbstractDhWorld(); if (world != null) @@ -312,28 +312,6 @@ public class ClientApi // render events // //===============// - public void rendererShutdownEvent() - { - LOGGER.info("Renderer shutting down."); - - IProfilerWrapper profiler = MC_CLIENT.getProfiler(); - profiler.push("DH-RendererShutdown"); - - profiler.pop(); - } - - public void rendererStartupEvent() - { - LOGGER.info("Renderer starting up."); - - IProfilerWrapper profiler = MC_CLIENT.getProfiler(); - profiler.push("DH-RendererStartup"); - - // make sure the GLProxy is created before the LodBufferBuilder needs it - GLProxy.getInstance(); - profiler.pop(); - } - public void clientTickEvent() { IProfilerWrapper profiler = MC_CLIENT.getProfiler(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index 9bc607734..05926b4f8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -101,7 +101,7 @@ public class ClientPluginChannelApi } else { - LOGGER.info("Unloading non-keyed level: [" + clientLevel.getDimensionName() + "]."); + LOGGER.info("Unloading non-keyed level: [" + clientLevel.getDhIdentifier() + "]."); this.levelUnloadHandler.accept(clientLevel); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java index 94890eb9c..d852c1504 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java @@ -22,12 +22,9 @@ package com.seibel.distanthorizons.core.api.internal; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelUnloadEvent; import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage; +import com.seibel.distanthorizons.core.world.*; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; -import com.seibel.distanthorizons.core.world.AbstractDhWorld; -import com.seibel.distanthorizons.core.world.DhClientServerWorld; -import com.seibel.distanthorizons.core.world.DhServerWorld; -import com.seibel.distanthorizons.core.world.IDhServerWorld; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -148,8 +145,13 @@ public class ServerApi public void serverPlayerJoinEvent(IServerPlayerWrapper player) { + if (DhApiWorldProxy.INSTANCE.worldLoaded() && DhApiWorldProxy.INSTANCE.getReadOnly()) + { + return; + } + IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); - LOGGER.info("Player [${player.getName()}] joined."); + LOGGER.info("Player ["+player.getName()+"] joined."); if (serverWorld != null) { serverWorld.addPlayer(player); @@ -157,8 +159,13 @@ public class ServerApi } public void serverPlayerDisconnectEvent(IServerPlayerWrapper player) { + if (DhApiWorldProxy.INSTANCE.worldLoaded() && DhApiWorldProxy.INSTANCE.getReadOnly()) + { + return; + } + IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); - LOGGER.info("Player [${player.getName()}] disconnected."); + LOGGER.info("Player ["+player.getName()+"] disconnected."); if (serverWorld != null) { serverWorld.removePlayer(player); @@ -166,8 +173,13 @@ public class ServerApi } public void serverPlayerLevelChangeEvent(IServerPlayerWrapper player, IServerLevelWrapper originLevel, IServerLevelWrapper destinationLevel) { + if (DhApiWorldProxy.INSTANCE.worldLoaded() && DhApiWorldProxy.INSTANCE.getReadOnly()) + { + return; + } + IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); - LOGGER.info("Player [${player.getName()}] changed level: [${originLevel.getKeyedLevelDimensionName()}] -> [${destinationLevel.getKeyedLevelDimensionName()}]."); + LOGGER.info("Player ["+player.getName()+"] changed level: ["+originLevel.getKeyedLevelDimensionName()+"] -> ["+destinationLevel.getKeyedLevelDimensionName()+"]."); if (serverWorld != null) { serverWorld.changePlayerLevel(player, originLevel, destinationLevel); @@ -176,6 +188,11 @@ public class ServerApi public void pluginMessageReceived(IServerPlayerWrapper player, @NotNull AbstractNetworkMessage message) { + if (DhApiWorldProxy.INSTANCE.worldLoaded() && DhApiWorldProxy.INSTANCE.getReadOnly()) + { + return; + } + IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); if (serverWorld != null) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index afc8d8788..4d02dd495 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -39,7 +39,6 @@ import com.seibel.distanthorizons.coreapi.ModInfo; import org.apache.logging.log4j.Logger; import org.lwjgl.util.tinyfd.TinyFileDialogs; -import javax.swing.*; import java.awt.*; import java.io.File; import java.util.*; @@ -549,40 +548,40 @@ public class Config public static class HeightFog { public static ConfigEntry heightFogMixMode = new ConfigEntry.Builder() - .set(EDhApiHeightFogMixMode.BASIC) + .set(EDhApiHeightFogMixMode.SPHERICAL) .comment("" + "How should height effect the fog thickness? \n" + "Note: height fog is combined with the other fog settings. \n" + "\n" - + EDhApiHeightFogMixMode.BASIC + ": No special height fog effect. Fog is calculated based on camera distance \n" - + EDhApiHeightFogMixMode.IGNORE_HEIGHT + ": Ignore height completely. Fog is only calculated with horizontal distance \n" - + EDhApiHeightFogMixMode.ADDITION + ": heightFog + farFog \n" + + EDhApiHeightFogMixMode.SPHERICAL + ": Fog is calculated based on camera distance. \n" + + EDhApiHeightFogMixMode.CYLINDRICAL + ": Ignore height, fog is calculated based on horizontal distance. \n" + + "\n" + EDhApiHeightFogMixMode.MAX + ": max(heightFog, farFog) \n" + + EDhApiHeightFogMixMode.ADDITION + ": heightFog + farFog \n" + EDhApiHeightFogMixMode.MULTIPLY + ": heightFog * farFog \n" + EDhApiHeightFogMixMode.INVERSE_MULTIPLY + ": 1 - (1-heightFog) * (1-farFog) \n" + EDhApiHeightFogMixMode.LIMITED_ADDITION + ": farFog + max(farFog, heightFog) \n" + EDhApiHeightFogMixMode.MULTIPLY_ADDITION + ": farFog + farFog * heightFog \n" + EDhApiHeightFogMixMode.INVERSE_MULTIPLY_ADDITION + ": farFog + 1 - (1-heightFog) * (1-farFog) \n" + EDhApiHeightFogMixMode.AVERAGE + ": farFog*0.5 + heightFog*0.5 \n" - + "\n" - + "Note: height fog settings are ignored if '" + EDhApiHeightFogMixMode.BASIC + "' or '" + EDhApiHeightFogMixMode.IGNORE_HEIGHT + "' are selected.") + + "\n") .build(); - public static ConfigEntry heightFogMode = new ConfigEntry.Builder() - .set(EDhApiHeightFogMode.ABOVE_AND_BELOW_CAMERA) + public static ConfigEntry heightFogDirection = new ConfigEntry.Builder() + .set(EDhApiHeightFogDirection.BELOW_SET_HEIGHT) .comment("" + "Where should the height fog start? \n" + "\n" - + EDhApiHeightFogMode.ABOVE_CAMERA + ": Height fog starts at the camera and goes towards the sky \n" - + EDhApiHeightFogMode.BELOW_CAMERA + ": Height fog starts at the camera and goes towards the void \n" - + EDhApiHeightFogMode.ABOVE_AND_BELOW_CAMERA + ": Height fog starts from the camera to goes towards both the sky and void \n" - + EDhApiHeightFogMode.ABOVE_SET_HEIGHT + ": Height fog starts from a set height and goes towards the sky \n" - + EDhApiHeightFogMode.BELOW_SET_HEIGHT + ": Height fog starts from a set height and goes towards the void \n" - + EDhApiHeightFogMode.ABOVE_AND_BELOW_SET_HEIGHT + ": Height fog starts from a set height and goes towards both the sky and void") + + EDhApiHeightFogDirection.ABOVE_CAMERA + ": Height fog starts at the camera and goes towards the sky \n" + + EDhApiHeightFogDirection.BELOW_CAMERA + ": Height fog starts at the camera and goes towards the void \n" + + EDhApiHeightFogDirection.ABOVE_AND_BELOW_CAMERA + ": Height fog starts from the camera to goes towards both the sky and void \n" + + EDhApiHeightFogDirection.ABOVE_SET_HEIGHT + ": Height fog starts from a set height and goes towards the sky \n" + + EDhApiHeightFogDirection.BELOW_SET_HEIGHT + ": Height fog starts from a set height and goes towards the void \n" + + EDhApiHeightFogDirection.ABOVE_AND_BELOW_SET_HEIGHT + ": Height fog starts from a set height and goes towards both the sky and void") .build(); public static ConfigEntry heightFogBaseHeight = new ConfigEntry.Builder() - .setMinDefaultMax(-4096.0, 70.0, 4096.0) + .setMinDefaultMax(-4096.0, 80.0, 4096.0) .comment("If the height fog is calculated around a set height, what is that height position?") .build(); @@ -596,7 +595,7 @@ public class Config .build(); public static ConfigEntry heightFogEnd = new ConfigEntry.Builder() - .setMinDefaultMax(FOG_RANGE_MIN, 1.0, FOG_RANGE_MAX) + .setMinDefaultMax(FOG_RANGE_MIN, 0.6, FOG_RANGE_MAX) .comment("" + "Should the end of the height fog be offset? \n" + "\n" @@ -605,7 +604,7 @@ public class Config .build(); public static ConfigEntry heightFogMin = new ConfigEntry.Builder() - .setMinDefaultMax(-5.0, 0.0, FOG_RANGE_MAX) + .setMinDefaultMax(0.0, 0.0, FOG_RANGE_MAX) .comment("" + "What is the minimum fog thickness? \n" + "\n" @@ -633,7 +632,7 @@ public class Config .build(); public static ConfigEntry heightFogDensity = new ConfigEntry.Builder() - .setMinDefaultMax(0.01, 2.5, 50.0) + .setMinDefaultMax(0.01, 20.0, 50.0) .comment("What is the height fog's density?") .build(); @@ -1268,6 +1267,19 @@ public class Config + "Expected Compression Ratio: 0.7\n" + "") .build(); + + public static ConfigEntry recalculateChunkHeightmaps = new ConfigEntry.Builder() + .set(false) + .comment("" + + "True: Recalculate chunk height maps before chunks can be used by DH.\n" + + " This can fix problems with worlds created by World Painter or \n" + + " other external tools where the heightmap format may be incorrect. \n" + + "False: Assume any height maps handled by Minecraft are correct. \n" + + "\n" + + "Fastest: False\n" + + "Most Compatible: True\n" + + "") + .build(); } public static class MultiThreading @@ -1531,11 +1543,11 @@ public class Config public static ConfigEntry levelKeyPrefix = new ConfigEntry.Builder() .setServersideShortName("levelKeyPrefix") - .set(getDefaultLevelKeyPrefix()) + .set("") .comment("" + "Prefix of the level keys sent to the clients.\n" - + "If the mod is running behind a proxy, each backend should use a unique value (an empty string is allowed for one of the servers).\n" - + "This value may be auto-generated if the mod is installed before the first start of the server.\n" + + "If the mod is running behind a proxy, each backend should use a unique value.\n" + + "If this value is empty, level key will be based on the server's seed hash.\n" + "") .build(); @@ -1622,21 +1634,6 @@ public class Config } } - private static String getDefaultLevelKeyPrefix() - { - IMinecraftSharedWrapper mcWrapper = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class); - if (mcWrapper.isDedicatedServer()) - { - return mcWrapper.isWorldNew() - ? "server" + ThreadLocalRandom.current().nextInt(1, 1000) - : ""; - } - else - { - return SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).getUsername(); - } - } - /** Guesses whether a dev environment is used based on the current folder path */ private static boolean isRunningInDevEnvironment() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java index cc44a96fd..f8e2b6202 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java @@ -19,9 +19,7 @@ package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding; -import com.seibel.distanthorizons.api.DhApi; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.render.glObject.GLProxy; @@ -30,7 +28,6 @@ import com.seibel.distanthorizons.core.render.renderer.LodRenderer; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.StatsMap; import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import org.apache.logging.log4j.Logger; import org.lwjgl.system.MemoryUtil; @@ -46,9 +43,6 @@ import java.util.concurrent.*; public class ColumnRenderBuffer implements AutoCloseable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - - private static final long MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS = 1_000_000; /** number of bytes a single quad takes */ public static final int QUADS_BYTE_SIZE = LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 4; @@ -59,110 +53,104 @@ public class ColumnRenderBuffer implements AutoCloseable - - public final DhBlockPos pos; + public final DhBlockPos blockPos; public boolean buffersUploaded = false; private GLVertexBuffer[] vbos; private GLVertexBuffer[] vbosTransparent; + private CompletableFuture uploadFuture = null; + //==============// // constructors // //==============// - public ColumnRenderBuffer(DhBlockPos pos) + public ColumnRenderBuffer(DhBlockPos blockPos) { - this.pos = pos; + this.blockPos = blockPos; this.vbos = new GLVertexBuffer[0]; this.vbosTransparent = new GLVertexBuffer[0]; } - - //==================// // buffer uploading // //==================// /** Should be run on a DH thread. */ - public void uploadBuffer(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod) throws InterruptedException + public synchronized CompletableFuture makeAndUploadBuffersAsync(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod) { - LodUtil.assertTrue(DhApi.isDhThread(), "Buffer uploading needs to be done on a DH thread to prevent locking up any MC threads."); + if (this.uploadFuture != null) + { + return this.uploadFuture; + } + this.uploadFuture = new CompletableFuture<>(); + + + + // make the buffers + ArrayList opaqueBuffers = builder.makeOpaqueVertexBuffers(); + ArrayList transparentBuffers = builder.makeTransparentVertexBuffers(); + + this.vbos = resizeBuffer(this.vbos, opaqueBuffers.size()); + this.vbosTransparent = resizeBuffer(this.vbosTransparent, transparentBuffers.size()); // upload on MC's render thread - CompletableFuture uploadFuture = new CompletableFuture<>(); - MC_CLIENT.executeOnRenderThread(() -> + GLProxy.getInstance().queueRunningOnRenderThread(() -> { try { - this.uploadBuffers(builder, gpuUploadMethod); - uploadFuture.complete(null); + // skip this event if requested + if (Thread.interrupted() || this.uploadFuture.isCancelled()) + { + throw new InterruptedException(); + } + + // upload on the render thread + uploadBuffersDirect(this.vbos, opaqueBuffers, gpuUploadMethod); + uploadBuffersDirect(this.vbosTransparent, transparentBuffers, gpuUploadMethod); + this.buffersUploaded = true; + + // success + this.uploadFuture.complete(this); + this.uploadFuture = null; } - catch (InterruptedException e) + catch (InterruptedException ignore) { - throw new CompletionException(e); + this.uploadFuture.complete(this); + this.uploadFuture = null; } - }); - - - try - { - // wait for the upload to finish - uploadFuture.get(5_000, TimeUnit.MILLISECONDS); - } - catch (ExecutionException e) - { - LOGGER.warn("Error uploading builder ["+builder+"] synchronously. Error: "+e.getMessage(), e); - } - catch (TimeoutException e) - { - // timeouts can be ignored because it generally means the - // MC Render thread executor was closed - //LOGGER.warn("Error uploading builder ["+builder+"] synchronously. Error: "+e.getMessage(), e); - } - } - private void uploadBuffers(LodQuadBuilder builder, EDhApiGpuUploadMethod method) throws InterruptedException - { - // uploading mapped buffers used to be done here, - // however due to a memory leak and complication with the previous code, - // now we only allow direct uploading. - // (There's also insufficient data to state whether mapped buffers are necessary - // for DH to upload without stuttering the main thread) - - this.vbos = makeAndUploadBuffers(builder, method, this.vbos, builder.makeOpaqueVertexBuffers()); - this.vbosTransparent = makeAndUploadBuffers(builder, method, this.vbosTransparent, builder.makeTransparentVertexBuffers()); - - this.buffersUploaded = true; - } - /** This resizes and returns the vbo array if necessary based on the amount of data needed for this area. */ - private static GLVertexBuffer[] makeAndUploadBuffers(LodQuadBuilder builder, EDhApiGpuUploadMethod method, GLVertexBuffer[] vbos, ArrayList buffers) throws InterruptedException - { - try - { - vbos = resizeBuffer(vbos, buffers.size()); - uploadBuffersDirect(vbos, buffers, method); - } - finally - { - // all the buffers must be manually freed to prevent memory leaks - if (buffers != null) + catch (Exception e) { - for (ByteBuffer buffer : buffers) + LOGGER.error("Unexpected issue uploading buffer ["+this.blockPos +"], error: ["+e.getMessage()+"].", e); + + this.uploadFuture.completeExceptionally(e); + this.uploadFuture = null; + } + finally + { + // all the buffers must be manually freed to prevent memory leaks + + for (ByteBuffer buffer : opaqueBuffers) + { + MemoryUtil.memFree(buffer); + } + + for (ByteBuffer buffer : transparentBuffers) { MemoryUtil.memFree(buffer); } } - } + }); - // return the array in case it was resized - return vbos; + return this.uploadFuture; } - public static GLVertexBuffer[] resizeBuffer(GLVertexBuffer[] vbos, int newSize) + private static GLVertexBuffer[] resizeBuffer(GLVertexBuffer[] vbos, int newSize) { if (vbos.length == newSize) { @@ -228,8 +216,6 @@ public class ColumnRenderBuffer implements AutoCloseable - - //========// // render // //========// @@ -238,7 +224,7 @@ public class ColumnRenderBuffer implements AutoCloseable public boolean renderOpaque(LodRenderer renderContext, DhApiRenderParam renderEventParam) { boolean hasRendered = false; - renderContext.setModelViewMatrixOffset(this.pos, renderEventParam); + renderContext.setModelViewMatrixOffset(this.blockPos, renderEventParam); for (GLVertexBuffer vbo : this.vbos) { if (vbo == null) @@ -266,7 +252,7 @@ public class ColumnRenderBuffer implements AutoCloseable try { // can throw an IllegalStateException if the GL program was freed before it should've been - renderContext.setModelViewMatrixOffset(this.pos, renderEventParam); + renderContext.setModelViewMatrixOffset(this.blockPos, renderEventParam); for (GLVertexBuffer vbo : this.vbosTransparent) { @@ -287,7 +273,7 @@ public class ColumnRenderBuffer implements AutoCloseable } catch (IllegalStateException e) { - LOGGER.error("renderContext program doesn't exist for pos: "+this.pos, e); + LOGGER.error("renderContext program doesn't exist for pos: "+this.blockPos, e); } return hasRendered; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java index 61fb96081..17f37ad5e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java @@ -115,62 +115,17 @@ public class ColumnRenderBufferBuilder LodQuadBuilder quadBuilder ) { - // TODO put into a single future/thread so it can be easily canceled - ThreadPoolExecutor bufferUploaderExecutor = ThreadPoolUtil.getBufferUploaderExecutor(); - if (bufferUploaderExecutor == null || bufferUploaderExecutor.isTerminated()) + ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getMinY(), DhSectionPos.getMinCornerBlockZ(pos))); + CompletableFuture uploadFuture = buffer.makeAndUploadBuffersAsync(quadBuilder, GLProxy.getInstance().getGpuUploadMethod()); + uploadFuture.whenComplete((uploadedBuffer, exception) -> { - // one or more of the thread pools has been shut down - CompletableFuture future = new CompletableFuture<>(); - future.cancel(true); - return future; - } - - - try - { - return CompletableFuture.supplyAsync(() -> - { - try - { - ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getMinY(), DhSectionPos.getMinCornerBlockZ(pos))); - try - { - buffer.uploadBuffer(quadBuilder, GLProxy.getInstance().getGpuUploadMethod()); - if (buffer.buffersUploaded) - { - return buffer; - } - else - { - buffer.close(); - return null; - } - } - catch (Exception e) - { - buffer.close(); - throw e; - } - } - catch (InterruptedException e) - { - throw UncheckedInterruptedException.convert(e); - } - catch (Throwable e3) - { - LOGGER.error("LodNodeBufferBuilder was unable to upload buffer for pos ["+DhSectionPos.toString(pos)+"], error: [" + e3.getMessage() + "].", e3); - throw e3; - } - }, bufferUploaderExecutor); - } - catch (RejectedExecutionException ignore) - { - // shouldn't happen, but just in case - - CompletableFuture future = new CompletableFuture<>(); - future.cancel(true); - return future; - } + // clean up if not uploaded + if (!uploadedBuffer.buffersUploaded) + { + uploadedBuffer.close(); + } + }); + return uploadFuture; } private static void makeLodRenderData( LodQuadBuilder quadBuilder, ColumnRenderSource renderSource, IDhClientLevel clientLevel, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java index bbfba806d..b763e25ea 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java @@ -20,7 +20,6 @@ package com.seibel.distanthorizons.core.dataObjects.transformers; import com.seibel.distanthorizons.api.enums.config.EDhApiBlocksToAvoid; -import com.seibel.distanthorizons.api.enums.config.EDhApiVerticalQuality; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; @@ -40,12 +39,10 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; -import com.seibel.distanthorizons.coreapi.util.MathUtil; import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import org.apache.logging.log4j.Logger; -import java.text.ParseException; import java.util.HashSet; /** @@ -236,10 +233,10 @@ public class FullDataToRenderDataTransformer if (!brokenPos.contains(fullDataMapping.getPos())) { brokenPos.add(fullDataMapping.getPos()); - String dimName = level.getLevelWrapper().getDimensionName(); + String levelId = level.getLevelWrapper().getDhIdentifier(); LOGGER.warn("Unable to get data point with id ["+id+"] " + "(Max possible ID: ["+fullDataMapping.getMaxValidId()+"]) " + - "for pos ["+fullDataMapping.getPos()+"] in dimension ["+dimName+"]. " + + "for pos ["+fullDataMapping.getPos()+"] in level ["+levelId+"]. " + "Error: ["+e.getMessage()+"]. " + "Further errors for this position won't be logged."); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java index f67c97862..8e2edcd90 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java @@ -141,7 +141,7 @@ public class LodDataBuilder for (int relBlockZ = 0; relBlockZ < LodUtil.CHUNK_WIDTH; relBlockZ++) { LongArrayList longs = new LongArrayList(chunkWrapper.getHeight() / 4); - int lastY = chunkWrapper.getMaxBuildHeight(); + int lastY = chunkWrapper.getExclusiveMaxBuildHeight(); IBiomeWrapper biome = chunkWrapper.getBiome(relBlockX, lastY, relBlockZ); IBlockStateWrapper blockState = AIR; int mappedId = dataSource.mapping.addIfNotPresentAndGetId(biome, blockState); @@ -149,7 +149,7 @@ public class LodDataBuilder byte blockLight; byte skyLight; - if (lastY < chunkWrapper.getMaxBuildHeight()) + if (lastY < chunkWrapper.getExclusiveMaxBuildHeight()) { // FIXME: The lastY +1 offset is to reproduce the old behavior. Remove this when we get per-face lighting blockLight = (byte) chunkWrapper.getDhBlockLight(relBlockX, lastY + 1, relBlockZ); @@ -167,7 +167,7 @@ public class LodDataBuilder int y = chunkWrapper.getLightBlockingHeightMapValue(relBlockX, relBlockZ); // go up until we reach open air or the world limit IBlockStateWrapper topBlockState = previousBlockState = chunkWrapper.getBlockState(relBlockX, y, relBlockZ, mcBlockPos, previousBlockState); - while (!topBlockState.isAir() && y < chunkWrapper.getMaxBuildHeight()) + while (!topBlockState.isAir() && y < chunkWrapper.getExclusiveMaxBuildHeight()) { try { @@ -180,7 +180,7 @@ public class LodDataBuilder { if (!getTopErrorLogged) { - LOGGER.warn("Unexpected issue in LodDataBuilder, future errors won't be logged. Chunk [" + chunkWrapper.getChunkPos() + "] with max height: [" + chunkWrapper.getMaxBuildHeight() + "] had issue getting block at pos [" + relBlockX + "," + y + "," + relBlockZ + "] error: " + e.getMessage(), e); + LOGGER.warn("Unexpected issue in LodDataBuilder, future errors won't be logged. Chunk [" + chunkWrapper.getChunkPos() + "] with max height: [" + chunkWrapper.getExclusiveMaxBuildHeight() + "] had issue getting block at pos [" + relBlockX + "," + y + "," + relBlockZ + "] error: " + e.getMessage(), e); getTopErrorLogged = true; } @@ -208,7 +208,7 @@ public class LodDataBuilder // check if this block is visible from any direction || blockVisible(chunkWrapper, relBlockX, y, relBlockZ)) { - longs.add(FullDataPointUtil.encode(mappedId, lastY - y, y + 1 - chunkWrapper.getMinBuildHeight(), blockLight, skyLight)); + longs.add(FullDataPointUtil.encode(mappedId, lastY - y, y + 1 - chunkWrapper.getInclusiveMinBuildHeight(), blockLight, skyLight)); biome = newBiome; blockState = newBlockState; @@ -219,7 +219,7 @@ public class LodDataBuilder } } } - longs.add(FullDataPointUtil.encode(mappedId, lastY - y, y + 1 - chunkWrapper.getMinBuildHeight(), blockLight, skyLight)); + longs.add(FullDataPointUtil.encode(mappedId, lastY - y, y + 1 - chunkWrapper.getInclusiveMinBuildHeight(), blockLight, skyLight)); dataSource.setSingleColumn(longs, relBlockX + chunkOffsetX, @@ -289,7 +289,7 @@ public class LodDataBuilder { return true; } - if (testBlockPos.getY() < chunkWrapper.getMinBuildHeight() || testBlockPos.getY() > chunkWrapper.getMaxBuildHeight()) + if (testBlockPos.getY() < chunkWrapper.getInclusiveMinBuildHeight() || testBlockPos.getY() > chunkWrapper.getExclusiveMaxBuildHeight()) { return true; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java index b4d8b5889..901301b00 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java @@ -131,16 +131,16 @@ public class FullDataSourceProviderV2 DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showFullDataUpdateStatus); - String dimensionName = level.getLevelWrapper().getDimensionName(); + String levelId = level.getLevelWrapper().getDhIdentifier(); // start migrating any legacy data sources present in the background - this.migrationThreadPool = ThreadUtil.makeRateLimitedThreadPool(1, MIGRATION_THREAD_NAME_PREFIX + "["+dimensionName+"]", Config.Common.MultiThreading.runTimeRatioForUpdatePropagatorThreads.get(), Thread.MIN_PRIORITY, (Semaphore) null); + this.migrationThreadPool = ThreadUtil.makeRateLimitedThreadPool(1, MIGRATION_THREAD_NAME_PREFIX + "["+levelId+"]", Config.Common.MultiThreading.runTimeRatioForUpdatePropagatorThreads.get(), Thread.MIN_PRIORITY, (Semaphore) null); this.migrationThreadPool.execute(this::convertLegacyDataSources); // update propagation doesn't need to be run on the server since only the highest detail level is needed //if (SharedApi.getEnvironment() != EWorldEnvironment.SERVER_ONLY) // TODO //{ - this.updateQueueProcessor = ThreadUtil.makeSingleThreadPool("Parent Update Queue ["+dimensionName+"]"); + this.updateQueueProcessor = ThreadUtil.makeSingleThreadPool("Parent Update Queue ["+levelId+"]"); this.updateQueueProcessor.execute(this::runUpdateQueue); //} //else @@ -347,8 +347,8 @@ public class FullDataSourceProviderV2 private void convertLegacyDataSources() { - String dimensionName = this.level.getLevelWrapper().getDimensionName(); - LOGGER.info("Attempting to migrate data sources for: ["+dimensionName+"]-["+this.saveDir+"]..."); + String levelId = this.level.getLevelWrapper().getDhIdentifier(); + LOGGER.info("Attempting to migrate data sources for: ["+levelId+"]-["+this.saveDir+"]..."); @@ -367,7 +367,7 @@ public class FullDataSourceProviderV2 this.showMigrationStartMessage(); - LOGGER.info("deleting [" + dimensionName + "] - ["+totalDeleteCount+"] unused data sources..."); + LOGGER.info("deleting [" + levelId + "] - ["+totalDeleteCount+"] unused data sources..."); this.legacyDeletionCount = totalDeleteCount; ArrayList unusedDataPosList = this.legacyFileHandler.repo.getUnusedDataSourcePositionStringList(50); @@ -385,7 +385,7 @@ public class FullDataSourceProviderV2 long endStart = System.currentTimeMillis(); long deleteTime = endStart - startTime; - LOGGER.info("Deleting [" + dimensionName + "] - [" + unusedCount + "/" + totalDeleteCount + "] in ["+deleteTime+"]ms ..."); + LOGGER.info("Deleting [" + levelId + "] - [" + unusedCount + "/" + totalDeleteCount + "] in ["+deleteTime+"]ms ..."); // a slight delay is added to prevent accidentally locking the database when deleting a lot of rows @@ -398,7 +398,7 @@ public class FullDataSourceProviderV2 } catch (InterruptedException ignore){} } - LOGGER.info("Done deleting [" + dimensionName + "] - ["+totalDeleteCount+"] unused data sources."); + LOGGER.info("Done deleting [" + levelId + "] - ["+totalDeleteCount+"] unused data sources."); } @@ -423,7 +423,7 @@ public class FullDataSourceProviderV2 int progressCount = 0; while (!legacyDataSourceList.isEmpty() && this.migrationThreadRunning.get()) { - LOGGER.info("Migrating [" + dimensionName + "] - [" + progressCount + "/" + totalMigrationCount + "]..."); + LOGGER.info("Migrating [" + levelId + "] - [" + progressCount + "/" + totalMigrationCount + "]..."); ArrayList> updateFutureList = new ArrayList<>(); for (int i = 0; i < legacyDataSourceList.size() && this.migrationThreadRunning.get(); i++) @@ -485,7 +485,7 @@ public class FullDataSourceProviderV2 } catch (Exception e) { - LOGGER.info("migration stopped due to error for: ["+dimensionName+"]-["+this.saveDir+"], error: ["+e.getMessage()+"].", e); + LOGGER.info("migration stopped due to error for: ["+levelId+"]-["+this.saveDir+"], error: ["+e.getMessage()+"].", e); this.showMigrationEndMessage(false); this.migrationStoppedWithError = true; } @@ -493,13 +493,13 @@ public class FullDataSourceProviderV2 { if (this.migrationThreadRunning.get()) { - LOGGER.info("migration complete for: ["+dimensionName+"]-["+this.saveDir+"]."); + LOGGER.info("migration complete for: ["+levelId+"]-["+this.saveDir+"]."); this.showMigrationEndMessage(true); this.migrationCount = 0; } else { - LOGGER.info("migration stopped for: ["+dimensionName+"]-["+this.saveDir+"]."); + LOGGER.info("migration stopped for: ["+levelId+"]-["+this.saveDir+"]."); this.showMigrationEndMessage(false); this.migrationStoppedWithError = true; } @@ -526,9 +526,9 @@ public class FullDataSourceProviderV2 } this.migrationStartMessageQueued = true; - String dimName = this.level.getLevelWrapper().getDimensionName(); + String levelId = this.level.getLevelWrapper().getDhIdentifier(); ClientApi.INSTANCE.showChatMessageNextFrame( - "Old Distant Horizons data is being migrated for ["+dimName+"]. \n" + + "Old Distant Horizons data is being migrated for ["+levelId+"]. \n" + "While migrating LODs may load slowly \n" + "and DH world gen will be disabled. \n" + "You can see migration progress in the F3 menu." @@ -537,16 +537,16 @@ public class FullDataSourceProviderV2 private void showMigrationEndMessage(boolean success) { - String dimName = this.level.getLevelWrapper().getDimensionName(); + String levelId = this.level.getLevelWrapper().getDhIdentifier(); if (success) { - ClientApi.INSTANCE.showChatMessageNextFrame("Distant Horizons data migration for ["+dimName+"] completed."); + ClientApi.INSTANCE.showChatMessageNextFrame("Distant Horizons data migration for ["+levelId+"] completed."); } else { ClientApi.INSTANCE.showChatMessageNextFrame( - "Distant Horizons data migration for ["+dimName+"] stopped. \n" + + "Distant Horizons data migration for ["+levelId+"] stopped. \n" + "Some data may not have been migrated." ); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java index 8b5288f13..b16f55cb4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java @@ -148,7 +148,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im { boolean oldQueueExists = this.worldGenQueueRef.compareAndSet(null, newWorldGenQueue); LodUtil.assertTrue(oldQueueExists, "previous world gen queue is still here!"); - LOGGER.info("Set world gen queue for level [" + this.level.getLevelWrapper().getDimensionName() + "]."); + LOGGER.info("Set world gen queue for level [" + this.level.getLevelWrapper().getDhIdentifier() + "]."); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java index a6feccefb..d9e7c6bbe 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java @@ -80,14 +80,14 @@ public class ClientOnlySaveStructure implements ISaveStructure if (newLevelWrapper instanceof IServerKeyedClientLevel) { IServerKeyedClientLevel keyedClientLevel = (IServerKeyedClientLevel) newLevelWrapper; - LOGGER.info("Loading level [" + newLevelWrapper.getDimensionName() + "] with key: [" + keyedClientLevel.getServerLevelKey() + "]."); + LOGGER.info("Loading level [" + newLevelWrapper.getDhIdentifier() + "] with key: [" + keyedClientLevel.getServerLevelKey() + "]."); // This world was identified by the server directly, so we can know for sure which folder to use. - saveFolder = getSaveFolderFromDimensionName(keyedClientLevel.getServerLevelKey()); + saveFolder = getSaveFolderByLevelId(keyedClientLevel.getServerLevelKey()); } else { // get the default folder - saveFolder = getSaveFolderFromDimensionName(levelWrapper.getDimensionName()); + saveFolder = getSaveFolderByLevelId(levelWrapper.getDhIdentifier()); } // Allow API users to override the save folder @@ -160,7 +160,7 @@ public class ClientOnlySaveStructure implements ISaveStructure } - private static File getSaveFolderFromDimensionName(String dimensionName) + private static File getSaveFolderByLevelId(String dimensionName) { String path = MC_SHARED.getInstallationDirectory().getPath() + File.separatorChar + SERVER_DATA_FOLDER_NAME + File.separatorChar diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimCompare.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimCompare.java deleted file mode 100644 index 3b73031aa..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimCompare.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 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 . - */ - -package com.seibel.distanthorizons.core.file.subDimMatching; - -import com.seibel.distanthorizons.core.config.Config; -import org.jetbrains.annotations.NotNull; - -import java.io.File; - -/** - * Contains data used to compare different sub LodDimensions. - * Sub Dimensions are the different folders under a dimension. - * For example: "\Distant_Horizons_server_data\server_1\dim_the_nether\6fb97c01-e4c7-4634-87db-36b1e490e4c3" - * is a sub dimension for the server "server_1" in the nether. - * - * @author James Seibel - * @version 2022-12-17 - */ -public class SubDimCompare implements Comparable -{ - /** - * the maximum distance in blocks a player can be away from the - * given dimension and still be considered in the same place. - */ - public static int MAX_SIMILAR_PLAYER_POS_DISTANCE_IN_BLOCKS = 3; - - public int equalDataPoints = 0; - public int totalDataPoints = 0; - public int playerPosDist = 0; - public File folder = null; - - - public SubDimCompare(int newEqualDataPoints, int newTotalDataPoints, int newPlayerPosDistance, File newSubDimFolder) - { - this.equalDataPoints = newEqualDataPoints; - this.totalDataPoints = newTotalDataPoints; - this.playerPosDist = newPlayerPosDistance; - - this.folder = newSubDimFolder; - } - - /** returns a number between 0 (no equal datapoint) and 1 (totally equal) */ - public double getPercentEqual() - { - // its possible the comparison didn't find any data points - if (this.totalDataPoints != 0) - { - return (double) this.equalDataPoints / (double) this.totalDataPoints; - } - else - { - return 0; - } - } - - - @Override - public int compareTo(@NotNull SubDimCompare other) - { - if (this.equalDataPoints != other.equalDataPoints) - { - // compare based on data points - return Double.compare(this.getPercentEqual(), other.getPercentEqual()); - } - else - { - // break ties based on player position - return Integer.compare(this.playerPosDist, other.playerPosDist); - } - } - - /** Returns true if this sub dimension is close enough to be considered a valid sub dimension */ - public boolean isValidSubDim() - { - double minimumSimilarityRequired = 0.5;//Config.Client.Advanced.Multiplayer.multiverseSimilarityRequiredPercent.get(); - return this.getPercentEqual() >= minimumSimilarityRequired - || this.playerPosDist <= MAX_SIMILAR_PLAYER_POS_DISTANCE_IN_BLOCKS; - } - - @Override - public String toString() { return this.equalDataPoints + "/" + this.totalDataPoints + ": " + this.getPercentEqual() + " playerPos: " + this.playerPosDist; } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java deleted file mode 100644 index d98287125..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java +++ /dev/null @@ -1,386 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 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 . - */ - -package com.seibel.distanthorizons.core.file.subDimMatching; - -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; -import com.seibel.distanthorizons.core.generation.DhLightingEngine; -import com.seibel.distanthorizons.core.level.DhClientLevel; -import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.pos.DhChunkPos; -import com.seibel.distanthorizons.core.pos.DhSectionPos; -import com.seibel.distanthorizons.core.util.FullDataPointUtil; -import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.util.ThreadUtil; -import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; -import com.seibel.distanthorizons.coreapi.util.StringUtil; -import it.unimi.dsi.fastutil.longs.LongArrayList; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Used to allow multiple levels using the same dimension type.
- * This is specifically needed for servers running the Multiverse plugin (or similar). - * - * @author James Seibel - * @version 12-17-2022 - */ -@Deprecated -public class SubDimensionLevelMatcher implements AutoCloseable -{ - private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - public static final Logger LOGGER = DhLoggerBuilder.getLogger(); - //new ConfigBasedLogger(LogManager.getLogger(), - //() -> Config.Common.Logging.logFileSubDimEvent.get()); - - private final ExecutorService matcherThread = ThreadUtil.makeSingleThreadPool("Sub Dimension Matcher"); - - private SubDimensionPlayerData playerData = null; - private SubDimensionPlayerData firstSeenPlayerData = null; - - /** If true the LodDimensionFileHelper is attempting to determine the folder for this dimension */ - private final AtomicBoolean determiningWorldFolder = new AtomicBoolean(false); - private final IClientLevelWrapper currentClientLevel; - private volatile File foundLevelFile = null; - private final List potentialLevelFolders; - private final File levelsFolder; - - - - //=============// - // constructor // - //=============// - - public SubDimensionLevelMatcher(IClientLevelWrapper targetLevel, File levelsFolder, List potentialLevelFolders) - { - this.currentClientLevel = targetLevel; - this.potentialLevelFolders = potentialLevelFolders; - this.levelsFolder = levelsFolder; - - if (potentialLevelFolders.size() == 0) - { - String newId = UUID.randomUUID().toString(); - LOGGER.info("No potential level files found. Creating a new sub dimension with the ID ["+ StringUtil.shortenString(newId, 8)+"]..."); - this.foundLevelFile = this.CreateSubDimFolder(newId); - } - } - - - - //==============// - // level finder // - //==============// - - public boolean isFindingLevel(ILevelWrapper level) { return Objects.equals(level, this.currentClientLevel); } - - /** May return null if the level isn't known yet */ - public File tryGetLevel() - { - this.tryGetLevelInternalAsync(); - return this.foundLevelFile; - } - private void tryGetLevelInternalAsync() - { - if (this.foundLevelFile != null) - { - return; - } - - // prevent multiple threads running at the same time - if (this.determiningWorldFolder.getAndSet(true)) - { - return; - } - - - this.matcherThread.submit(() -> - { - try - { - // attempt to get the file handler - File saveDir = this.attemptToDetermineSubDimensionFolder(); - if (saveDir != null) - { - this.foundLevelFile = saveDir; - } - } - catch (IOException e) - { - LOGGER.error("Unable to set the dimension file handler for level [" + this.currentClientLevel + "]. Error: ", e); - } - finally - { - // make sure we unlock this method - this.determiningWorldFolder.set(false); - } - }); - } - - /** - * Currently this method checks a single chunk (where the player is) - * and compares it against the same chunk position in the other dimension worlds to - * guess which world the player is in. - * - * @throws IOException if the folder doesn't exist or can't be accessed - */ - public File attemptToDetermineSubDimensionFolder() throws IOException - { - // Update PlayerData - SubDimensionPlayerData newPlayerData = SubDimensionPlayerData.tryGetPlayerData(MC_CLIENT); - if (newPlayerData != null) - { - if (this.firstSeenPlayerData == null) - { - this.firstSeenPlayerData = newPlayerData; - } - this.playerData = newPlayerData; - } - - - - //================================// - // generate a LOD to test against // - //================================// - - // attempt to get a chunk at the player's pos - IChunkWrapper newlyLoadedChunk = MC_CLIENT.getWrappedClientLevel().tryGetChunk(new DhChunkPos(this.playerData.playerBlockPos)); - if (newlyLoadedChunk == null) - { - return null; - } - DhLightingEngine.INSTANCE.lightChunk(newlyLoadedChunk, new ArrayList<>(), MC_CLIENT.getWrappedClientLevel().hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT); - - // build the chunk LOD - FullDataSourceV2 newChunkSizedFullDataView = FullDataSourceV2.createFromChunk(newlyLoadedChunk); - // convert to a data source for easier comparing - FullDataSourceV2 newDataSource = FullDataSourceV2.createEmpty(DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, this.playerData.playerBlockPos)); - newDataSource.update(newChunkSizedFullDataView); - - - - //================================// - // test each known sub-dim folder // - //================================// - - // log the start of this attempt - LOGGER.info("Attempting to determine sub-dimension for [" + MC_CLIENT.getWrappedClientLevel().getDimensionName() + "]"); - LOGGER.info("Player block pos in dimension: [" + this.playerData.playerBlockPos.getX() + "," + this.playerData.playerBlockPos.getY() + "," + this.playerData.playerBlockPos.getZ() + "]"); - LOGGER.info("Potential Sub Dimension folders: [" + this.potentialLevelFolders.size() + "]"); - - SubDimCompare mostSimilarSubDim = null; - for (File testLevelFolder : this.potentialLevelFolders) - { - LOGGER.info("Testing level folder: [" + StringUtil.shortenString(testLevelFolder.getName(), 8) + "]"); - - FullDataSourceV2 testFullDataSource = null; - try - { - // get the data source to compare against - try (IDhLevel tempLevel = new DhClientLevel(new ClientOnlySaveStructure(), this.currentClientLevel, testLevelFolder, false, null)) - { - testFullDataSource = tempLevel.getFullDataProvider().getAsync(DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, this.playerData.playerBlockPos)).join(); - if (testFullDataSource == null) - { - continue; - } - } - - - // confirm both data sources have the same section pos - long newSectionChunkPos = DhSectionPos.convertToDetailLevel(newDataSource.getPos(), DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL); - long testSectionChunkPos = DhSectionPos.convertToDetailLevel(testFullDataSource.getPos(), DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL); - LodUtil.assertTrue(newSectionChunkPos == testSectionChunkPos, "data source positions don't match"); - - - - // compare the data sources - int equalDataPoints = 0; - int totalDataPointCount = 0; - for (int x = 0; x < FullDataSourceV2.WIDTH; x++) - { - for (int z = 0; z < FullDataSourceV2.WIDTH; z++) - { - LongArrayList newColumn = newDataSource.get(x, z); - LongArrayList testColumn = testFullDataSource.get(x, z); - - if (newColumn != null && testColumn != null) - { - // compare each data point in the column - - FullDataPointIdMap newDataMap = newDataSource.mapping; - FullDataPointIdMap testDataMap = testFullDataSource.mapping; - - // use min to prevent going out of bounds - int minColumnIndex = Math.min(newColumn.size(), testColumn.size()); - for (int i = 0; i < minColumnIndex; i++) - { - long newDataPoint = newColumn.getLong(i); - long testDataPoint = testColumn.getLong(i); - - int newId = FullDataPointUtil.getId(newDataPoint); - int testId = FullDataPointUtil.getId(testDataPoint); - - - // bottom Y - int newBottom = FullDataPointUtil.getBottomY(newDataPoint); - int testBottom = FullDataPointUtil.getBottomY(testDataPoint); - if (newBottom == testBottom) - { - equalDataPoints++; - } - totalDataPointCount++; - - // height - int newHeight = FullDataPointUtil.getHeight(newDataPoint); - int testHeight = FullDataPointUtil.getHeight(testDataPoint); - if (newHeight == testHeight) - { - equalDataPoints++; - } - totalDataPointCount++; - - // biome - IBiomeWrapper newBiome = newDataMap.getBiomeWrapper(newId); - IBiomeWrapper testBiome = testDataMap.getBiomeWrapper(testId); - if (newBiome.equals(testBiome)) - { - equalDataPoints++; - } - totalDataPointCount++; - - // block - IBlockStateWrapper newBlock = newDataMap.getBlockStateWrapper(newId); - IBlockStateWrapper testBlock = testDataMap.getBlockStateWrapper(testId); - if (newBlock.equals(testBlock)) - { - equalDataPoints++; - } - totalDataPointCount++; - - // ignore light values - // since we are using the DH lighting engine and only 1 chunk the values will never be the same - } - } - else if (newColumn != null) - { - // missing test column - totalDataPointCount += newColumn.size(); - } - else - { - // new column present, test absent, can't compare - } - } - } - - - - // get the player data for this dimension folder - SubDimensionPlayerData testPlayerData = new SubDimensionPlayerData(testLevelFolder); - LOGGER.info("Last known player pos: [" + testPlayerData.playerBlockPos.getX() + "," + testPlayerData.playerBlockPos.getY() + "," + testPlayerData.playerBlockPos.getZ() + "]"); - - // check if the block positions are close - int playerBlockDist = testPlayerData.playerBlockPos.getManhattanDistance(this.playerData.playerBlockPos); - LOGGER.info("Player block position distance between saved sub dimension and first seen is [" + playerBlockDist + "]"); - - // determine if this world is closer to the newly loaded world - SubDimCompare subDimCompare = new SubDimCompare(equalDataPoints, totalDataPointCount, playerBlockDist, testLevelFolder); - if (mostSimilarSubDim == null || subDimCompare.compareTo(mostSimilarSubDim) > 0) - { - mostSimilarSubDim = subDimCompare; - } - - - String subDimShortName = StringUtil.shortenString(testLevelFolder.getName(), 8); // variables are separated out for easier debugging - String equalPercent = StringUtil.shortenString(mostSimilarSubDim.getPercentEqual()+"", 5); - LOGGER.info("Sub dimension ["+subDimShortName+"...] is current dimension probability: "+equalPercent+" ("+equalDataPoints+"/"+totalDataPointCount+")"); - } - catch (Exception e) - { - // this sub dimension isn't formatted correctly - // for now we are just assuming it is an unrelated file - LOGGER.warn("Error checking level: "+e.getMessage(), e); - } - finally - { - if (testFullDataSource != null) - { - try { testFullDataSource.close(); } catch (Exception ignore) {} - } - } - } - - - - //================================// - // return the found sub dimension // - //================================// - - // the first seen player data is no longer needed, the sub dimension has been determined - this.firstSeenPlayerData = null; - if (mostSimilarSubDim != null && mostSimilarSubDim.isValidSubDim()) - { - // we found a sub dim folder that is similar, use it - - LOGGER.info("Sub Dimension set to: [" + StringUtil.shortenString(mostSimilarSubDim.folder.getName(), 8) + "...] with an equality of [" + mostSimilarSubDim.getPercentEqual() + "]"); - return mostSimilarSubDim.folder; - } - else - { - // no sub dim folder, create a new one - - String newId = UUID.randomUUID().toString(); - - double highestEqualityPercent = mostSimilarSubDim != null ? mostSimilarSubDim.getPercentEqual() : 0; - String message = "No suitable sub dimension found. The highest equality was [" + StringUtil.shortenString(highestEqualityPercent + "", 5) + "]. Creating a new sub dimension with ID: " + StringUtil.shortenString(newId, 8) + "..."; - LOGGER.info(message); - - File folder = this.CreateSubDimFolder(newId); - folder.mkdirs(); - return folder; - } - } - - - private File CreateSubDimFolder(String subDimId) { return new File(this.levelsFolder.getPath() + File.separatorChar + this.currentClientLevel.getDimensionName(), subDimId); } - - @Override - public void close() { this.matcherThread.shutdownNow(); } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionPlayerData.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionPlayerData.java deleted file mode 100644 index e62e25bc3..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionPlayerData.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 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 . - */ - -package com.seibel.distanthorizons.core.file.subDimMatching; - - -import com.electronwill.nightconfig.core.file.CommentedFileConfig; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; -import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; - -import org.jetbrains.annotations.Nullable; -import java.io.File; - -/** - * Data container for any player data we can use to differentiate one dimension from another. - * - * @author James Seibel - * @version 2022-3-26 - */ -public class SubDimensionPlayerData -{ - public static final IWrapperFactory FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class); - - public static final String PLAYER_DATA_FILE_NAME = "_playerData.toml"; - - - public static final String PLAYER_BLOCK_POS_X_PATH = "playerBlockPosX"; - public static final String PLAYER_BLOCK_POS_Y_PATH = "playerBlockPosY"; - public static final String PLAYER_BLOCK_POS_Z_PATH = "playerBlockPosZ"; - public DhBlockPos playerBlockPos; - - // not implemented yet - public static final String WORLD_SPAWN_POS_X_PATH = "worldSpawnBlockPosX"; - public static final String WORLD_SPAWN_POS_Y_PATH = "worldSpawnBlockPosY"; - public static final String WORLD_SPAWN_POS_Z_PATH = "worldSpawnBlockPosZ"; - /** - * The client world has access to a spawn point, so this should be possible to fill in. - * I'm not sure what this will look like for worlds that don't have a spawn point. - */ - public DhBlockPos worldSpawnPointBlockPos; - - - - @Nullable - public static SubDimensionPlayerData tryGetPlayerData(IMinecraftClientWrapper mcClient) - { - if (!mcClient.playerExists()) - { - return null; - } - - try - { - return new SubDimensionPlayerData(mcClient); - } - catch (RuntimeException e) - { - // Player no longer exists due to concurrency. FIXME: Remember here is called not on main thread!!! - return null; - } - } - - private SubDimensionPlayerData(IMinecraftClientWrapper mc) - { - this.updateData(mc); - } - - public SubDimensionPlayerData(File dimensionFolder) - { - File file = getFileForDimensionFolder(dimensionFolder); - try (CommentedFileConfig toml = CommentedFileConfig.builder(file).build()) - { - toml.load(); - - // get the player block pos if it is specified - if (toml.contains(PLAYER_BLOCK_POS_X_PATH) - && toml.contains(PLAYER_BLOCK_POS_Y_PATH) - && toml.contains(PLAYER_BLOCK_POS_Z_PATH)) - { - int x = toml.getIntOrElse(PLAYER_BLOCK_POS_X_PATH, 0); - int y = toml.getIntOrElse(PLAYER_BLOCK_POS_Y_PATH, 0); - int z = toml.getIntOrElse(PLAYER_BLOCK_POS_Z_PATH, 0); - this.playerBlockPos = new DhBlockPos(x, y, z); - } - else - { - this.playerBlockPos = new DhBlockPos(0, 0, 0); - } - } - } - - - - public static File getFileForDimensionFolder(File file) { return new File(file.getPath() + File.separatorChar + PLAYER_DATA_FILE_NAME); } - - - /** Should be called often to make sure this object is up to date with the player's info */ - public void updateData(IMinecraftClientWrapper mc) - { - this.playerBlockPos = mc.getPlayerBlockPos(); - - if (this.playerBlockPos == null) - { - throw new RuntimeException("No player block pos!"); - } - } - - /** Writes everything from this object to the file given. */ - public void toTomlFile(CommentedFileConfig toml) - { - // player block pos - toml.add(PLAYER_BLOCK_POS_X_PATH, this.playerBlockPos.getX()); - toml.add(PLAYER_BLOCK_POS_Y_PATH, this.playerBlockPos.getY()); - toml.add(PLAYER_BLOCK_POS_Z_PATH, this.playerBlockPos.getZ()); - - toml.save(); - } - - - @Override - public String toString() - { - return "PlayerBlockPos: [" + this.playerBlockPos.getX() + "," + this.playerBlockPos.getY() + "," + this.playerBlockPos.getZ() + "]"; - } - -} - diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java index bfb18c5fa..8a1584a73 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java @@ -204,7 +204,7 @@ public class DhLightingEngine IBlockStateWrapper previousBlockState = null; int maxY = chunk.getMaxNonEmptyHeight(); - int minY = chunk.getMinBuildHeight(); + int minY = chunk.getInclusiveMinBuildHeight(); // get the adjacent chunk's sky lights for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++) // relative block pos @@ -322,7 +322,7 @@ public class DhLightingEngine continue; } - if (relNeighbourBlockPos.getY() < neighbourChunk.getMinNonEmptyHeight() || relNeighbourBlockPos.getY() > neighbourChunk.getMaxBuildHeight()) + 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 diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index 0bec732b3..0d8cf3b26 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -26,7 +26,11 @@ import com.seibel.distanthorizons.api.objects.data.DhApiChunk; import com.seibel.distanthorizons.api.objects.data.IDhApiFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.generation.tasks.*; +import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; +import com.seibel.distanthorizons.core.generation.tasks.InProgressWorldGenTaskGroup; +import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; +import com.seibel.distanthorizons.core.generation.tasks.WorldGenTask; +import com.seibel.distanthorizons.core.generation.tasks.WorldGenTaskGroup; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; @@ -191,7 +195,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb try { // loop until the generator is shutdown - while (!Thread.interrupted() && !DhApiWorldProxy.INSTANCE.getReadOnly()) + while (!Thread.interrupted() && DhApiWorldProxy.INSTANCE.worldLoaded() && !DhApiWorldProxy.INSTANCE.getReadOnly()) { this.generator.preGeneratorTaskStart(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/jar/JarUtils.java b/core/src/main/java/com/seibel/distanthorizons/core/jar/JarUtils.java index f607b98fb..20633553e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/jar/JarUtils.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/jar/JarUtils.java @@ -29,6 +29,7 @@ import org.jetbrains.annotations.Nullable; import java.io.*; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Objects; @@ -46,27 +47,44 @@ public class JarUtils @Nullable public static File jarFile = null; + + + //=============// + // constructor // + //=============// + static { try { - jarFile = new File(JarUtils.class.getProtectionDomain().getCodeSource().getLocation().toURI()); // Always safe + // this will fail in development environments due to how the jars are compiled + // this may also fail in forge production + URI jarUri = JarUtils.class.getProtectionDomain().getCodeSource().getLocation().toURI(); + jarFile = new File(jarUri); } - catch (Exception e) + catch (Exception eGetUri) { try { - LOGGER.warn("Unable to get the jar file, trying backup method... Error: "+e.getMessage(), e); jarFile = SingletonInjector.INSTANCE.get(IModChecker.class).modLocation(ModInfo.ID); } - catch (Exception f) + catch (Exception eGetModLoc) { - LOGGER.warn("Backup jar file getter failed. Error: "+f.getMessage(), f); + // only log if both methods fail since it isn't a problem unless both + // methods fail + LOGGER.warn("Unable to get jar file via URI or Mod Checker Location."); + LOGGER.warn("URI Error: ["+ eGetUri.getMessage()+"]", eGetUri); + LOGGER.warn("Mod Location Error: ["+ eGetModLoc.getMessage()+"]", eGetModLoc); } } } + + //=========// + // methods // + //=========// + /** * Gets the URI of a resource * @@ -75,9 +93,7 @@ public class JarUtils * @throws URISyntaxException If the file doesnt exist */ public static URI accessFileURI(String resource) throws URISyntaxException - { - return Objects.requireNonNull(JarUtils.class.getResource(resource)).toURI(); - } + { return Objects.requireNonNull(JarUtils.class.getResource(resource)).toURI(); } /** * Get a file within the mods resources @@ -146,7 +162,7 @@ public class JarUtils while ((bytesCount = fis.read(byteArray)) != -1) { digest.update(byteArray, 0, bytesCount); - } ; + } //close the stream; We don't need it now. fis.close(); @@ -174,7 +190,8 @@ public class JarUtils /** Please use the EPlatform enum instead */ @Deprecated public static OperatingSystem getOperatingSystem() - { // Get the os and turn it into that enum + { + // Get the os and turn it into that enum switch (EPlatform.get()) { case WINDOWS: diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java index 8ab6428b0..4c2e29675 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java @@ -114,7 +114,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I continue; } - NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getDimensionName()+"] Fulfilled request group ["+entry.getKey()+"]"); + NETWORK_LOGGER.debug("[" + this.serverLevelWrapper.getDhIdentifier() + "] Fulfilled request group [" + entry.getKey() + "]"); // Make this group unavailable for adding into this.requestGroupByPos.remove(entry.getKey()); @@ -230,7 +230,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I FullDataSourceRequestMessage requestMessage = requestGroup.requestMessages.remove(msg.futureId); if (requestGroup.requestMessages.isEmpty()) { - NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getDimensionName()+"] Cancelled request group ["+DhSectionPos.toString(requestMessage.sectionPos)+"]."); + NETWORK_LOGGER.debug("[" + this.serverLevelWrapper.getDhIdentifier() + "] Cancelled request group [" + DhSectionPos.toString(requestMessage.sectionPos) + "]."); this.requestGroupByPos.remove(requestMessage.sectionPos); this.serverside.fullDataFileHandler.removeRetrievalRequestIf(pos -> pos == requestMessage.sectionPos); } @@ -310,7 +310,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I { DataSourceRequestGroup newGroup = new DataSourceRequestGroup(); this.tryFulfillDataSourceRequestGroup(newGroup, pos); - NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getDimensionName()+"] Created request group for pos ["+DhSectionPos.toString(pos)+"]."); + NETWORK_LOGGER.debug("[" + this.serverLevelWrapper.getDhIdentifier() + "] Created request group for pos [" + DhSectionPos.toString(pos) + "]."); return newGroup; }); @@ -356,8 +356,8 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I new InvalidLevelException( "Generation not allowed. " + "Requested dimension: ["+((ILevelRelatedMessage) message).getLevelName()+"], " + - "player dimension: ["+message.getSession().serverPlayer.getLevel().getDimensionName()+"], " + - "handler dimension: ["+this.getLevelWrapper().getDimensionName()+"]" + "player dimension: [" + message.getSession().serverPlayer.getLevel().getDhIdentifier() + "], " + + "handler dimension: [" + this.getLevelWrapper().getDhIdentifier() + "]" ) ); } @@ -450,36 +450,44 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I LOGGER.warn("Unable to send FullDataPartialUpdateMessage - getNetworkCompressionExecutor() is null"); return this.getFullDataProvider().updateDataSourceAsync(data); } - CompletableFuture.runAsync(() -> + + try { - Objects.requireNonNull(this.beaconBeamRepo); - try (FullDataPayload payload = new FullDataPayload(data, this.beaconBeamRepo.getAllBeamsForPos(data.getPos()))) + CompletableFuture.runAsync(() -> { - for (ServerPlayerState serverPlayerState : this.serverPlayerStateManager.getReadyPlayers()) + Objects.requireNonNull(this.beaconBeamRepo); + try (FullDataPayload payload = new FullDataPayload(data, this.beaconBeamRepo.getAllBeamsForPos(data.getPos()))) { - if (serverPlayerState.getServerPlayer().getLevel() != this.serverLevelWrapper) + for (ServerPlayerState serverPlayerState : this.serverPlayerStateManager.getReadyPlayers()) { - continue; - } - - if (!serverPlayerState.sessionConfig.isRealTimeUpdatesEnabled()) - { - continue; - } - - Vec3d playerPosition = serverPlayerState.getServerPlayer().getPosition(); - int distanceFromPlayer = DhSectionPos.getChebyshevBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; - if (distanceFromPlayer >= serverPlayerState.getServerPlayer().getViewDistance() - && distanceFromPlayer <= serverPlayerState.sessionConfig.getMaxUpdateDistanceRadius()) - { - serverPlayerState.fullDataPayloadSender.sendInChunks(payload, () -> + if (serverPlayerState.getServerPlayer().getLevel() != this.serverLevelWrapper) { - serverPlayerState.networkSession.sendMessage(new FullDataPartialUpdateMessage(this.serverLevelWrapper, payload)); - }); + continue; + } + + if (!serverPlayerState.sessionConfig.isRealTimeUpdatesEnabled()) + { + continue; + } + + Vec3d playerPosition = serverPlayerState.getServerPlayer().getPosition(); + int distanceFromPlayer = DhSectionPos.getChebyshevBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; + if (distanceFromPlayer >= serverPlayerState.getServerPlayer().getViewDistance() + && distanceFromPlayer <= serverPlayerState.sessionConfig.getMaxUpdateDistanceRadius()) + { + serverPlayerState.fullDataPayloadSender.sendInChunks(payload, () -> + { + serverPlayerState.networkSession.sendMessage(new FullDataPartialUpdateMessage(this.serverLevelWrapper, payload)); + }); + } } } - } - }, executor); + }, executor); + } + catch (RejectedExecutionException ignore) + { + // the executor was shut down, it should be back up shortly and able to accept new jobs + } return this.getFullDataProvider().updateDataSourceAsync(data); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index b5823ddda..4d67fa2dc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -294,7 +294,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel @Override public void addDebugMenuStringsToList(List messageList) { - String dimName = this.levelWrapper.getDimensionName(); + String dimName = this.levelWrapper.getDhIdentifier(); boolean rendering = this.clientside.isRendering(); messageList.add("["+dimName+"] rendering: "+(rendering ? "yes" : "no")); @@ -338,7 +338,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel //================// @Override - public String toString() { return "DhClientLevel{"+this.getClientLevelWrapper().getDimensionName()+"}"; } + public String toString() { return "DhClientLevel{" + this.getClientLevelWrapper().getDhIdentifier() + "}"; } @Override public void close() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index 357a68521..0ffe9a7de 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -132,7 +132,7 @@ public class DhClientServerLevel extends AbstractDhServerLevel implements IDhCli public void addDebugMenuStringsToList(List messageList) { // header - String dimName = this.serverLevelWrapper.getDimensionName(); + String dimName = this.serverLevelWrapper.getDhIdentifier(); boolean rendering = this.clientside.isRendering(); messageList.add("["+dimName+"] rendering: "+(rendering ? "yes" : "no")); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 0dc6640b4..ecd3fcfb0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -78,7 +78,7 @@ public class DhServerLevel extends AbstractDhServerLevel @Override public void addDebugMenuStringsToList(List messageList) { - messageList.add("[" + this.serverLevelWrapper.getDimensionName() + "]"); + messageList.add("[" + this.serverLevelWrapper.getDhIdentifier() + "]"); super.addDebugMenuStringsToList(messageList); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java index 4246ef68f..8c1011974 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java @@ -77,7 +77,6 @@ public class F3Screen ThreadPoolExecutor updatePool = ThreadPoolUtil.getUpdatePropagatorExecutor(); ThreadPoolExecutor lodBuilderPool = ThreadPoolUtil.getChunkToLodBuilderExecutor(); ThreadPoolExecutor bufferBuilderPool = ThreadPoolUtil.getBufferBuilderExecutor(); - ThreadPoolExecutor bufferUploaderPool = ThreadPoolUtil.getBufferUploaderExecutor(); AbstractDhWorld world = SharedApi.getAbstractDhWorld(); Iterable levelIterator = world.getAllLoadedLevels(); @@ -96,7 +95,6 @@ public class F3Screen messageList.add(getThreadPoolStatString("Update Propagator", updatePool)); messageList.add(getThreadPoolStatString("LOD Builder", lodBuilderPool)); messageList.add(getThreadPoolStatString("Buffer Builder", bufferBuilderPool)); - messageList.add(getThreadPoolStatString("Buffer Uploader", bufferUploaderPool)); messageList.add(""); // chunk updates messageList.add(SharedApi.INSTANCE.getDebugMenuString()); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java index fd2202551..1f911b079 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java @@ -134,7 +134,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende public synchronized boolean tick(DhBlockPos2D targetPos) { - if (DhApiWorldProxy.INSTANCE.getReadOnly()) + if (DhApiWorldProxy.INSTANCE.worldLoaded() && DhApiWorldProxy.INSTANCE.getReadOnly()) { return false; } @@ -302,7 +302,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende public void addDebugMenuStringsToList(List messageList) { - messageList.add(this.getQueueName() + " [" + this.level.getClientLevelWrapper().getDimensionName() + "]"); + messageList.add(this.getQueueName() + " [" + this.level.getClientLevelWrapper().getDhIdentifier() + "]"); messageList.add("Requests: " + this.finishedRequests + " / " + (this.getWaitingTaskCount() + this.finishedRequests.get()) + " (failed: " + this.failedRequests + ", rate limit: " + this.getRequestRateLimit() + ")"); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java index 43d5bd37d..5176bb845 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java @@ -17,7 +17,7 @@ public interface ILevelRelatedMessage return this.getLevelName().equals(((IServerLevelWrapper) levelWrapper).getKeyedLevelDimensionName()); } - return this.getLevelName().equals(levelWrapper.getDimensionName()); + return this.getLevelName().equals(levelWrapper.getDhIdentifier()); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java index 683b1df66..ca19b181a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java @@ -48,7 +48,7 @@ public class FullDataSourceRequestMessage extends AbstractTrackableMessage imple public FullDataSourceRequestMessage() {} public FullDataSourceRequestMessage(ILevelWrapper levelWrapper, long sectionPos, @Nullable Long clientTimestamp) { - this.levelName = levelWrapper.getDimensionName(); + this.levelName = levelWrapper.getDhIdentifier(); this.sectionPos = sectionPos; this.clientTimestamp = clientTimestamp; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java index c71bd2c03..dd0b123ea 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java @@ -147,7 +147,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen } catch (Exception e) { - LOGGER.error("Quad Tree tick exception for dimension: " + this.level.getLevelWrapper().getDimensionName() + ", exception: " + e.getMessage(), e); + LOGGER.error("Quad Tree tick exception for level: [" + this.level.getLevelWrapper().getDhIdentifier() + "], error: [" + e.getMessage() + "].", e); } finally { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java index 99a55189b..48242a572 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java @@ -274,10 +274,11 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable this.bufferUploadFuture = ColumnRenderBufferBuilder.uploadBuffersAsync(this.level, this.pos, lodQuadBuilder); return this.bufferUploadFuture.thenCompose((buffer) -> { + // needed to clean up the old data ColumnRenderBuffer previousBuffer = this.renderBuffer; - // upload complete, clean up the old data if - this.renderBuffer = buffer; + // upload complete + this.renderBuffer = buffer.buffersUploaded ? buffer : null; this.buildAndUploadRenderDataToGpuFuture = null; this.bufferBuildFuture = null; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java deleted file mode 100644 index 3ea6f8455..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java +++ /dev/null @@ -1,420 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 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 . - */ - -package com.seibel.distanthorizons.core.render.fog; - -import com.seibel.distanthorizons.api.enums.rendering.*; -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; -import com.seibel.distanthorizons.core.render.glObject.GLProxy; -import com.seibel.distanthorizons.core.render.glObject.shader.Shader; -import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Objects; - -/** - * This holds fog related settings and - * creates the fog related shader code. - * - * @author Leetom - * @author James Seibel - * @version 2022-11-24 - */ -// TODO: Move lots out of here, there should be a listener hooked onto the config to update the shader -public class LodFogConfig -{ - private static final IOptifineAccessor OPTIFINE = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class); - - public static final boolean DEBUG_DUMP_GENERATED_CODE = false; - - public final FogSettings farFogSetting; - public final FogSettings heightFogSetting; - public final EDhApiHeightFogMixMode heightFogMixMode; - public final EDhApiHeightFogMode heightFogMode; - public final float heightFogHeight; - - // TODO: Move these out of here - public final int earthCurveRatio; - - // Noise Values - public final boolean noiseEnable; - public final int noiseSteps; - public final float noiseIntensity; - public final int noiseDropoff; - - - public static LodFogConfig generateFogConfig() { return new LodFogConfig(); } - - /** sets all fog options from the config */ - private LodFogConfig() - { - // TODO: Move these out of here - this.earthCurveRatio = Config.Client.Advanced.Graphics.Experimental.earthCurveRatio.get(); - - this.noiseEnable = Config.Client.Advanced.Graphics.NoiseTexture.enableNoiseTexture.get(); - this.noiseSteps = Config.Client.Advanced.Graphics.NoiseTexture.noiseSteps.get(); - this.noiseIntensity = Config.Client.Advanced.Graphics.NoiseTexture.noiseIntensity.get().floatValue(); - this.noiseDropoff = Config.Client.Advanced.Graphics.NoiseTexture.noiseDropoff.get(); - - - if (Config.Client.Advanced.Graphics.Fog.enableDhFog.get()) - { - // fog should be drawn - - this.farFogSetting = new FogSettings( - Config.Client.Advanced.Graphics.Fog.farFogStart.get(), - Config.Client.Advanced.Graphics.Fog.farFogEnd.get(), - Config.Client.Advanced.Graphics.Fog.farFogMin.get(), - Config.Client.Advanced.Graphics.Fog.farFogMax.get(), - Config.Client.Advanced.Graphics.Fog.farFogDensity.get(), - Config.Client.Advanced.Graphics.Fog.farFogFalloff.get() - ); - - this.heightFogMixMode = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMixMode.get(); - if (this.heightFogMixMode == EDhApiHeightFogMixMode.IGNORE_HEIGHT - || this.heightFogMixMode == EDhApiHeightFogMixMode.BASIC) - { - // basic fog mixing - - this.heightFogSetting = null; - this.heightFogMode = null; - this.heightFogHeight = 0.f; - } - else - { - // advanced fog mixing - - this.heightFogSetting = new FogSettings( - Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDensity.get(), - Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogEnd.get(), - Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMin.get(), - Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMax.get(), - Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDensity.get(), - Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogFalloff.get() - ); - - this.heightFogMode = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMode.get(); - - if (this.heightFogMode.basedOnCamera) - { - this.heightFogHeight = 0.f; - } - else - { - this.heightFogHeight = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogBaseHeight.get().floatValue(); - } - } - } - else - { - // fog disabled - - this.farFogSetting = null; - this.heightFogMixMode = null; - this.heightFogMode = null; - this.heightFogSetting = null; - this.heightFogHeight = 0.f; - } - } - - public StringBuilder loadAndProcessFragShader(String path, boolean absoluteFilePath) - { - StringBuilder stringBuilder = this.makeRuntimeDefine(); - this.generateRuntimeShaderCode(Shader.loadFile(path, absoluteFilePath, stringBuilder)); - - if (DEBUG_DUMP_GENERATED_CODE) - { - try (FileOutputStream file = new FileOutputStream("debugGenerated.frag", false)) - { - file.write(stringBuilder.toString().getBytes(StandardCharsets.UTF_8)); - GLProxy.GL_LOGGER.info("Debug dumped generated code to debugGenerated.frag for {}", path); - } - catch (IOException e) - { - GLProxy.GL_LOGGER.warn("Failed to debug dump generated code to file for {}", path); - } - } - - return stringBuilder; - } - - /** Generates the necessary constants for a fragment shader */ - private void generateRuntimeShaderCode(StringBuilder str) - { - str.append("// =======RUNTIME GENERATED CODE SECTION======== //\n"); - - // Generate method: float getNearFogThickness(float dist); - str.append("" + - "float getNearFogThickness(float dist) \n" + - "{ \n" + - " return linearFog(dist, uNearFogStart, uNearFogLength, 0.0, 1.0); \n" + - "} \n"); - - - if (this.farFogSetting == null) - { - str.append("\n" + - "float getFarFogThickness(float dist) { return 0.0; } \n" + - "float getHeightFogThickness(float dist) { return 0.0; } \n" + - "float calculateFarFogDepth(float horizontal, float dist, float uNearFogStart) { return 0.0; } \n" + - "float calculateHeightFogDepth(float vertical, float realY) { return 0.0; } \n" + - "float mixFogThickness(float near, float far, float height) \n" + - "{ \n" + - " return 0.0; \n" + - "} \n\n"); - } - else - { - // Generate method: float getFarFogThickness(float dist); - str.append("" + - "float getFarFogThickness(float dist) \n" + - "{ \n" + - getFarFogMethod(this.farFogSetting.fogType) + "\n" + - "} \n"); - - - // Generate method: float getHeightFogThickness(float dist); - str.append("" + - "float getHeightFogThickness(float dist) \n" + - "{ \n" + - (this.heightFogSetting != null ? getHeightFogMethod(this.heightFogSetting.fogType) : " return 0.0;") + "\n" + - "} \n"); - - - // Generate method: float calculateHeightFogDepth(float vertical, float realY); - str.append("" + - "float calculateHeightFogDepth(float vertical, float realY) \n" + - "{ \n" + - (this.heightFogSetting != null ? getHeightDepthMethod(this.heightFogMode, this.heightFogHeight) : " return 0.0;") + "\n" + - "} \n"); - - - // Generate method: calculateFarFogDepth(float horizontal, float dist, float uNearFogStart); - str.append("" + - "float calculateFarFogDepth(float horizontal, float dist, float uNearFogStart) \n" + - "{ \n" + - " return " + (this.heightFogMixMode == EDhApiHeightFogMixMode.BASIC ? - "(dist - uNearFogStart)/(1.0 - uNearFogStart);" : - "(horizontal - uNearFogStart)/(1.0 - uNearFogStart);") + - "} \n"); - - // Generate method: float mixFogThickness(float near, float far, float height); - str.append("" + - "float mixFogThickness(float near, float far, float height) \n" + - "{ \n" + - getMixFogLine(this.heightFogMixMode) + "\n" + - "} \n"); - } - } - - - - //=================// - // shader creation // - // helper methods // - //=================// - - private StringBuilder makeRuntimeDefine() - { - StringBuilder str = new StringBuilder(); - str.append("// =======RUNTIME GENERATED DEFINE SECTION======== //\n"); - str.append("#version 150 core\n"); - - FogSettings activeFarFogSetting = this.farFogSetting != null ? this.farFogSetting : FogSettings.EMPTY; - FogSettings activeHeightFogSetting = this.heightFogSetting != null ? this.heightFogSetting : FogSettings.EMPTY; - - str.append("\n" + - "#define farFogStart " + activeFarFogSetting.start + "\n" + - "#define farFogLength " + (activeFarFogSetting.end - activeFarFogSetting.start) + "\n" + - "#define farFogMin " + activeFarFogSetting.min + "\n" + - "#define farFogRange " + (activeFarFogSetting.max - activeFarFogSetting.min) + "\n" + - "#define farFogDensity " + activeFarFogSetting.density + "\n" + - "\n" + - "#define heightFogStart " + activeHeightFogSetting.start + "\n" + - "#define heightFogLength " + (activeHeightFogSetting.end - activeHeightFogSetting.start) + "\n" + - "#define heightFogMin " + activeHeightFogSetting.min + "\n" + - "#define heightFogRange " + (activeHeightFogSetting.max - activeHeightFogSetting.min) + "\n" + - "#define heightFogDensity " + activeHeightFogSetting.density + "\n" + - "\n"); - - str.append("// =======RUNTIME END======== //\n"); - return str; - } - - private static String getFarFogMethod(EDhApiFogFalloff fogType) - { - switch (fogType) - { - case LINEAR: - return "return linearFog(dist, farFogStart, farFogLength, farFogMin, farFogRange);\n"; - case EXPONENTIAL: - return "return exponentialFog(dist, farFogStart, farFogLength, farFogMin, farFogRange, farFogDensity);\n"; - case EXPONENTIAL_SQUARED: - return "return exponentialSquaredFog(dist, farFogStart, farFogLength, farFogMin, farFogRange, farFogDensity);\n"; - - default: - throw new IllegalArgumentException("FogType [" + fogType + "] not implemented for [getFarFogMethod]."); - } - } - - private static String getHeightDepthMethod(EDhApiHeightFogMode heightMode, float heightFogHeight) - { - String str = ""; - if (!heightMode.basedOnCamera) - { - str = " vertical = realY - (" + heightFogHeight + ");\n"; - } - - if (heightMode.below && heightMode.above) - { - str += " return abs(vertical);\n"; - } - else if (heightMode.below) - { - str += " return -vertical;\n"; - } - else if (heightMode.above) - { - str += " return vertical;\n"; - } - else - { - str += " return 0;\n"; - } - return str; - } - - /** - * Returns the method call for the given fog type.
- * Example:
- * " return linearFog(dist, heightFogStart, heightFogLength, heightFogMin, heightFogRange);" - */ - private static String getHeightFogMethod(EDhApiFogFalloff fogType) - { - switch (fogType) - { - case LINEAR: - return " return linearFog(dist, heightFogStart, heightFogLength, heightFogMin, heightFogRange);\n"; - case EXPONENTIAL: - return " return exponentialFog(dist, heightFogStart, heightFogLength, heightFogMin, heightFogRange, heightFogDensity);\n"; - case EXPONENTIAL_SQUARED: - return " return exponentialSquaredFog(dist, heightFogStart, heightFogLength, heightFogMin, heightFogRange, heightFogDensity);\n"; - - default: - throw new IllegalArgumentException("FogType [" + fogType + "] not implemented for [getHeightFogMethod]."); - } - } - - /** - * creates a line in the format
- * " return max(1.0-near, far);" - */ - private static String getMixFogLine(EDhApiHeightFogMixMode heightFogMode) - { - String str = " return "; - - switch (heightFogMode) - { - case BASIC: - case IGNORE_HEIGHT: - str += "near * far;\n"; - break; - - case ADDITION: - str += "near * (far + height);\n"; - break; - - case MAX: - str += "near * max(far, height);\n"; - break; - - case INVERSE_MULTIPLY: - str += "near * (1.0 - (1.0-far)*(1.0-height));\n"; - break; - - case MULTIPLY: - str += "near * far * height;\n"; - break; - - case LIMITED_ADDITION: - str += "near * (far + max(far, height));\n"; - break; - - case MULTIPLY_ADDITION: - str += "near * (far + far*height);\n"; - break; - - case INVERSE_MULTIPLY_ADDITION: - str += "near * (far + 1.0 - (1.0-far)*(1.0-height));\n"; - break; - - case AVERAGE: - str += "near * (far*0.5 + height*0.5);\n"; - break; - - default: - throw new IllegalArgumentException("FogType [" + heightFogMode + "] not implemented for [getMixFogMethod]."); - } - - return str; - } - - - - - - - //========================// - // default object methods // - //========================// - - @Override - public boolean equals(Object other) - { - if (this == other) - { - return true; - } - else if (other == null || this.getClass() != other.getClass()) - { - return false; - } - else - { - LodFogConfig that = (LodFogConfig) other; - return Float.compare(that.heightFogHeight, this.heightFogHeight) == 0 && - Objects.equals(this.farFogSetting, that.farFogSetting) && - Objects.equals(this.heightFogSetting, that.heightFogSetting) && this.heightFogMixMode == that.heightFogMixMode && - this.heightFogMode == that.heightFogMode - // TODO: Move these out of here - && this.earthCurveRatio == that.earthCurveRatio - && this.noiseEnable == that.noiseEnable && this.noiseSteps == that.noiseSteps && this.noiseIntensity == that.noiseIntensity && this.noiseDropoff == that.noiseDropoff; - } - } - - @Override - public int hashCode() - { - return Objects.hash(this.farFogSetting, this.heightFogSetting, this.heightFogMixMode, this.heightFogMode, this.heightFogHeight, this.earthCurveRatio, this.noiseEnable, this.noiseSteps, this.noiseIntensity, this.noiseDropoff); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java index 93b0db3f5..7159d4366 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java @@ -238,16 +238,16 @@ public class GLProxy Runnable runnable = this.renderThreadRunnableQueue.poll(); while(runnable != null) { + runnable.run(); + + // only try running for 4ms (240 FPS) at a time to prevent random lag spikes long currentTime = System.nanoTime(); long runDuration = currentTime - startTime; - // only try running for 4ms at a time to (hopefully) prevent random lag spikes if (runDuration > 4_000_000) { break; } - - runnable.run(); runnable = this.renderThreadRunnableQueue.poll(); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java index fc821a461..94c1284a1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java @@ -49,6 +49,8 @@ public class ShaderProgram /** Stores the handle of the program. */ public final int id; + + // TODO: A better way to set the fragData output name /** * Creates a shader program. @@ -73,20 +75,20 @@ public class ShaderProgram } - public ShaderProgram(List> vert, List> frag, String[] attributes) + public ShaderProgram(List> vertSupplierList, List> fragSupplierList, String[] attributes) { - id = GL32.glCreateProgram(); + this.id = GL32.glCreateProgram(); - for (Supplier v : vert) + for (Supplier vertSupplier : vertSupplierList) { - Shader vertShader = new Shader(GL32.GL_VERTEX_SHADER, v.get()); + Shader vertShader = new Shader(GL32.GL_VERTEX_SHADER, vertSupplier.get()); GL32.glAttachShader(this.id, vertShader.id); vertShader.free(); // important! } - for (Supplier f : frag) + for (Supplier fragSupplier : fragSupplierList) { - Shader fragShader = new Shader(GL32.GL_FRAGMENT_SHADER, f.get()); + Shader fragShader = new Shader(GL32.GL_FRAGMENT_SHADER, fragSupplier.get()); GL32.glAttachShader(this.id, fragShader.id); fragShader.free(); // important! } @@ -101,28 +103,22 @@ public class ShaderProgram if (status != GL32.GL_TRUE) { String message = "Shader Link Error. Details: " + GL32.glGetProgramInfoLog(this.id); - free(); // important! + this.free(); // important! throw new RuntimeException(message); } - GL32.glUseProgram(id); // This HAVE to be a direct call to prevent calling the overloaded version + GL32.glUseProgram(this.id); // This HAVE to be a direct call to prevent calling the overloaded version } - /** This will bind ShaderProgram */ - public void bind() - { - GL32.glUseProgram(id); - } - /** This will unbind ShaderProgram */ - public void unbind() - { - GL32.glUseProgram(0); - } - // REMEMBER to always free the resource! - public void free() - { - GL32.glDeleteProgram(id); - } + + + public void bind() { GL32.glUseProgram(this.id); } + public void unbind() { GL32.glUseProgram(0); } + + public void free() { GL32.glDeleteProgram(this.id); } + + + /** * WARNING: Slow native call! Cache it if possible! @@ -139,12 +135,12 @@ public class ShaderProgram if (i == -1) throw new RuntimeException("Attribute name not found: " + name); return i; } - // Same as above but without throwing errors. - // Return -1 if attribute doesn't exist or has been optimized out + /** + * Same as above but without throwing errors.
+ * Returns -1 if the attribute doesn't exist or has been optimized out. + */ public int tryGetAttributeLocation(CharSequence name) - { - return GL32.glGetAttribLocation(id, name); - } + { return GL32.glGetAttribLocation(this.id, name); } /** * WARNING: Slow native call! Cache it if possible! @@ -168,41 +164,34 @@ public class ShaderProgram // Same as above but without throwing errors. // Return -1 if uniform doesn't exist or has been optimized out public int tryGetUniformLocation(CharSequence name) - { - return GL32.glGetUniformLocation(id, name); - } + { return GL32.glGetUniformLocation(this.id, name); } - /** Requires ShaderProgram binded. */ - public void setUniform(int location, boolean value) - { - // This use -1 for false as that equals all one set - GL32.glUniform1i(location, value ? 1 : 0); - } + /** Requires a bound ShaderProgram. */ + public void setUniform(int location, boolean value) { GL32.glUniform1i(location, value ? 1 : 0); } + /** @see ShaderProgram#setUniform(int, boolean) */ + public void trySetUniform(int location, boolean value) { if (location != -1) { this.setUniform(location, value); } } - /** Requires ShaderProgram binded. */ - public void setUniform(int location, int value) - { - GL32.glUniform1i(location, value); - } + /** Requires a bound ShaderProgram. */ + public void setUniform(int location, int value) { GL32.glUniform1i(location, value); } + /** @see ShaderProgram#setUniform(int, int) */ + public void trySetUniform(int location, int value) { if (location != -1) { this.setUniform(location, value); } } - /** Requires ShaderProgram binded. */ - public void setUniform(int location, float value) - { - GL32.glUniform1f(location, value); - } + /** Requires a bound ShaderProgram. */ + public void setUniform(int location, float value) { GL32.glUniform1f(location, value); } + /** @see ShaderProgram#setUniform(int, float) */ + public void trySetUniform(int location, float value) { if (location != -1) { this.setUniform(location, value); } } - /** Requires ShaderProgram binded. */ - public void setUniform(int location, Vec3f value) - { - GL32.glUniform3f(location, value.x, value.y, value.z); - } - /** Requires ShaderProgram binded. */ - public void setUniform(int location, DhApiVec3i value) - { - GL32.glUniform3i(location, value.x, value.y, value.z); - } + /** Requires a bound ShaderProgram. */ + public void setUniform(int location, Vec3f value) { GL32.glUniform3f(location, value.x, value.y, value.z); } + /** @see ShaderProgram#setUniform(int, Vec3f) */ + public void trySetUniform(int location, Vec3f value) { if (location != -1) { this.setUniform(location, value); } } - /** Requires ShaderProgram binded. */ + /** Requires a bound ShaderProgram. */ + public void setUniform(int location, DhApiVec3i value) { GL32.glUniform3i(location, value.x, value.y, value.z); } + /** @see ShaderProgram#setUniform(int, Mat4f) */ + public void trySetUniform(int location, DhApiVec3i value) { if (location != -1) { this.setUniform(location, value); } } + + /** Requires a bound ShaderProgram. */ public void setUniform(int location, Mat4f value) { try (MemoryStack stack = MemoryStack.stackPush()) @@ -212,14 +201,22 @@ public class ShaderProgram GL32.glUniformMatrix4fv(location, false, buffer); } } + /** @see ShaderProgram#setUniform(int, Mat4f) */ + public void trySetUniform(int location, Mat4f value) { if (location != -1) { this.setUniform(location, value); } } /** - * Converts the color's RGBA values into values between 0 and 1. - * Requires ShaderProgram binded. + * Converts the color's RGBA values into values between 0 and 1.
+ * Requires a bound ShaderProgram. */ public void setUniform(int location, Color value) { - GL32.glUniform4f(location, value.getRed() / 256.0f, value.getGreen() / 256.0f, value.getBlue() / 256.0f, value.getAlpha() / 256.0f); + GL32.glUniform4f(location, + value.getRed() / 256.0f, + value.getGreen() / 256.0f, + value.getBlue() / 256.0f, + value.getAlpha() / 256.0f); } + /** @see ShaderProgram#setUniform(int, Color) */ + public void trySetUniform(int location, Color value) { if (location != -1) { this.setUniform(location, value); } } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java index cf91f4d5e..692fb8842 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java @@ -44,7 +44,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRen import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogColorMode; -import com.seibel.distanthorizons.core.render.fog.LodFogConfig; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; @@ -104,8 +103,7 @@ public class LodRenderer // The shader program - IDhApiShaderProgram lodRenderProgram = null; - LodFogConfig fogConfig; + private IDhApiShaderProgram lodRenderProgram = null; public QuadElementBuffer quadIBO = null; public boolean isSetupComplete = false; @@ -634,22 +632,6 @@ public class LodRenderer } else { - LodFogConfig newFogConfig = LodFogConfig.generateFogConfig(); // TODO use a config listener instead - if (this.fogConfig == null) - { - this.fogConfig = newFogConfig; - } - - if (!this.fogConfig.equals(newFogConfig)) - { - this.fogConfig = newFogConfig; - - this.lodRenderProgram.free(); - this.lodRenderProgram = new DhTerrainShaderProgram(); - - FogShader.INSTANCE.free(); - FogShader.INSTANCE = new FogShader(newFogConfig); - } this.lodRenderProgram.bind(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/TestRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/TestRenderer.java index 9050f3083..0d423ced2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/TestRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/TestRenderer.java @@ -58,19 +58,23 @@ public class TestRenderer public void init() { - if (init) return; + if (this.init) + { + return; + } + logger.info("init"); - init = true; - va = AbstractVertexAttribute.create(); - va.bind(); + this.init = true; + this.va = AbstractVertexAttribute.create(); + this.va.bind(); // Pos - va.setVertexAttribute(0, 0, VertexPointer.addVec2Pointer(false)); + this.va.setVertexAttribute(0, 0, VertexPointer.addVec2Pointer(false)); // Color - va.setVertexAttribute(0, 1, VertexPointer.addVec4Pointer(false)); - va.completeAndCheck(Float.BYTES * 6); - basicShader = new ShaderProgram("shaders/test/vert.vert", "shaders/test/frag.frag", + this.va.setVertexAttribute(0, 1, VertexPointer.addVec4Pointer(false)); + this.va.completeAndCheck(Float.BYTES * 6); + this.basicShader = new ShaderProgram("shaders/test/vert.vert", "shaders/test/frag.frag", "fragColor", new String[]{"vPosition", "color"}); - createBuffer(); + this.createBuffer(); } // Render a square with uv color @@ -97,8 +101,8 @@ public class TestRenderer private void createBuffer() { - sharedContextBuffer = createTextingBuffer(); - sameContextBuffer = createTextingBuffer(); + this.sharedContextBuffer = createTextingBuffer(); + this.sameContextBuffer = createTextingBuffer(); } public void render() @@ -106,7 +110,7 @@ public class TestRenderer spamLogger.debug("rendering"); GLState state = new GLState(); - init(); + this.init(); GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, MC_RENDER.getTargetFrameBuffer()); GL32.glViewport(0, 0, MC_RENDER.getTargetFrameBufferViewportWidth(), MC_RENDER.getTargetFrameBufferViewportHeight()); GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); @@ -116,20 +120,20 @@ public class TestRenderer GL32.glDisable(GL32.GL_BLEND); //GL32.glDisable(GL32.GL_SCISSOR_TEST); - basicShader.bind(); - va.bind(); + this.basicShader.bind(); + this.va.bind(); // Switch between the two buffers per second if (System.currentTimeMillis() % 2000 < 1000) { - sameContextBuffer.bind(); - va.bindBufferToAllBindingPoints(sameContextBuffer.getId()); + this.sameContextBuffer.bind(); + this.va.bindBufferToAllBindingPoints(this.sameContextBuffer.getId()); spamLogger.debug("same context buffer"); } else { - sameContextBuffer.bind(); - va.bindBufferToAllBindingPoints(sharedContextBuffer.getId()); + this.sameContextBuffer.bind(); + this.va.bindBufferToAllBindingPoints(this.sharedContextBuffer.getId()); spamLogger.debug("shared context buffer"); } // Render the square diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java index 4e1887b8d..4d5cd3e98 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java @@ -582,9 +582,23 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister shaderProgram.fillSharedDirectUniformData(renderEventParam, shading, boxGroup, camPos); - for (DhApiRenderableBox box : boxGroup) + for (int i = 0; i < boxGroup.size(); i++) { - this.renderBox(shaderProgram, renderEventParam, boxGroup, box, camPos); + try + { + DhApiRenderableBox box = boxGroup.get(i); + if (box != null) + { + this.renderBox(shaderProgram, renderEventParam, boxGroup, box, camPos); + } + } + catch (IndexOutOfBoundsException e) + { + // Concurrency issue, the list was modified while rendering + // this can probably be ignored. + // However, if it does become a problem we can add locks to the box group. + break; + } } } private void renderBox( diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java index f35eafc94..51666a335 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java @@ -20,11 +20,11 @@ package com.seibel.distanthorizons.core.render.renderer.shaders; import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogColorMode; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogDirection; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.render.fog.LodFogConfig; import com.seibel.distanthorizons.core.render.glObject.GLState; -import com.seibel.distanthorizons.core.render.glObject.shader.Shader; import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram; import com.seibel.distanthorizons.core.render.renderer.LodRenderer; import com.seibel.distanthorizons.core.render.renderer.ScreenQuad; @@ -38,7 +38,7 @@ import java.awt.*; public class FogShader extends AbstractShaderRenderer { - public static FogShader INSTANCE = new FogShader(LodFogConfig.generateFogConfig()); + public static final FogShader INSTANCE = new FogShader(); private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IVersionConstants VERSION_CONSTANTS = SingletonInjector.INSTANCE.get(IVersionConstants.class); @@ -46,21 +46,46 @@ public class FogShader extends AbstractShaderRenderer public int frameBuffer; - private final LodFogConfig fogConfig; private Mat4f inverseMvmProjMatrix; - // Uniforms + //==========// + // Uniforms // + //==========// + + public int uDepthMap; + /** Inverted Model View Projection matrix */ + public int uInvMvmProj; + + // fog uniforms public int uFogColor; public int uFogScale; public int uFogVerticalScale; - public int uNearFogStart; - public int uNearFogLength; public int uFullFogMode; - /** Inverted Model View Projection matrix */ - public int uInvMvmProj; - public int uDepthMap; + // far fog + public int uFarFogStart; + public int uFarFogLength; + public int uFarFogMin; + public int uFarFogRange; + public int uFarFogDensity; + + // height fog + public int uHeightFogStart; + public int uHeightFogLength; + public int uHeightFogMin; + public int uHeightFogRange; + public int uHeightFogDensity; + + public int uHeightFogEnabled; + public int uHeightFogFalloffType; + public int uHeightBasedOnCamera; + public int uHeightFogBaseHeight; + public int uHeightFogAppliesUp; + public int uHeightFogAppliesDown; + public int uUseSphericalFog; + public int uHeightFogMixingMode; + public int uCameraBlockYPos; @@ -68,15 +93,13 @@ public class FogShader extends AbstractShaderRenderer // constructor // //=============// - public FogShader(LodFogConfig fogConfig) { this.fogConfig = fogConfig; } - + public FogShader() { } + @Override public void onInit() { this.shader = new ShaderProgram( - // TODO rename normal.vert to something like "postProcess.vert" - () -> Shader.loadFile("shaders/normal.vert", false, new StringBuilder()).toString(), - () -> this.fogConfig.loadAndProcessFragShader("shaders/fog/fog.frag", false).toString(), + "shaders/normal.vert", "shaders/fog/fog.frag", "fragColor", new String[]{"vPosition"} ); @@ -87,15 +110,35 @@ public class FogShader extends AbstractShaderRenderer this.uInvMvmProj = this.shader.getUniformLocation("uInvMvmProj"); // Fog uniforms - this.uFogScale = this.shader.tryGetUniformLocation("uFogScale"); - this.uFogVerticalScale = this.shader.tryGetUniformLocation("uFogVerticalScale"); - this.uFogColor = this.shader.tryGetUniformLocation("uFogColor"); - this.uFullFogMode = this.shader.tryGetUniformLocation("uFullFogMode"); + this.uFogScale = this.shader.getUniformLocation("uFogScale"); + this.uFogVerticalScale = this.shader.getUniformLocation("uFogVerticalScale"); + this.uFogColor = this.shader.getUniformLocation("uFogColor"); + this.uFullFogMode = this.shader.getUniformLocation("uFullFogMode"); - // near fog - this.uNearFogStart = this.shader.tryGetUniformLocation("uNearFogStart"); - this.uNearFogLength = this.shader.tryGetUniformLocation("uNearFogLength"); + // fog config + this.uFarFogStart = this.shader.getUniformLocation("uFarFogStart"); + this.uFarFogLength = this.shader.getUniformLocation("uFarFogLength"); + this.uFarFogMin = this.shader.getUniformLocation("uFarFogMin"); + this.uFarFogRange = this.shader.getUniformLocation("uFarFogRange"); + this.uFarFogDensity = this.shader.getUniformLocation("uFarFogDensity"); + // height fog + this.uHeightFogStart = this.shader.getUniformLocation("uHeightFogStart"); + this.uHeightFogLength = this.shader.getUniformLocation("uHeightFogLength"); + this.uHeightFogMin = this.shader.getUniformLocation("uHeightFogMin"); + this.uHeightFogRange = this.shader.getUniformLocation("uHeightFogRange"); + this.uHeightFogDensity = this.shader.getUniformLocation("uHeightFogDensity"); + + this.uHeightFogEnabled = this.shader.getUniformLocation("uHeightFogEnabled"); + this.uHeightFogFalloffType = this.shader.getUniformLocation("uHeightFogFalloffType"); + this.uHeightBasedOnCamera = this.shader.getUniformLocation("uHeightBasedOnCamera"); + this.uHeightFogBaseHeight = this.shader.getUniformLocation("uHeightFogBaseHeight"); + this.uHeightFogAppliesUp = this.shader.getUniformLocation("uHeightFogAppliesUp"); + this.uHeightFogAppliesDown = this.shader.getUniformLocation("uHeightFogAppliesDown"); + this.uUseSphericalFog = this.shader.getUniformLocation("uUseSphericalFog"); + this.uHeightFogMixingMode = this.shader.getUniformLocation("uHeightFogMixingMode"); + this.uCameraBlockYPos = this.shader.getUniformLocation("uCameraBlockYPos"); + } @@ -107,22 +150,66 @@ public class FogShader extends AbstractShaderRenderer @Override protected void onApplyUniforms(float partialTicks) { + int lodDrawDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH; + + + if (this.inverseMvmProjMatrix != null) { this.shader.setUniform(this.uInvMvmProj, this.inverseMvmProjMatrix); } - int lodDrawDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH; - // Fog - if (this.uFullFogMode != -1) this.shader.setUniform(this.uFullFogMode, MC_RENDER.isFogStateSpecial() ? 1 : 0); - if (this.uFogColor != -1) this.shader.setUniform(this.uFogColor, MC_RENDER.isFogStateSpecial() ? this.getSpecialFogColor(partialTicks) : this.getFogColor(partialTicks)); + // Fog uniforms + this.shader.setUniform(this.uFogColor, MC_RENDER.isFogStateSpecial() ? this.getSpecialFogColor(partialTicks) : this.getFogColor(partialTicks)); + this.shader.setUniform(this.uFogScale, 1.f / lodDrawDistance); + this.shader.setUniform(this.uFogVerticalScale, 1.f / MC.getWrappedClientLevel().getMaxHeight()); + this.shader.setUniform(this.uFullFogMode, MC_RENDER.isFogStateSpecial() ? 1 : 0); + + + // fog config + float farFogStart = Config.Client.Advanced.Graphics.Fog.farFogStart.get().floatValue(); + float farFogEnd = Config.Client.Advanced.Graphics.Fog.farFogEnd.get().floatValue(); + float farFogMin = Config.Client.Advanced.Graphics.Fog.farFogMin.get().floatValue(); + float farFogMax = Config.Client.Advanced.Graphics.Fog.farFogMax.get().floatValue(); + float farFogDensity = Config.Client.Advanced.Graphics.Fog.farFogDensity.get().floatValue(); + + this.shader.setUniform(this.uFarFogStart, farFogStart); + this.shader.setUniform(this.uFarFogLength, farFogEnd - farFogStart); + this.shader.setUniform(this.uFarFogMin, farFogMin); + this.shader.setUniform(this.uFarFogRange, farFogMax - farFogMin); + this.shader.setUniform(this.uFarFogDensity, farFogDensity); + + + // height config + EDhApiHeightFogMixMode heightFogMixingMode = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMixMode.get(); + boolean heightFogEnabled = heightFogMixingMode != EDhApiHeightFogMixMode.SPHERICAL && heightFogMixingMode != EDhApiHeightFogMixMode.CYLINDRICAL; + boolean useSphericalFog = heightFogMixingMode == EDhApiHeightFogMixMode.SPHERICAL; + EDhApiHeightFogDirection heightFogCameraDirection = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDirection.get(); + + float heightFogStart = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogStart.get().floatValue(); + float heightFogEnd = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogEnd.get().floatValue(); + float heightFogMin = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMin.get().floatValue(); + float heightFogMax = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMax.get().floatValue(); + float heightFogDensity = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDensity.get().floatValue(); + + this.shader.setUniform(this.uHeightFogStart, heightFogStart); + this.shader.setUniform(this.uHeightFogLength, heightFogEnd - heightFogStart); + this.shader.setUniform(this.uHeightFogMin, heightFogMin); + this.shader.setUniform(this.uHeightFogRange, heightFogMax - heightFogMin); + this.shader.setUniform(this.uHeightFogDensity, heightFogDensity); + + + this.shader.setUniform(this.uHeightFogEnabled, heightFogEnabled); + this.shader.setUniform(this.uHeightFogFalloffType, Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogFalloff.get().value); + this.shader.setUniform(this.uHeightFogBaseHeight, Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogBaseHeight.get().floatValue()); + this.shader.setUniform(this.uHeightBasedOnCamera, heightFogCameraDirection.basedOnCamera); + this.shader.setUniform(this.uHeightFogAppliesUp, heightFogCameraDirection.fogAppliesUp); + this.shader.setUniform(this.uHeightFogAppliesDown, heightFogCameraDirection.fogAppliesDown); + this.shader.setUniform(this.uUseSphericalFog, useSphericalFog); + this.shader.setUniform(this.uHeightFogMixingMode, heightFogMixingMode.value); + this.shader.setUniform(this.uCameraBlockYPos, (float)MC_RENDER.getCameraExactPosition().y); - float nearFogStart = (VERSION_CONSTANTS.isVanillaRenderedChunkSquare() ? (float) Math.sqrt(2.0) : 1.0f) / lodDrawDistance; - if (this.uNearFogStart != -1) this.shader.setUniform(this.uNearFogStart, nearFogStart); - if (this.uNearFogLength != -1) this.shader.setUniform(this.uNearFogLength, 0.0f); - if (this.uFogScale != -1) this.shader.setUniform(this.uFogScale, 1.f / lodDrawDistance); - if (this.uFogVerticalScale != -1) this.shader.setUniform(this.uFogVerticalScale, 1.f / MC.getWrappedClientLevel().getMaxHeight()); } private Color getFogColor(float partialTicks) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java index 2e18c15f1..cc1a5a0dd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java @@ -23,18 +23,19 @@ import java.awt.*; /** * Handles the bit-wise math used when - * dealing with colors stored as integers. + * dealing with colors stored as integers.

* + * Minecraft color format is: 0xAA BB GG RR
+ * DH mod color format is: 0xAA RR GG BB
+ * OpenGL RGBA format native order: 0xRR GG BB AA
+ * OpenGL RGBA format Java Order: 0xAA BB GG RR
+ * * @author Cola * @author Leonardo Amato * @version 2023-5-15 */ public class ColorUtil { - //note: Minecraft color format is: 0xAA BB GG RR - //________ DH mod color format is: 0xAA RR GG BB - //OpenGL RGBA format native order: 0xRR GG BB AA - //_ OpenGL RGBA format Java Order: 0xAA BB GG RR public static final int INVISIBLE = argbToInt(0, 0, 0, 0); @@ -215,7 +216,7 @@ public class ColorUtil } public static Color toColorObjRGB(int color) { return new Color(getRed(color), getGreen(color), getBlue(color)); } - public static Color toColorObjRGBA(int color) { return new Color(getRed(color), getGreen(color), getBlue(color), getAlpha(color)); } + public static Color toColorObjARGB(int color) { return new Color(getRed(color), getGreen(color), getBlue(color), getAlpha(color)); } public static int toColorInt(Color color) { return argbToInt(color.getAlpha(), color.getRed(), color.getGreen(), color.getBlue()); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java index 054cc8936..37dc6957f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java @@ -189,14 +189,6 @@ public class RenderUtil return false; //Level is not ready yet. } - /* if (MC_RENDER.isFogStateSpecial()) - { - // if the player is blind/under-water, don't render LODs, - // and don't change minecraft's fog - // which blindness relies on. - return false; - } */ - if (MC_RENDER.getLightmapWrapper(levelWrapper) == null) { return false; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/DummyRunExecutorService.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/RunOnThisThreadExecutorService.java similarity index 79% rename from core/src/main/java/com/seibel/distanthorizons/core/util/objects/DummyRunExecutorService.java rename to core/src/main/java/com/seibel/distanthorizons/core/util/objects/RunOnThisThreadExecutorService.java index 407cf3673..f9e84d197 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/DummyRunExecutorService.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/RunOnThisThreadExecutorService.java @@ -19,6 +19,8 @@ package com.seibel.distanthorizons.core.util.objects; +import org.jetbrains.annotations.NotNull; + import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -30,45 +32,43 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -public class DummyRunExecutorService implements ExecutorService +/** + * This runs the given {@link Runnable}'s on the same + * thread as they're queued. + * This can be used to change MC threads to run on DH threads. + * This is done to prevent concurrent issues with world generation + * and reduce putting undue load on MC threads. + */ +public class RunOnThisThreadExecutorService implements ExecutorService { private boolean shutdownCalled = false; - @Override - public void execute(Runnable command) - { - command.run(); - } + @Override - public void shutdown() - { - shutdownCalled = true; - } + public void execute(Runnable command) { command.run(); } @Override + public void shutdown() { this.shutdownCalled = true; } + + @Override + @NotNull public List shutdownNow() { - shutdownCalled = true; - return new ArrayList(); + this.shutdownCalled = true; + return new ArrayList<>(); } @Override - public boolean isShutdown() - { - return shutdownCalled; - } + public boolean isShutdown() { return this.shutdownCalled; } @Override - public boolean isTerminated() - { - return shutdownCalled; - } + public boolean isTerminated() { return this.shutdownCalled; } @Override public boolean awaitTermination(long timeout, TimeUnit unit) { - shutdownCalled = true; + this.shutdownCalled = true; return true; } @@ -119,7 +119,7 @@ public class DummyRunExecutorService implements ExecutorService List> futures = new ArrayList>(tasks.size()); for (Callable t : tasks) { - futures.add(submit(t)); + futures.add(this.submit(t)); } return futures; } @@ -151,7 +151,7 @@ public class DummyRunExecutorService implements ExecutorService @Override public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws ExecutionException { - return invokeAny(tasks); + return this.invokeAny(tasks); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java index 39151eb93..c78dbefd7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java @@ -56,11 +56,6 @@ public class ThreadPoolUtil @Nullable public static ThreadPoolExecutor getWorldGenExecutor() { return worldGenThreadPool.executor; } - public static final String BUFFER_UPLOADER_THREAD_NAME = "Buffer Uploader"; - private static ThreadPoolExecutor bufferUploaderThreadPool; - @Nullable - public static ThreadPoolExecutor getBufferUploaderExecutor() { return bufferUploaderThreadPool; } - public static final String CLEANUP_THREAD_NAME = "Cleanup"; private static ThreadPoolExecutor cleanupThreadPool; @Nullable @@ -118,7 +113,6 @@ public class ThreadPoolUtil updatePropagatorThreadPool = new ConfigThreadPool(UPDATE_PROPAGATOR_THREAD_FACTORY, Config.Common.MultiThreading.numberOfUpdatePropagatorThreads, Config.Common.MultiThreading.runTimeRatioForUpdatePropagatorThreads, null); worldGenThreadPool = new ConfigThreadPool(WORLD_GEN_THREAD_FACTORY, Config.Common.MultiThreading.numberOfWorldGenerationThreads, Config.Common.MultiThreading.runTimeRatioForWorldGenerationThreads, null); networkCompressionThreadPool = new ConfigThreadPool(NETWORK_COMPRESSION_THREAD_FACTORY, Config.Common.MultiThreading.numberOfNetworkCompressionThreads, Config.Common.MultiThreading.runTimeRatioForNetworkCompressionThreads, null); - bufferUploaderThreadPool = ThreadUtil.makeSingleThreadPool(BUFFER_UPLOADER_THREAD_NAME); cleanupThreadPool = ThreadUtil.makeSingleThreadPool(CLEANUP_THREAD_NAME); beaconCullingThreadPool = ThreadUtil.makeSingleThreadPool(BEACON_CULLING_THREAD_NAME); @@ -160,7 +154,6 @@ public class ThreadPoolUtil updatePropagatorThreadPool.shutdownExecutorService(); worldGenThreadPool.shutdownExecutorService(); networkCompressionThreadPool.shutdownExecutorService(); - bufferUploaderThreadPool.shutdown(); cleanupThreadPool.shutdown(); beaconCullingThreadPool.shutdown(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java index 780c68a5f..36677667d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java @@ -10,16 +10,27 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import org.jetbrains.annotations.NotNull; -import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; public abstract class AbstractDhServerWorld extends AbstractDhWorld implements IDhServerWorld { - protected final HashMap dhLevelByLevelWrapper = new HashMap<>(); + /** + * Concurrent since levels can be added/remove while other processing is happening. + * (Otherwise we may need to just put the logic in a lock. + */ + protected final ConcurrentHashMap dhLevelByLevelWrapper = new ConcurrentHashMap<>(); public final LocalSaveStructure saveStructure = new LocalSaveStructure(); private final ServerPlayerStateManager serverPlayerStateManager; + + + //=============// + // constructor // + //=============// + public AbstractDhServerWorld(EWorldEnvironment worldEnvironment) { super(worldEnvironment); @@ -31,7 +42,6 @@ public abstract class AbstractDhServerWorld) this.dhLevelByLevelWrapper.values().stream().distinct()::iterator) + Iterator it = this.dhLevelByLevelWrapper.values().stream().distinct().iterator(); + while (it.hasNext()) { + TDhServerLevel level = it.next(); level.registerNetworkHandlers(playerState); } @@ -110,7 +122,7 @@ public abstract class AbstractDhServerWorld getAllLoadedLevelWrappers() + public Iterable getAllLoadedLevelWrappers() throws IllegalStateException { AbstractDhWorld world = SharedApi.getAbstractDhWorld(); if (world == null) @@ -155,7 +155,7 @@ public class DhApiWorldProxy implements IDhApiWorldProxy } @Override - public Iterable getAllLoadedLevelsForDimensionType(IDhApiDimensionTypeWrapper dimensionTypeWrapper) + public Iterable getAllLoadedLevelsForDimensionType(IDhApiDimensionTypeWrapper dimensionTypeWrapper) throws IllegalStateException { AbstractDhWorld world = SharedApi.getAbstractDhWorld(); if (world == null) @@ -176,7 +176,7 @@ public class DhApiWorldProxy implements IDhApiWorldProxy } @Override - public Iterable getAllLoadedLevelsWithDimensionNameLike(String dimensionName) + public Iterable getAllLoadedLevelsWithDimensionNameLike(String dimensionName) throws IllegalStateException { AbstractDhWorld world = SharedApi.getAbstractDhWorld(); if (world == null) @@ -190,7 +190,7 @@ public class DhApiWorldProxy implements IDhApiWorldProxy for (IDhLevel dhLevel : world.getAllLoadedLevels()) { ILevelWrapper levelWrapper = dhLevel.getLevelWrapper(); - String levelDimName = levelWrapper.getDimensionName().toLowerCase(); + String levelDimName = levelWrapper.getDhIdentifier().toLowerCase(); if (levelDimName.contains(soughtDimName)) { returnList.add(levelWrapper); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java index 714c94b76..4a048f0c7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientServerWorld.java @@ -79,7 +79,7 @@ public class DhClientServerWorld extends AbstractDhServerWorld> 4; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java index c615d4e96..0b81e7096 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java @@ -27,7 +27,6 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IMutableBlockPosWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; import com.seibel.distanthorizons.core.util.LodUtil; @@ -47,18 +46,20 @@ public interface IChunkWrapper extends IBindable DhChunkPos getChunkPos(); - default int getHeight() { return this.getMaxBuildHeight() - this.getMinBuildHeight(); } - int getMinBuildHeight(); - int getMaxBuildHeight(); + default int getHeight() { return this.getExclusiveMaxBuildHeight() - this.getInclusiveMinBuildHeight(); } + /** inclusive (IE if returning -64 the min block can be placed at -64) */ + int getInclusiveMinBuildHeight(); + /** exclusive (IE if returning 320 the max block can be placed at 319) */ + int getExclusiveMaxBuildHeight(); /** * returns the Y level for the last non-empty section in this chunk, - * or {@link IChunkWrapper#getMinBuildHeight()} if this chunk is completely empty. + * or {@link IChunkWrapper#getInclusiveMinBuildHeight()} if this chunk is completely empty. */ int getMinNonEmptyHeight(); /** * returns the Y level for the first non-empty section in this chunk, - * or {@link IChunkWrapper#getMaxBuildHeight()} if this chunk is completely empty. + * or {@link IChunkWrapper#getExclusiveMaxBuildHeight()} if this chunk is completely empty. */ int getMaxNonEmptyHeight(); @@ -99,7 +100,7 @@ public interface IChunkWrapper extends IBindable default boolean blockPosInsideChunk(int x, int y, int z) { return (x >= this.getMinBlockX() && x <= this.getMaxBlockX() - && y >= this.getMinBuildHeight() && y < this.getMaxBuildHeight() + && y >= this.getInclusiveMinBuildHeight() && y < this.getExclusiveMaxBuildHeight() && z >= this.getMinBlockZ() && z <= this.getMaxBlockZ()); } default boolean blockPosInsideChunk(DhBlockPos2D blockPos) @@ -144,8 +145,8 @@ public interface IChunkWrapper extends IBindable // FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the constructor - int minHeight = this.getMinBuildHeight(); - int maxHeight = this.getMaxBuildHeight() + 1; + int minHeight = this.getInclusiveMinBuildHeight(); + int maxHeight = this.getExclusiveMaxBuildHeight() + 1; if (x < 0 || x >= LodUtil.CHUNK_WIDTH || z < 0 || z >= LodUtil.CHUNK_WIDTH @@ -157,6 +158,27 @@ public interface IChunkWrapper extends IBindable throw new IndexOutOfBoundsException(errorMessage); } } + /** used to prevent accidentally attempting to get/set values outside this chunk's boundaries */ + default void throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(int x, int z) throws IndexOutOfBoundsException + { + if (!RUN_RELATIVE_POS_INDEX_VALIDATION) + { + return; + } + + + // FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the constructor + int minHeight = this.getInclusiveMinBuildHeight(); + int maxHeight = this.getExclusiveMaxBuildHeight() + 1; + + if (x < 0 || x >= LodUtil.CHUNK_WIDTH + || z < 0 || z >= LodUtil.CHUNK_WIDTH) + { + String errorMessage = "Relative position [" + x + "," + z + "] out of bounds. \n" + + "X/Z must be between 0 and 15 (inclusive)."; + throw new IndexOutOfBoundsException(errorMessage); + } + } /** @@ -167,7 +189,7 @@ public interface IChunkWrapper extends IBindable */ default int relativeBlockPosToIndex(int xRel, int y, int zRel) { - int yRel = y - this.getMinBuildHeight(); + int yRel = y - this.getInclusiveMinBuildHeight(); return (zRel * LodUtil.CHUNK_WIDTH * this.getHeight()) + (yRel * LodUtil.CHUNK_WIDTH) + xRel; } @@ -183,7 +205,7 @@ public interface IChunkWrapper extends IBindable index -= (zRel * LodUtil.CHUNK_WIDTH * this.getHeight()); final int y = index / LodUtil.CHUNK_WIDTH; - final int yRel = y + this.getMinBuildHeight(); + final int yRel = y + this.getInclusiveMinBuildHeight(); final int xRel = index % LodUtil.CHUNK_WIDTH; return new DhBlockPos(xRel, yRel, zRel); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java index bec5ddcff..fcb22b01e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java @@ -25,6 +25,7 @@ import java.util.UUID; import com.seibel.distanthorizons.core.enums.EDhDirection; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; +import com.seibel.distanthorizons.core.render.glObject.GLProxy; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; @@ -147,7 +148,11 @@ public interface IMinecraftClientWrapper extends IBindable Object getOptionsObject(); - /** Executes the given task on Minecraft's render thread. */ + /** + * Executes the given task on Minecraft's render thread. + * @deprecated use {@link GLProxy#runningOnRenderThread()} instead + */ + @Deprecated void executeOnRenderThread(Runnable runnable); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftSharedWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftSharedWrapper.java index fc2a761bc..28b65d32d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftSharedWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftSharedWrapper.java @@ -30,9 +30,6 @@ public interface IMinecraftSharedWrapper extends IBindable File getInstallationDirectory(); - /** @return true if this is the first time loading this world */ - boolean isWorldNew(); - int getPlayerCount(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java index 550a88428..d10b8e71b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java @@ -36,7 +36,15 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable @Override String getDimensionName(); - + + long getHashedSeed(); + + /** + * A string intended to uniquely identify this level. + */ + @Override + default String getDhIdentifier() { return this.getDimensionName() + "_" + this.getHashedSeed(); } + @Override boolean hasCeiling(); @@ -61,7 +69,7 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable /** Fired when the level is being unloaded. Doesn't unload the level. */ void onUnload(); - // TODO I don't like the circular reference, can we merge the level wrapper and DhLevels? + // TODO James doesn't like this circular reference, can we merge the level wrapper and DhLevels? @Deprecated void setParentLevel(IDhLevel parentLevel); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java index dd8c95c8c..c4e419d12 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java @@ -32,7 +32,7 @@ public interface IServerLevelWrapper extends ILevelWrapper default String getKeyedLevelDimensionName() { - String dimensionName = this.getDimensionName(); + String dimensionName = this.getDhIdentifier(); if (Config.Server.sendLevelKeys.get()) { @@ -43,18 +43,22 @@ public interface IServerLevelWrapper extends ILevelWrapper String cleanWorldFolderName = this.getMcSaveFolder().getParentFile().getName() .replaceAll("[^" + LevelInitMessage.PART_ALLOWED_CHARS_REGEX + " ]", "") .replaceAll(" ", "_"); - levelKeyPrefix += (!levelKeyPrefix.isEmpty() ? "_" : "") + cleanWorldFolderName; + + levelKeyPrefix += (!levelKeyPrefix.isEmpty() ? "_" : "") + cleanWorldFolderName + + "_" + this.getHashedSeed(); } - if (!levelKeyPrefix.isEmpty()) + if (levelKeyPrefix.isEmpty()) { - String mainPart = "@" + dimensionName; - - return levelKeyPrefix.substring(0, Math.min( - LevelInitMessage.MAX_LENGTH - mainPart.length(), - levelKeyPrefix.length() - )) + mainPart; + levelKeyPrefix = String.valueOf(this.getHashedSeed()); } + + String mainPart = "@" + dimensionName; + + return levelKeyPrefix.substring(0, Math.min( + LevelInitMessage.MAX_LENGTH - mainPart.length(), + levelKeyPrefix.length() + )) + mainPart; } return dimensionName; diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 10236c0ca..a2940b8c9 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -269,10 +269,10 @@ "distanthorizons.config.client.advanced.graphics.fog.heightFog.heightFogMixMode": "Height Fog Mix Mode", "distanthorizons.config.client.advanced.graphics.fog.heightFog.heightFogMixMode.@tooltip": - "How the height should effect the fog thickness combined with the normal function? \n\nBASIC: No special height fog effect. Fog is calculated based on camera distance \nIGNORE_HEIGHT: Ignore height completely. Fog is calculated based on horizontal distance \nADDITION: heightFog + farFog \nMAX: max(heightFog, farFog) \nMULTIPLY: heightFog * farFog \nINVERSE_MULTIPLY: 1 - (1-heightFog) * (1-farFog) \nLIMITED_ADDITION: farFog + max(farFog, heightFog) \nMULTIPLY_ADDITION: farFog + farFog * heightFog \nINVERSE_MULTIPLY_ADDITION: farFog + 1 - (1-heightFog) * (1-farFog) \nAVERAGE: farFog*0.5 + heightFog*0.5 \n\nNote that for 'BASIC' mode and 'IGNORE_HEIGHT' mode, fog settings for height fog has no effect.\n", - "distanthorizons.config.client.advanced.graphics.fog.heightFog.heightFogMode": - "Height Fog Mode", - "distanthorizons.config.client.advanced.graphics.fog.heightFog.heightFogMode.@tooltip": + "Defines how height effects fog. \nWhen in doubt start with SPHERICAL, ADDITION, or MAX. \n\nSPHERICAL: Fog is calculated based on camera distance. \nCYLINDRICAL: Ignore height, fog is calculated based on horizontal distance. \nMAX: max(heightFog, farFog) \nADDITION: heightFog + farFog \nMULTIPLY: heightFog * farFog \nINVERSE_MULTIPLY: 1 - (1-heightFog) * (1-farFog) \nLIMITED_ADDITION: farFog + max(farFog, heightFog) \nMULTIPLY_ADDITION: farFog + farFog * heightFog \nINVERSE_MULTIPLY_ADDITION: farFog + 1 - (1-heightFog) * (1-farFog) \nAVERAGE: farFog*0.5 + heightFog*0.5", + "distanthorizons.config.client.advanced.graphics.fog.heightFog.heightFogDirection": + "Height Fog Direction", + "distanthorizons.config.client.advanced.graphics.fog.heightFog.heightFogDirection.@tooltip": "Where should the height fog be located? \n\nABOVE_CAMERA: Height fog starts from camera to the sky \nBELOW_CAMERA: Height fog starts from camera to the void \nABOVE_AND_BELOW_CAMERA: Height fog starts from camera to both the sky and the void \nABOVE_SET_HEIGHT: Height fog starts from a set height to the sky \nBELOW_SET_HEIGHT: Height fog starts from a set height to the void \nABOVE_AND_BELOW_SET_HEIGHT: Height fog starts from a set height to both the sky and the void \n", "distanthorizons.config.client.advanced.graphics.fog.heightFog.heightFogBaseHeight": "Height Fog Base Height", @@ -571,6 +571,10 @@ "Lossy World Compression", "distanthorizons.config.common.lodBuilding.worldCompression.@tooltip": "How should block data be compressed when creating LOD data? \nThis setting will only affect new or updated LOD data, \nany data already generated when this setting is changed will be \nunaffected until it is modified or re-loaded. \n\nMost Accurate: Merge Same Blocks \nHighest Compression: Visually Equal", + "distanthorizons.config.common.lodBuilding.recalculateChunkHeightmaps": + "Recalculate Chunk Heightmaps", + "distanthorizons.config.common.lodBuilding.recalculateChunkHeightmaps.@tooltip": + "True: Recalculate chunk height maps before chunks can be used by DH. This can fix problems with worlds created by external tools. \nFalse: Assume any height maps handled by Minecraft are correct. \n\nFastest: False\nMost Compatible: True", "distanthorizons.config.common.lodBuilding.showMigrationChatWarning": "Log Migration In Chat", @@ -801,10 +805,10 @@ "distanthorizons.config.enum.EDhApiFogFalloff.EXPONENTIAL_SQUARED": "Exponential squared", - "distanthorizons.config.enum.EDhApiHeightFogMixMode.BASIC": - "Basic", - "distanthorizons.config.enum.EDhApiHeightFogMixMode.IGNORE_HEIGHT": - "Ignore Height", + "distanthorizons.config.enum.EDhApiHeightFogMixMode.SPHERICAL": + "Spherical", + "distanthorizons.config.enum.EDhApiHeightFogMixMode.CYLINDRICAL": + "Cylindrical", "distanthorizons.config.enum.EDhApiHeightFogMixMode.ADDITION": "Addition", "distanthorizons.config.enum.EDhApiHeightFogMixMode.MAX": @@ -822,17 +826,17 @@ "distanthorizons.config.enum.EDhApiHeightFogMixMode.AVERAGE": "Average", - "distanthorizons.config.enum.EDhApiHeightFogMode.ABOVE_CAMERA": + "distanthorizons.config.enum.EDhApiHeightFogDirection.ABOVE_CAMERA": "Above Camera", - "distanthorizons.config.enum.EDhApiHeightFogMode.BELOW_CAMERA": + "distanthorizons.config.enum.EDhApiHeightFogDirection.BELOW_CAMERA": "Below Camera", - "distanthorizons.config.enum.EDhApiHeightFogMode.ABOVE_AND_BELOW_CAMERA": + "distanthorizons.config.enum.EDhApiHeightFogDirection.ABOVE_AND_BELOW_CAMERA": "Above And Below Camera", - "distanthorizons.config.enum.EDhApiHeightFogMode.ABOVE_SET_HEIGHT": + "distanthorizons.config.enum.EDhApiHeightFogDirection.ABOVE_SET_HEIGHT": "Above Set Height", - "distanthorizons.config.enum.EDhApiHeightFogMode.BELOW_SET_HEIGHT": + "distanthorizons.config.enum.EDhApiHeightFogDirection.BELOW_SET_HEIGHT": "Below Set Height", - "distanthorizons.config.enum.EDhApiHeightFogMode.ABOVE_AND_BELOW_SET_HEIGHT": + "distanthorizons.config.enum.EDhApiHeightFogDirection.ABOVE_AND_BELOW_SET_HEIGHT": "Above And Below Set Height", "distanthorizons.config.enum.EDhApiVanillaOverdraw.NEVER": diff --git a/core/src/main/resources/assets/distanthorizons/textures/clouds - original.png b/core/src/main/resources/assets/distanthorizons/textures/clouds-original.png similarity index 100% rename from core/src/main/resources/assets/distanthorizons/textures/clouds - original.png rename to core/src/main/resources/assets/distanthorizons/textures/clouds-original.png diff --git a/core/src/main/resources/assets/distanthorizons/textures/test grid.png b/core/src/main/resources/assets/distanthorizons/textures/test-grid.png similarity index 100% rename from core/src/main/resources/assets/distanthorizons/textures/test grid.png rename to core/src/main/resources/assets/distanthorizons/textures/test-grid.png diff --git a/core/src/main/resources/shaders/fog/fog.frag b/core/src/main/resources/shaders/fog/fog.frag index eed4c6b6b..a4495bd82 100644 --- a/core/src/main/resources/shaders/fog/fog.frag +++ b/core/src/main/resources/shaders/fog/fog.frag @@ -1,99 +1,120 @@ +#version 150 core in vec2 TexCoord; out vec4 fragColor; + + uniform sampler2D uDepthMap; // inverted model view matrix and projection matrix uniform mat4 uInvMvmProj; +// fog uniforms +uniform vec4 uFogColor; uniform float uFogScale; uniform float uFogVerticalScale; -uniform vec4 uFogColor; uniform int uFullFogMode; +uniform int uFogFalloffType; -uniform float uNearFogStart; -uniform float uNearFogLength; +// fog config +uniform float uFarFogStart; +uniform float uFarFogLength; +uniform float uFarFogMin; +uniform float uFarFogRange; +uniform float uFarFogDensity; + +// height fog config +uniform float uHeightFogStart; +uniform float uHeightFogLength; +uniform float uHeightFogMin; +uniform float uHeightFogRange; +uniform float uHeightFogDensity; -/* ========MARCO DEFINED BY RUNTIME CODE GEN========= +uniform bool uHeightFogEnabled; +uniform int uHeightFogFalloffType; +uniform bool uHeightBasedOnCamera; +uniform float uHeightFogBaseHeight; +uniform bool uHeightFogAppliesUp; +uniform bool uHeightFogAppliesDown; +uniform bool uUseSphericalFog; +uniform int uHeightFogMixingMode; +uniform float uCameraBlockYPos; -float farFogStart; -float farFogLength; -float farFogMin; -float farFogRange; -float farFogDensity; - -float heightFogStart; -float heightFogLength; -float heightFogMin; -float heightFogRange; -float heightFogDensity; -*/ - -// method definitions -// ==== The below 5 methods will be run-time generated. ==== -float getNearFogThickness(float dist); -float getFarFogThickness(float dist); -float getHeightFogThickness(float dist); -float calculateFarFogDepth(float horizontal, float dist, float nearFogStart); -float calculateHeightFogDepth(float vertical, float realY); -float mixFogThickness(float near, float far, float height); -// ========================================================= const vec3 MAGIC = vec3(0.06711056, 0.00583715, 52.9829189); -float InterleavedGradientNoise(const in vec2 pixel) { - float x = dot(pixel, MAGIC.xy); - return fract(MAGIC.z * fract(x)); -} -vec3 calcViewPosition(float fragmentDepth) { - vec4 ndc = vec4(TexCoord.xy, fragmentDepth, 1.0); - ndc.xyz = ndc.xyz * 2.0 - 1.0; - vec4 eyeCoord = uInvMvmProj * ndc; - return eyeCoord.xyz / eyeCoord.w; -} +//====================// +// method definitions // +//====================// + +float InterleavedGradientNoise(const in vec2 pixel); +vec3 calcViewPosition(float fragmentDepth); + +float getFarFogThickness(float dist); +float getHeightFogThickness(float dist); +float calculateHeightFogDepth(float worldYPos); +float mixFogThickness(float far, float height); + + + +//======// +// main // +//======// /** * Fragment shader for fog. - * This should be passed last so it applies above other affects like AO - * - * version: 2023-6-21 + * This should be run last so it applies above other affects like Ambient Occlusioning */ -void main() +void main() { - float vertexYPos = 100.0f; float fragmentDepth = texture(uDepthMap, TexCoord).r; fragColor = vec4(uFogColor.rgb, 0.0); // a fragment depth of "1" means the fragment wasn't drawn to, // we only want to apply Fog to LODs, not to the sky outside the LODs - if (fragmentDepth < 1.0) { - if (uFullFogMode == 0) { + if (fragmentDepth < 1.0) + { + int fogMode = uFullFogMode; + if (fogMode == 0) + { // render fog based on distance from the camera vec3 vertexWorldPos = calcViewPosition(fragmentDepth); - float horizontalDist = length(vertexWorldPos.xz) * uFogScale; - float heightDist = calculateHeightFogDepth(vertexWorldPos.y, vertexYPos) * uFogVerticalScale; - float farDist = calculateFarFogDepth(horizontalDist, length(vertexWorldPos.xyz) * uFogScale, uNearFogStart); + float horizontalWorldDistance = length(vertexWorldPos.xz) * uFogScale; + float worldDistance = length(vertexWorldPos.xyz) * uFogScale; + float activeDistance = uUseSphericalFog ? worldDistance : horizontalWorldDistance; - float nearFogThickness = getNearFogThickness(horizontalDist); - float farFogThickness = getFarFogThickness(farDist); - float heightFogThickness = getHeightFogThickness(heightDist); - float mixedFogThickness = mixFogThickness(nearFogThickness, farFogThickness, heightFogThickness); + + // far fog + float farFogThickness = getFarFogThickness(activeDistance); + + // height fog + float heightFogDepth = calculateHeightFogDepth(vertexWorldPos.y); + float heightFogThickness = getHeightFogThickness(heightFogDepth); + + // combined fog + float mixedFogThickness = mixFogThickness(farFogThickness, heightFogThickness); fragColor.a = clamp(mixedFogThickness, 0.0, 1.0); - float dither = InterleavedGradientNoise(gl_FragCoord.xy) - 0.5; - fragColor.a += dither / 255.0; + // test + //fragColor.a = heightFogThickness; + + // dither fog (to smooth out aliasing) + //float dither = InterleavedGradientNoise(gl_FragCoord.xy) - 0.5; + //fragColor.a += dither / 255.0; } - else if (uFullFogMode == 1) { + else if (fogMode == 1) + { // render everything with the fog color fragColor.a = 1.0; } - else { + else + { // test code. // this can be fired by manually changing the fullFogMode to a (normally) @@ -108,22 +129,174 @@ void main() } } -// Are these still needed? -float linearFog(float x, float fogStart, float fogLength, float fogMin, float fogRange) { - x = clamp((x-fogStart)/fogLength, 0.0, 1.0); - return fogMin + fogRange * x; + +// +// methods // +// + +float InterleavedGradientNoise(const in vec2 pixel) +{ + float x = dot(pixel, MAGIC.xy); + return fract(MAGIC.z * fract(x)); +} + +vec3 calcViewPosition(float fragmentDepth) +{ + vec4 ndc = vec4(TexCoord.xy, fragmentDepth, 1.0); + ndc.xyz = ndc.xyz * 2.0 - 1.0; + + vec4 eyeCoord = uInvMvmProj * ndc; + return eyeCoord.xyz / eyeCoord.w; +} + + + +float linearFog(float worldDist, float fogStart, float fogLength, float fogMin, float fogRange) +{ + worldDist = (worldDist - fogStart) / fogLength; + worldDist = clamp(worldDist, 0.0, 1.0); + return fogMin + fogRange * worldDist; } float exponentialFog(float x, float fogStart, float fogLength, - float fogMin, float fogRange, float fogDensity) +float fogMin, float fogRange, float fogDensity) { x = max((x-fogStart)/fogLength, 0.0) * fogDensity; return fogMin + fogRange - fogRange/exp(x); } float exponentialSquaredFog(float x, float fogStart, float fogLength, - float fogMin, float fogRange, float fogDensity) +float fogMin, float fogRange, float fogDensity) { x = max((x-fogStart)/fogLength, 0.0) * fogDensity; return fogMin + fogRange - fogRange/exp(x*x); } + + + +// +// generated methods // +// + +float getFarFogThickness(float dist) +{ + if (uFogFalloffType == 0) // LINEAR + { + return linearFog(dist, uFarFogStart, uFarFogLength, uFarFogMin, uFarFogRange); + } + else if (uFogFalloffType == 1) // EXPONENTIAL + { + return exponentialFog(dist, uFarFogStart, uFarFogLength, uFarFogMin, uFarFogRange, uFarFogDensity); + } + else // EXPONENTIAL_SQUARED + { + return exponentialSquaredFog(dist, uFarFogStart, uFarFogLength, uFarFogMin, uFarFogRange, uFarFogDensity); + } +} + +float getHeightFogThickness(float dist) +{ + if (!uHeightFogEnabled) + { + return 0.0; + } + + if (uHeightFogFalloffType == 0) // LINEAR + { + return linearFog(dist, uHeightFogStart, uHeightFogLength, uHeightFogMin, uHeightFogRange); + } + else if (uHeightFogFalloffType == 1) // EXPONENTIAL + { + return exponentialFog(dist, uHeightFogStart, uHeightFogLength, uHeightFogMin, uHeightFogRange, uHeightFogDensity); + } + else // EXPONENTIAL_SQUARED + { + return exponentialSquaredFog(dist, uHeightFogStart, uHeightFogLength, uHeightFogMin, uHeightFogRange, uHeightFogDensity); + } +} + +/** 1 = full fog, 0 = no fog */ +float calculateHeightFogDepth(float worldYPos) +{ + // worldYPos -65 - 384 + + + //worldYPos = worldYPos * -1; // negative, fog below height; positive, fog above height + //return worldYPos * uFogVerticalScale; // "* uFogVerticalScale" is done to convert world position to a percent of the world height; + + if (!uHeightFogEnabled) + { + // ignore the height + return 0.0; + } + + + if (!uHeightBasedOnCamera) + { + worldYPos -= (uHeightFogBaseHeight - uCameraBlockYPos); + } + + + if (uHeightFogAppliesDown && uHeightFogAppliesUp) + { + // TODO this aint right + return abs(worldYPos) * uFogVerticalScale; + } + else if (uHeightFogAppliesDown) + { + // apploy fog below given height + return -worldYPos * uFogVerticalScale; + } + else if (uHeightFogAppliesUp) + { + // apply fog above given height + return worldYPos * uFogVerticalScale; + } + else + { + // shouldn't happen, + return 0; + } + +} + +float mixFogThickness(float far, float height) +{ + switch (uHeightFogMixingMode) + { + case 0: // BASIC + case 1: // IGNORE_HEIGHT + return far; + + case 2: // MAX + return max(far, height); + + case 3: // ADDITION + return (far + height); + + case 4: // MULTIPLY + return far * height; + + case 5: // INVERSE_MULTIPLY + return (1.0 - (1.0-far)*(1.0-height)); + + case 6: // LIMITED_ADDITION + return (far + max(far, height)); + + case 7: // MULTIPLY_ADDITION + return (far + far*height); + + case 8: // INVERSE_MULTIPLY_ADDITION + return (far + 1.0 - (1.0-far)*(1.0-height)); + + case 9: // AVERAGE + return (far*0.5 + height*0.5); + } + + // shouldn't happen, but default to BASIC / IGNORE_HEIGHT + // if an invalid option is selected + return far; +} + + + diff --git a/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java b/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java index 334670bdc..df68445eb 100644 --- a/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java +++ b/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java @@ -39,9 +39,10 @@ public class LevelWrapperTest implements IDhApiLevelWrapper @Override public IDhApiDimensionTypeWrapper getDimensionType() { return null; } - @Override public String getDimensionName() { return null; } + @Override + public String getDhIdentifier() { return null; } @Override public EDhApiLevelType getLevelType() { return null; }