From ed6a4573ebd619951cea4e75a2893a1a6a6ebba6 Mon Sep 17 00:00:00 2001 From: Steveplays28 Date: Tue, 22 Aug 2023 11:36:46 +0200 Subject: [PATCH 01/19] feat: Update rendering block ignores Barrier blocks, structure void blocks, light blocks, and air blocks now share 2 `HashMap`s that define blocks that should be ignored by the LOD builder. --- .../FullDataToRenderDataTransformer.java | 12 ++++++++---- .../core/wrapperInterfaces/IWrapperFactory.java | 2 ++ .../wrapperInterfaces/block/IBlockStateWrapper.java | 2 ++ 3 files changed, 12 insertions(+), 4 deletions(-) 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 d344048c1..4381a9499 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 @@ -18,11 +18,13 @@ 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.RenderDataPointUtil; +import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; +import java.util.HashMap; + /** * Handles converting {@link ChunkSizedFullDataAccessor}, {@link IIncompleteFullDataSource}, * and {@link IFullDataSource}'s to {@link ColumnRenderSource}. @@ -30,7 +32,7 @@ import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; public class FullDataToRenderDataTransformer { private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class); - private static final IBlockStateWrapper AIR = WRAPPER_FACTORY.getAirBlockStateWrapper(); + private static final HashMap RENDERER_IGNORED_BLOCKS = WRAPPER_FACTORY.getRendererIgnoredBlocks(); @@ -305,9 +307,11 @@ public class FullDataToRenderDataTransformer int light = FullDataPointUtil.getLight(fullData); IBiomeWrapper biome = fullDataMapping.getBiomeWrapper(id); IBlockStateWrapper block = fullDataMapping.getBlockStateWrapper(id); - if (block.equals(AIR)) + + if (RENDERER_IGNORED_BLOCKS.containsValue(block)) { - // we don't render air + // Don't render a block that's marked as ignored by the renderer + // This map should include air (if it doesn't, the map either got refactored or something is wrong) continue; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java index cf9b88c49..5180dbb5c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java @@ -27,6 +27,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.Abstrac import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; import java.io.IOException; +import java.util.HashMap; /** * This handles creating abstract wrapper objects. @@ -40,6 +41,7 @@ public interface IWrapperFactory extends IBindable IBiomeWrapper deserializeBiomeWrapper(String str) throws IOException; IBlockStateWrapper deserializeBlockStateWrapper(String str) throws IOException; IBlockStateWrapper getAirBlockStateWrapper(); + HashMap getRendererIgnoredBlocks(); /** * Specifically designed to be used with the API. diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java index e3ba0e4c8..d0b8444a7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java @@ -2,6 +2,8 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.block; import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper; +import java.util.List; + /** A Minecraft version independent way of handling Blocks. */ public interface IBlockStateWrapper extends IDhApiBlockStateWrapper { From f6ee8048eb49a8b0e2ac702bb3ba0f60638bd6aa Mon Sep 17 00:00:00 2001 From: James Seibel Date: Wed, 23 Aug 2023 21:23:58 -0500 Subject: [PATCH 02/19] Have getRendererIgnoredBlocks()'s require a level and return a hashset --- .../transformers/FullDataToRenderDataTransformer.java | 9 ++++----- .../core/wrapperInterfaces/IWrapperFactory.java | 9 +++++++-- .../core/wrapperInterfaces/block/IBlockStateWrapper.java | 3 +-- 3 files changed, 12 insertions(+), 9 deletions(-) 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 4381a9499..12e99b60b 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 @@ -23,7 +23,7 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; -import java.util.HashMap; +import java.util.HashSet; /** * Handles converting {@link ChunkSizedFullDataAccessor}, {@link IIncompleteFullDataSource}, @@ -32,7 +32,6 @@ import java.util.HashMap; public class FullDataToRenderDataTransformer { private static final IWrapperFactory WRAPPER_FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class); - private static final HashMap RENDERER_IGNORED_BLOCKS = WRAPPER_FACTORY.getRendererIgnoredBlocks(); @@ -292,6 +291,7 @@ public class FullDataToRenderDataTransformer boolean colorBelowWithAvoidedBlocks = Config.Client.Advanced.Graphics.Quality.tintWithAvoidedBlocks.get(); FullDataPointIdMap fullDataMapping = data.getMapping(); + HashSet blockStatesToIgnore = WRAPPER_FACTORY.getRendererIgnoredBlocks(level.getLevelWrapper()); boolean isVoid = true; int colorToApplyToNextBlock = -1; @@ -308,10 +308,9 @@ public class FullDataToRenderDataTransformer IBiomeWrapper biome = fullDataMapping.getBiomeWrapper(id); IBlockStateWrapper block = fullDataMapping.getBlockStateWrapper(id); - if (RENDERER_IGNORED_BLOCKS.containsValue(block)) + if (blockStatesToIgnore.contains(block)) { - // Don't render a block that's marked as ignored by the renderer - // This map should include air (if it doesn't, the map either got refactored or something is wrong) + // Don't render: air, barriers, light blocks, etc. continue; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java index 5180dbb5c..4997c3c1b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java @@ -23,11 +23,12 @@ import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.worldGeneration.AbstractBatchGenerationEnvironmentWrapper; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; import java.io.IOException; -import java.util.HashMap; +import java.util.HashSet; /** * This handles creating abstract wrapper objects. @@ -41,7 +42,11 @@ public interface IWrapperFactory extends IBindable IBiomeWrapper deserializeBiomeWrapper(String str) throws IOException; IBlockStateWrapper deserializeBlockStateWrapper(String str) throws IOException; IBlockStateWrapper getAirBlockStateWrapper(); - HashMap getRendererIgnoredBlocks(); + /** + * Returns the set of {@link IBlockStateWrapper}'s that shouldn't be rendered.
+ * Generally this contains blocks like: air, barriers, light blocks, etc. + */ + HashSet getRendererIgnoredBlocks(ILevelWrapper levelWrapper); /** * Specifically designed to be used with the API. diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java index d0b8444a7..14760a02f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java @@ -2,11 +2,10 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.block; import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper; -import java.util.List; - /** A Minecraft version independent way of handling Blocks. */ public interface IBlockStateWrapper extends IDhApiBlockStateWrapper { + /** will only work if a level is currently loaded */ String serialize(); /** From 81992f6a87307d9869d8bef5f926b4c92e4b7295 Mon Sep 17 00:00:00 2001 From: coolGi Date: Thu, 24 Aug 2023 20:28:25 +0930 Subject: [PATCH 03/19] Formatted some resource files --- core/src/main/resources/log4jConfig.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/log4jConfig.xml b/core/src/main/resources/log4jConfig.xml index d5fa620c1..f1304d0df 100644 --- a/core/src/main/resources/log4jConfig.xml +++ b/core/src/main/resources/log4jConfig.xml @@ -19,7 +19,7 @@ - + %d %p %c{1.} [%t] %m%n From 971caca9c922a9f65c8deae9ef6e1badc2108b16 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Thu, 24 Aug 2023 07:20:49 -0500 Subject: [PATCH 04/19] Improve config render cache invalidation logic and add missing config listeners --- .../distanthorizons/core/config/Config.java | 3 ++ .../RenderCacheConfigEventHandler.java | 40 +------------------ 2 files changed, 5 insertions(+), 38 deletions(-) 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 a59355d57..2bbff4aa4 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 @@ -200,6 +200,7 @@ public class Config + ETransparency.DISABLED + ": LODs will be opaque. \n" + "") .setPerformance(EConfigEntryPerformance.MEDIUM) + .addListener(RenderCacheConfigEventHandler.INSTANCE) .build(); public static ConfigEntry blocksToIgnore = new ConfigEntry.Builder() @@ -531,6 +532,7 @@ public class Config + "0 = black \n" + "1 = normal \n" + "2 = near white") + .addListener(RenderCacheConfigEventHandler.INSTANCE) .build(); public static ConfigEntry saturationMultiplier = new ConfigEntry.Builder() // TODO: Make this a float (the ClassicConfigGUI doesnt support floats) @@ -541,6 +543,7 @@ public class Config + "0 = black and white \n" + "1 = normal \n" + "2 = very saturated") + .addListener(RenderCacheConfigEventHandler.INSTANCE) .build(); public static ConfigEntry enableCaveCulling = new ConfigEntry.Builder() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/RenderCacheConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/RenderCacheConfigEventHandler.java index 4f69ce2ed..4ad052173 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/RenderCacheConfigEventHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/RenderCacheConfigEventHandler.java @@ -20,13 +20,8 @@ public class RenderCacheConfigEventHandler implements IConfigListener { public static RenderCacheConfigEventHandler INSTANCE = new RenderCacheConfigEventHandler(); - // previous values used to check if a watched setting was actually modified - private EVerticalQuality previousVerticalQualitySetting = null; - private EMaxHorizontalResolution previousHorizontalResolution = null; - private ELodShading lodShading = null; - /** how long to wait in milliseconds before applying the config changes */ - private static final long TIMEOUT_IN_MS = 400L; + private static final long TIMEOUT_IN_MS = 4_000L; private Timer cacheClearingTimer; @@ -38,38 +33,7 @@ public class RenderCacheConfigEventHandler implements IConfigListener @Override public void onConfigValueSet() { - // confirm a setting was actually changed - boolean refreshRenderData = false; - - - EVerticalQuality newVerticalQuality = Config.Client.Advanced.Graphics.Quality.verticalQuality.get(); - if (this.previousVerticalQualitySetting != newVerticalQuality) - { - this.previousVerticalQualitySetting = newVerticalQuality; - refreshRenderData = true; - } - - EMaxHorizontalResolution newHorizontalResolution = Config.Client.Advanced.Graphics.Quality.maxHorizontalResolution.get(); - if (this.previousHorizontalResolution != newHorizontalResolution) - { - this.previousHorizontalResolution = newHorizontalResolution; - refreshRenderData = true; - } - - ELodShading newLodShading = Config.Client.Advanced.Graphics.AdvancedGraphics.lodShading.get(); - if (this.lodShading != newLodShading) - { - this.lodShading = newLodShading; - refreshRenderData = true; - } - - - - if (refreshRenderData) - { - this.refreshRenderDataAfterTimeout(); - } - + this.refreshRenderDataAfterTimeout(); } @Override From 3361ba2fabd2c0d86d78b936ab7b5907bf7a0003 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Thu, 24 Aug 2023 07:41:08 -0500 Subject: [PATCH 05/19] Fix config presets rewriting the config file before it loads --- .../com/seibel/distanthorizons/core/config/Config.java | 10 +++++++++- .../presets/AbstractPresetConfigEventHandler.java | 7 +++++++ .../core/config/file/ConfigFileHandling.java | 7 +++++-- 3 files changed, 21 insertions(+), 3 deletions(-) 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 2bbff4aa4..ff23e1864 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 @@ -31,7 +31,6 @@ import com.seibel.distanthorizons.core.config.eventHandlers.UnsafeValuesConfigLi import com.seibel.distanthorizons.core.config.eventHandlers.WorldCurvatureConfigEventHandler; import com.seibel.distanthorizons.core.config.eventHandlers.presets.ThreadPresetConfigEventHandler; import com.seibel.distanthorizons.core.config.eventHandlers.presets.RenderQualityPresetConfigEventHandler; -import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener; import com.seibel.distanthorizons.core.config.types.*; import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryAppearance; import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryPerformance; @@ -61,6 +60,15 @@ public class Config public static ConfigCategory client = new ConfigCategory.Builder().set(Client.class).build(); + /** + * False if the config hasn't been loaded in from file yet. + * While in this state the config shouldn't be modified since it may cause file corruption.

+ * + * True if the config has been loaded and is ready to use. + */ + public static boolean loaded = false; + + public static class Client { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java index 343af2b65..1aae2f6d8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java @@ -1,5 +1,6 @@ package com.seibel.distanthorizons.core.config.eventHandlers.presets; +import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.config.ConfigEntryWithPresetOptions; import com.seibel.distanthorizons.core.config.listeners.IConfigListener; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; @@ -62,6 +63,12 @@ public abstract class AbstractPresetConfigEventHandler Date: Thu, 24 Aug 2023 22:30:05 +0930 Subject: [PATCH 06/19] Removed running button pressed process by default --- .../com/seibel/distanthorizons/core/config/Config.java | 8 ++++---- .../distanthorizons/core/config/types/ConfigUIButton.java | 3 ++- 2 files changed, 6 insertions(+), 5 deletions(-) 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 2bbff4aa4..61c54dec6 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 @@ -1147,10 +1147,10 @@ public class Config .set(new HashMap()) .build(); - public static ConfigUIButton uiButtonTest = new ConfigUIButton(() -> { - System.setProperty("java.awt.headless", "false"); // Required to make it work - JOptionPane.showMessageDialog(null, "Button pressed!", "UITester dialog", JOptionPane.INFORMATION_MESSAGE); - }); + public static ConfigUIButton uiButtonTest = new ConfigUIButton(() -> { new Thread(() -> { + System.setProperty("java.awt.headless", "false"); // Required to make it work + JOptionPane.showMessageDialog(null, "Button pressed!", "UITester dialog", JOptionPane.INFORMATION_MESSAGE); + });}); public static ConfigCategory categoryTest = new ConfigCategory.Builder().set(CategoryTest.class).build(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigUIButton.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigUIButton.java index d96afea82..e60fbb680 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigUIButton.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigUIButton.java @@ -9,9 +9,10 @@ public class ConfigUIButton extends AbstractConfigType super(EConfigEntryAppearance.ONLY_IN_GUI, runnable); } + /** Runs the action of the button. NOTE: Will run on the main thread (so can halt the main process if not offloaded to a different thread) */ public void runAction() { - new Thread(this.value).start(); + this.value.run(); } public static class Builder extends AbstractConfigType.Builder From d553965f280bfe1aa1d69f734b0d26a2fcbb1f13 Mon Sep 17 00:00:00 2001 From: coolGi Date: Thu, 24 Aug 2023 22:56:03 +0930 Subject: [PATCH 07/19] Refactored and fixed several config related things (javadocs, and warnings) --- .../core/config/AppliedConfigState.java | 2 +- .../core/config/ConfigBase.java | 46 ++++++++++--------- .../core/config/NumberUtil.java | 12 ++--- .../config/file/ConfigTypeConverters.java | 4 +- .../core/config/types/AbstractConfigType.java | 5 +- .../core/config/types/ConfigCategory.java | 6 +-- .../core/config/types/ConfigLinkedEntry.java | 8 ++-- 7 files changed, 46 insertions(+), 37 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/AppliedConfigState.java b/core/src/main/java/com/seibel/distanthorizons/core/config/AppliedConfigState.java index 8c8a6be15..badbb60d0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/AppliedConfigState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/AppliedConfigState.java @@ -2,7 +2,7 @@ package com.seibel.distanthorizons.core.config; import com.seibel.distanthorizons.core.config.types.ConfigEntry; -// TODO: Make this intergrate with the config system +// TODO: Make this integrate with the config system public class AppliedConfigState { final ConfigEntry entry; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/ConfigBase.java b/core/src/main/java/com/seibel/distanthorizons/core/config/ConfigBase.java index 0df37a368..4b6df2e75 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/ConfigBase.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/ConfigBase.java @@ -22,7 +22,7 @@ import java.util.*; // Init the config after singletons have been blinded public class ConfigBase { - /** Our own config instance, don't modify */ + /** Our own config instance, don't modify unless you are the DH mod */ public static ConfigBase INSTANCE; public ConfigFileHandling configFileINSTANCE; @@ -31,24 +31,27 @@ public class ConfigBase public final String modName; public final int configVersion; + + /** - * What the config works with - * - * Enum - * Boolean - * Byte - * Integer - * Double - * Short - * Long - * Float - * String - * - * // Below, "T" should be a value from above - * List - * ArrayList - * Map - * HashMap + * What the config works with + *
+ *
{@link Enum} + *
{@link Boolean} + *
{@link Byte} + *
{@link Integer} + *
{@link Double} + *
{@link Short} + *
{@link Long} + *
{@link Float} + *
{@link String} + *
+ *
// Below, "T" should be a value from above + *
// Note: This is not checked, so we trust that you are doing the right thing + *
List + *
ArrayList + *
Map + *
HashMap */ public static final List> acceptableInputs = new ArrayList>() {{ @@ -102,7 +105,7 @@ public class ConfigBase } catch (IllegalAccessException exception) { - exception.printStackTrace(); + LOGGER.warn(exception); } AbstractConfigType entry = entries.get(entries.size() - 1); @@ -151,10 +154,11 @@ public class ConfigBase * @param checkEnums Checks if all the lang for the enum's exist */ // This is just to re-format the lang or check if there is something in the lang that is missing + @SuppressWarnings("unchecked") public String generateLang(boolean onlyShowNew, boolean checkEnums) { ILangWrapper langWrapper = SingletonInjector.INSTANCE.get(ILangWrapper.class); - List> enumList = new ArrayList<>(); + List>> enumList = new ArrayList<>(); String generatedLang = ""; @@ -168,7 +172,7 @@ public class ConfigBase if (checkEnums && entry.getType().isEnum() && !enumList.contains(entry.getType())) { // Put it in an enum list to work with at the end - enumList.add((Class) entry.getType()); + enumList.add((Class>) entry.getType()); } if (!onlyShowNew || langWrapper.langExists(entryPrefix)) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/NumberUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/config/NumberUtil.java index b53e59f89..b09954a0b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/NumberUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/NumberUtil.java @@ -13,7 +13,7 @@ import java.util.Map; public class NumberUtil { // Is there no better way of doing this? - public static Map minValues = new HashMap() + public static Map, Number> minValues = new HashMap, Number>() {{ put(Byte.class, Byte.MIN_VALUE); put(Short.class, Short.MIN_VALUE); @@ -22,7 +22,7 @@ public class NumberUtil put(Double.class, Double.MIN_VALUE); put(Float.class, Float.MIN_VALUE); }}; - public static Map maxValues = new HashMap() + public static Map, Number> maxValues = new HashMap, Number>() {{ put(Byte.class, Byte.MAX_VALUE); put(Short.class, Short.MAX_VALUE); @@ -32,11 +32,11 @@ public class NumberUtil put(Float.class, Float.MAX_VALUE); }}; - public static Number getMinimum(Class c) + public static Number getMinimum(Class c) { return minValues.get(c); } - public static Number getMaximum(Class c) + public static Number getMaximum(Class c) { return maxValues.get(c); } @@ -46,7 +46,7 @@ public class NumberUtil { if (a.getClass() != b.getClass()) return false; - Class typeClass = a.getClass(); + Class typeClass = a.getClass(); if (typeClass == Byte.class) return a.byteValue() > b.byteValue(); if (typeClass == Short.class) return a.shortValue() > b.shortValue(); @@ -62,7 +62,7 @@ public class NumberUtil { if (a.getClass() != b.getClass()) return false; - Class typeClass = a.getClass(); + Class typeClass = a.getClass(); if (typeClass == Byte.class) return a.byteValue() < b.byteValue(); if (typeClass == Short.class) return a.shortValue() < b.shortValue(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigTypeConverters.java b/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigTypeConverters.java index 489c9d191..32b6e5d1e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigTypeConverters.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigTypeConverters.java @@ -116,9 +116,11 @@ public class ConfigTypeConverters Map mapObject = (Map) item; Config jsonObject = Config.inMemory(); + Object[] keyArray = mapObject.keySet().toArray(); + for (int i = 0; i < mapObject.size(); i++) { - jsonObject.add(mapObject.keySet().toArray()[i].toString(), mapObject.get(mapObject.keySet().toArray()[i])); + jsonObject.add(keyArray[i].toString(), mapObject.get(keyArray[i])); } return JsonFormat.minimalInstance().createWriter().writeToString(jsonObject); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/AbstractConfigType.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/AbstractConfigType.java index 218d4bcc4..40a709245 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/AbstractConfigType.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/AbstractConfigType.java @@ -8,8 +8,9 @@ import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryAppearance * * @author coolGi */ +// Note for devs: The "S" is the class that is extending this public abstract class AbstractConfigType -{ // The S is the class that is extending this +{ public String category = ""; // This should only be set once in the init public String name; // This should only be set once in the init protected T value; @@ -74,11 +75,13 @@ public abstract class AbstractConfigType // Put this into your own builder + @SuppressWarnings("unchecked") public S setAppearance(EConfigEntryAppearance newAppearance) { this.tmpAppearance = newAppearance; return (S) this; } + @SuppressWarnings("unchecked") public S set(T newValue) { this.tmpValue = newValue; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigCategory.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigCategory.java index 07d5dea9a..0e25ff232 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigCategory.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigCategory.java @@ -8,12 +8,12 @@ import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryAppearance * * @author coolGi */ -public class ConfigCategory extends AbstractConfigType +public class ConfigCategory extends AbstractConfigType, ConfigCategory> { /** This should not be set by anything other than the config system itself */ public String destination; // Where the category goes to - private ConfigCategory(EConfigEntryAppearance appearance, Class value, String destination) + private ConfigCategory(EConfigEntryAppearance appearance, Class value, String destination) { super(appearance, value); this.destination = destination; @@ -32,7 +32,7 @@ public class ConfigCategory extends AbstractConfigType return value; } - public static class Builder extends AbstractConfigType.Builder + public static class Builder extends AbstractConfigType.Builder, Builder> { private String tmpDestination = null; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigLinkedEntry.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigLinkedEntry.java index b4c8f275b..f7fbdcb47 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigLinkedEntry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigLinkedEntry.java @@ -8,9 +8,9 @@ import com.seibel.distanthorizons.core.config.types.enums.EConfigEntryAppearance * * @author coolGi */ -public class ConfigLinkedEntry extends AbstractConfigType +public class ConfigLinkedEntry extends AbstractConfigType, ConfigLinkedEntry> { - public ConfigLinkedEntry(AbstractConfigType value) + public ConfigLinkedEntry(AbstractConfigType value) { super(EConfigEntryAppearance.ONLY_IN_GUI, value); } @@ -21,10 +21,10 @@ public class ConfigLinkedEntry extends AbstractConfigType newValue) { } - public static class Builder extends AbstractConfigType.Builder + public static class Builder extends AbstractConfigType.Builder, Builder> { /** Appearance shouldn't be changed */ @Override From f1b31a4cfe40bec682f4a71203a6c41428e744dd Mon Sep 17 00:00:00 2001 From: coolGi Date: Thu, 24 Aug 2023 22:56:19 +0930 Subject: [PATCH 08/19] Fixed config system only working with dh --- .../com/seibel/distanthorizons/core/config/Config.java | 8 -------- .../presets/AbstractPresetConfigEventHandler.java | 2 +- .../core/config/file/ConfigFileHandling.java | 5 ----- 3 files changed, 1 insertion(+), 14 deletions(-) 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 761db1814..4b4696a56 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 @@ -60,14 +60,6 @@ public class Config public static ConfigCategory client = new ConfigCategory.Builder().set(Client.class).build(); - /** - * False if the config hasn't been loaded in from file yet. - * While in this state the config shouldn't be modified since it may cause file corruption.

- * - * True if the config has been loaded and is ready to use. - */ - public static boolean loaded = false; - public static class Client diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java index 1aae2f6d8..12131b0bd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java @@ -64,7 +64,7 @@ public abstract class AbstractPresetConfigEventHandler Date: Fri, 25 Aug 2023 00:10:34 +0930 Subject: [PATCH 09/19] Fixed javadocs reference, and updated core --- .../com/seibel/distanthorizons/core/jar/DarkModeDetector.java | 2 +- .../main/java/com/seibel/distanthorizons/core/jar/Platform.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/jar/DarkModeDetector.java b/core/src/main/java/com/seibel/distanthorizons/core/jar/DarkModeDetector.java index bb690e039..5bddb1512 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/jar/DarkModeDetector.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/jar/DarkModeDetector.java @@ -11,7 +11,7 @@ import java.util.regex.Pattern; * * @author HanSolo * @author IMS - * @author coolGi2007 + * @author coolGi */ public class DarkModeDetector { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/jar/Platform.java b/core/src/main/java/com/seibel/distanthorizons/core/jar/Platform.java index 141ad25e6..74820fe9d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/jar/Platform.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/jar/Platform.java @@ -1,7 +1,7 @@ package com.seibel.distanthorizons.core.jar; /** - * A simple OS getting util based off LWJGL's Platform at org.lwjgl.system.Platform.
+ * A simple OS getting util based off LWJGL's Platform at {@link org.lwjgl.system.Platform}.
* This version includes some extra utils that we need and removes stuff which we don't. * * @author coolGi From 2ab48e0a8b3dd001e36202962228eb4abbefcaae Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 24 Aug 2023 21:09:39 +0500 Subject: [PATCH 10/19] Decouple world generation parts from DhLevel's --- .../core/api/internal/ClientApi.java | 4 + .../core/api/internal/ServerApi.java | 7 +- .../core/api/internal/SharedApi.java | 11 ++ .../GeneratedFullDataFileHandler.java | 16 +- .../generation/IWorldGenerationQueue.java | 26 ++++ .../core/generation/WorldGenerationQueue.java | 9 +- .../core/level/DhClientServerLevel.java | 12 +- .../core/level/DhServerLevel.java | 10 +- .../core/level/IDhServerLevel.java | 8 +- .../core/level/IDhWorldGenLevel.java | 9 ++ .../core/level/ServerLevelModule.java | 130 +--------------- .../core/level/WorldGenModule.java | 145 ++++++++++++++++++ .../core/world/DhClientWorld.java | 8 +- .../core/world/IDhClientWorld.java | 2 + 14 files changed, 239 insertions(+), 158 deletions(-) create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/level/IDhWorldGenLevel.java create mode 100644 core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java 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 6b8d683eb..6934834b1 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 @@ -319,6 +319,10 @@ public class ClientApi if (clientWorld != null) { clientWorld.clientTick(); + + // Ignore local world gen, as it's managed by server ticking + if (!(clientWorld instanceof DhClientServerWorld)) + SharedApi.worldGenTick(clientWorld::doWorldGen); } profiler.pop(); } 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 455d8d68d..afc363e1e 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 @@ -78,12 +78,7 @@ public class ServerApi if (serverWorld != null) { serverWorld.serverTick(); - this.lastWorldGenTickDelta--; - if (this.lastWorldGenTickDelta <= 0) - { - serverWorld.doWorldGen(); - this.lastWorldGenTickDelta = 20; - } + SharedApi.worldGenTick(serverWorld::doWorldGen); } } catch (Exception e) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java index 5544c8811..8abbd0609 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/SharedApi.java @@ -12,6 +12,7 @@ import com.seibel.distanthorizons.core.world.*; public class SharedApi { private static AbstractDhWorld currentWorld; + private static int lastWorldGenTickDelta = 0; @@ -52,6 +53,16 @@ public class SharedApi } } + public static void worldGenTick(Runnable worldGenRunnable) + { + lastWorldGenTickDelta--; + if (lastWorldGenTickDelta <= 0) + { + worldGenRunnable.run(); + lastWorldGenTickDelta = 20; + } + } + public static AbstractDhWorld getAbstractDhWorld() { return currentWorld; } /** returns null if the {@link SharedApi#currentWorld} isn't a {@link DhClientServerWorld} */ public static DhClientServerWorld getDhClientServerWorld() { return (currentWorld != null && DhClientServerWorld.class.isInstance(currentWorld)) ? (DhClientServerWorld) currentWorld : null; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java index 99e45dd5f..75b48d9cb 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataFileHandler.java @@ -5,11 +5,11 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.CompleteFull import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.interfaces.IIncompleteFullDataSource; import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; -import com.seibel.distanthorizons.core.generation.WorldGenerationQueue; +import com.seibel.distanthorizons.core.generation.IWorldGenerationQueue; import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; import com.seibel.distanthorizons.core.level.DhLevel; -import com.seibel.distanthorizons.core.level.IDhServerLevel; +import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhLodPos; import com.seibel.distanthorizons.core.pos.DhSectionPos; @@ -28,14 +28,14 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private final AtomicReference worldGenQueueRef = new AtomicReference<>(null); + private final AtomicReference worldGenQueueRef = new AtomicReference<>(null); private final ArrayList onWorldGenTaskCompleteListeners = new ArrayList<>(); // Use to hold onto incomplete data sources that are waiting for generation, so that they don't get GC'd before they are generated private final ConcurrentHashMap incompleteDataSources = new ConcurrentHashMap<>(); - public GeneratedFullDataFileHandler(IDhServerLevel level, AbstractSaveStructure saveStructure) { super(level, saveStructure); } + public GeneratedFullDataFileHandler(IDhLevel level, AbstractSaveStructure saveStructure) { super(level, saveStructure); } @@ -56,7 +56,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler //==================// /** Assumes there isn't a pre-existing queue. */ - public void setGenerationQueue(WorldGenerationQueue newWorldGenQueue) + public void setGenerationQueue(IWorldGenerationQueue newWorldGenQueue) { boolean oldQueueExists = this.worldGenQueueRef.compareAndSet(null, newWorldGenQueue); LodUtil.assertTrue(oldQueueExists, "previous world gen queue is still here!"); @@ -115,13 +115,13 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler @Nullable private CompletableFuture tryStartGenTask(FullDataMetaFile file, IIncompleteFullDataSource dataSource) { - WorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get(); + IWorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get(); // breaks down the missing positions into the desired detail level that the gen queue could accept if (worldGenQueue != null && !file.genQueueChecked) { DhSectionPos pos = file.pos; file.genQueueChecked = true; - byte maxSectDataDetailLevel = worldGenQueue.largestDataDetail; + byte maxSectDataDetailLevel = worldGenQueue.largestDataDetail(); byte targetDataDetailLevel = dataSource.getDataDetailLevel(); if (targetDataDetailLevel > maxSectDataDetailLevel) @@ -222,7 +222,7 @@ public class GeneratedFullDataFileHandler extends FullDataFileHandler if (source instanceof IIncompleteFullDataSource && !file.genQueueChecked) { - WorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get(); + IWorldGenerationQueue worldGenQueue = this.worldGenQueueRef.get(); if (worldGenQueue != null) { CompletableFuture future = this.updateFromExistingDataSources(file, (IIncompleteFullDataSource) source); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java new file mode 100644 index 000000000..e1aa2e376 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java @@ -0,0 +1,26 @@ +package com.seibel.distanthorizons.core.generation; + +import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; +import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; +import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import com.seibel.distanthorizons.core.pos.DhLodPos; +import com.seibel.distanthorizons.core.pos.DhSectionPos; + +import java.io.Closeable; +import java.util.concurrent.CompletableFuture; + +public interface IWorldGenerationQueue extends Closeable +{ + byte largestDataDetail(); + + CompletableFuture submitGenTask(DhLodPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker); + void cancelGenTasks(Iterable positions); + + void runCurrentGenTasksUntilBusy(DhBlockPos2D targetPos); + + int getWaitingTaskCount(); + int getInProgressTaskCount(); + + CompletableFuture startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning); + void close(); +} 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 20338b160..0a3c8477f 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 @@ -31,7 +31,7 @@ import java.util.*; import java.util.concurrent.*; import java.util.function.Consumer; -public class WorldGenerationQueue implements Closeable, IDebugRenderable +public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRenderable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); @@ -51,6 +51,8 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable /** largest numerical detail level allowed */ public final byte largestDataDetail; + @Override + public byte largestDataDetail() { return this.largestDataDetail; } /** lowest numerical detail level allowed */ public final byte smallestDataDetail; @@ -155,6 +157,11 @@ public class WorldGenerationQueue implements Closeable, IDebugRenderable //} } + @Override + public void cancelGenTasks(Iterable positions) + { + // TODO Should anything be here? + } //===============// // running tasks // 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 52e627541..acc6ce14a 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 @@ -76,26 +76,26 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS { serverside.worldGeneratorEnabledConfig.pollNewValue(); boolean shouldDoWorldGen = serverside.worldGeneratorEnabledConfig.get() && clientside.isRendering(); - boolean isWorldGenRunning = serverside.isWorldGenRunning(); + boolean isWorldGenRunning = serverside.worldGenModule.isWorldGenRunning(); if (shouldDoWorldGen && !isWorldGenRunning) { // start world gen - serverside.startWorldGen(); + serverside.worldGenModule.startWorldGen(serverside.dataFileHandler, new ServerLevelModule.WorldGenState(this)); } else if (!shouldDoWorldGen && isWorldGenRunning) { // stop world gen - serverside.stopWorldGen(); + serverside.worldGenModule.stopWorldGen(serverside.dataFileHandler); } - - if (serverside.isWorldGenRunning()) + + if (serverside.worldGenModule.isWorldGenRunning()) { ClientLevelModule.ClientRenderState renderState = clientside.ClientRenderStateRef.get(); if (renderState != null && renderState.quadtree != null) { serverside.dataFileHandler.removeGenRequestIf(p -> !renderState.quadtree.isSectionPosInBounds(p)); } - serverside.worldGenTick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); + serverside.worldGenModule.worldGenTick(new DhBlockPos2D(MC_CLIENT.getPlayerBlockPos())); } } 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 691193e95..907dac539 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 @@ -67,21 +67,21 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel public void doWorldGen() { boolean shouldDoWorldGen = true; //todo; - boolean isWorldGenRunning = serverside.isWorldGenRunning(); + boolean isWorldGenRunning = serverside.worldGenModule.isWorldGenRunning(); if (shouldDoWorldGen && !isWorldGenRunning) { // start world gen - serverside.startWorldGen(); + serverside.worldGenModule.startWorldGen(serverside.dataFileHandler, new ServerLevelModule.WorldGenState(this)); } else if (!shouldDoWorldGen && isWorldGenRunning) { // stop world gen - serverside.stopWorldGen(); + serverside.worldGenModule.stopWorldGen(serverside.dataFileHandler); } - if (serverside.isWorldGenRunning()) + if (serverside.worldGenModule.isWorldGenRunning()) { - serverside.worldGenTick(new DhBlockPos2D(0, 0)); // todo; + serverside.worldGenModule.worldGenTick(new DhBlockPos2D(0, 0)); // todo; } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhServerLevel.java index 8ef80a7e5..b6ed06b04 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhServerLevel.java @@ -3,11 +3,9 @@ package com.seibel.distanthorizons.core.level; import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataFileHandler; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; -public interface IDhServerLevel extends IDhLevel, GeneratedFullDataFileHandler.IOnWorldGenCompleteListener +public interface IDhServerLevel extends IDhWorldGenLevel { - void serverTick(); - void doWorldGen(); - - IServerLevelWrapper getServerLevelWrapper(); + void serverTick(); + IServerLevelWrapper getServerLevelWrapper(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/IDhWorldGenLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhWorldGenLevel.java new file mode 100644 index 000000000..a97faaadd --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/IDhWorldGenLevel.java @@ -0,0 +1,9 @@ +package com.seibel.distanthorizons.core.level; + +import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataFileHandler; + +public interface IDhWorldGenLevel extends IDhLevel, GeneratedFullDataFileHandler.IOnWorldGenCompleteListener +{ + void doWorldGen(); + +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java index 420a66dee..adc85dcca 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java @@ -8,15 +8,10 @@ import com.seibel.distanthorizons.core.file.structure.AbstractSaveStructure; import com.seibel.distanthorizons.core.generation.BatchGenerator; import com.seibel.distanthorizons.core.generation.WorldGenerationQueue; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.logging.f3.F3Screen; -import com.seibel.distanthorizons.core.pos.DhBlockPos2D; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.WorldGeneratorInjector; import org.apache.logging.log4j.Logger; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicReference; - public class ServerLevelModule { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); @@ -27,8 +22,7 @@ public class ServerLevelModule public final GeneratedFullDataFileHandler dataFileHandler; public final AppliedConfigState worldGeneratorEnabledConfig; - private final AtomicReference worldGenStateRef = new AtomicReference<>(); - private final F3Screen.DynamicMessage worldGenF3Message; + public final WorldGenModule worldGenModule; @@ -39,109 +33,17 @@ public class ServerLevelModule this.saveStructure = saveStructure; this.dataFileHandler = new GeneratedFullDataFileHandler(parent, saveStructure); this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration); - - this.worldGenF3Message = new F3Screen.DynamicMessage(() -> - { - WorldGenState worldGenState = this.worldGenStateRef.get(); - if (worldGenState != null) - { - int waiting = worldGenState.worldGenerationQueue.getWaitingTaskCount(); - int inProgress = worldGenState.worldGenerationQueue.getInProgressTaskCount(); - - return "World Gen Tasks: "+waiting+", (in progress: "+inProgress+")"; - } - else - { - return "World Gen Disabled"; - } - }); + this.worldGenModule = new WorldGenModule(this.dataFileHandler, this.parent); } - - - //==============// - // tick methods // - //==============// - - public void startWorldGen() - { - // create the new world generator - WorldGenState newWgs = new WorldGenState(parent); - if (!this.worldGenStateRef.compareAndSet(null, newWgs)) - { - LOGGER.warn("Failed to start world gen due to concurrency"); - newWgs.closeAsync(false); - } - dataFileHandler.addWorldGenCompleteListener(parent); - dataFileHandler.setGenerationQueue(newWgs.worldGenerationQueue); - } - - public void stopWorldGen() - { - WorldGenState worldGenState = this.worldGenStateRef.get(); - if (worldGenState == null) - { - LOGGER.warn("Attempted to stop world gen when it was not running"); - return; - } - - // shut down the world generator - while (!this.worldGenStateRef.compareAndSet(worldGenState, null)) - { - worldGenState = this.worldGenStateRef.get(); - if (worldGenState == null) - { - return; - } - } - dataFileHandler.clearGenerationQueue(); - worldGenState.closeAsync(true).join(); //TODO: Make it async. - dataFileHandler.removeWorldGenCompleteListener(parent); - } - - public boolean isWorldGenRunning() - { - return this.worldGenStateRef.get() != null; - } - - public void worldGenTick(DhBlockPos2D targetPosForGeneration) - { - WorldGenState worldGenState = this.worldGenStateRef.get(); - if (worldGenState != null) - { - // queue new world generation requests - worldGenState.tick(targetPosForGeneration); - } - } - - - //===============// // data handling // //===============// public void close() { // shutdown the world-gen - WorldGenState worldGenState = this.worldGenStateRef.get(); - if (worldGenState != null) - { - while (!this.worldGenStateRef.compareAndSet(worldGenState, null)) - { - worldGenState = this.worldGenStateRef.get(); - if (worldGenState == null) - { - break; - } - } - - if (worldGenState != null) - { - worldGenState.closeAsync(true).join(); //TODO: Make it async. - } - } - - this.dataFileHandler.close(); - this.worldGenF3Message.close(); + this.worldGenModule.close(); + dataFileHandler.close(); } @@ -150,9 +52,8 @@ public class ServerLevelModule // helper classes // //================// - private static class WorldGenState + public static class WorldGenState extends WorldGenModule.WorldGenState { - public final WorldGenerationQueue worldGenerationQueue; WorldGenState(IDhServerLevel level) { IDhApiWorldGenerator worldGenerator = WorldGeneratorInjector.INSTANCE.get(level.getLevelWrapper()); @@ -167,27 +68,6 @@ public class ServerLevelModule this.worldGenerationQueue = new WorldGenerationQueue(worldGenerator); } - CompletableFuture closeAsync(boolean doInterrupt) - { - return this.worldGenerationQueue.startClosing(true, doInterrupt) - .exceptionally(ex -> - { - LOGGER.error("Error closing generation queue", ex); - return null; - } - ).thenRun(this.worldGenerationQueue::close) - .exceptionally(ex -> - { - LOGGER.error("Error closing world gen", ex); - return null; - }); - } - - public void tick(DhBlockPos2D targetPosForGeneration) - { - worldGenerationQueue.runCurrentGenTasksUntilBusy(targetPosForGeneration); - } - } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java new file mode 100644 index 000000000..97b0c9232 --- /dev/null +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java @@ -0,0 +1,145 @@ +package com.seibel.distanthorizons.core.level; + +import com.seibel.distanthorizons.core.file.fullDatafile.GeneratedFullDataFileHandler; +import com.seibel.distanthorizons.core.generation.IWorldGenerationQueue; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; +import com.seibel.distanthorizons.core.pos.DhBlockPos2D; +import org.apache.logging.log4j.Logger; + +import java.io.Closeable; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.atomic.AtomicReference; + +public class WorldGenModule implements Closeable +{ + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + + private final GeneratedFullDataFileHandler dataFileHandler; + private final GeneratedFullDataFileHandler.IOnWorldGenCompleteListener onWorldGenCompleteListener; + + private final AtomicReference worldGenStateRef = new AtomicReference<>(); + private final F3Screen.DynamicMessage worldGenF3Message; + + public static abstract class WorldGenState + { + public IWorldGenerationQueue worldGenerationQueue; + + CompletableFuture closeAsync(boolean doInterrupt) + { + return this.worldGenerationQueue.startClosing(true, doInterrupt) + .exceptionally(ex -> + { + LOGGER.error("Error closing generation queue", ex); + return null; + } + ).thenRun(this.worldGenerationQueue::close) + .exceptionally(ex -> + { + LOGGER.error("Error closing world gen", ex); + return null; + }); + } + + public void tick(DhBlockPos2D targetPosForGeneration) + { + worldGenerationQueue.runCurrentGenTasksUntilBusy(targetPosForGeneration); + } + } + + public WorldGenModule(GeneratedFullDataFileHandler dataFileHandler, GeneratedFullDataFileHandler.IOnWorldGenCompleteListener onWorldGenCompleteListener) + { + this.dataFileHandler = dataFileHandler; + this.onWorldGenCompleteListener = onWorldGenCompleteListener; + this.worldGenF3Message = new F3Screen.DynamicMessage(() -> + { + WorldGenState worldGenState = this.worldGenStateRef.get(); + if (worldGenState != null) + { + int waiting = worldGenState.worldGenerationQueue.getWaitingTaskCount(); + int inProgress = worldGenState.worldGenerationQueue.getInProgressTaskCount(); + + return "World Gen Tasks: "+waiting+", (in progress: "+inProgress+")"; + } + else + { + return "World Gen Disabled"; + } + }); + + } + + public void startWorldGen(GeneratedFullDataFileHandler dataFileHandler, WorldGenState newWgs) + { + // create the new world generator + if (!this.worldGenStateRef.compareAndSet(null, newWgs)) + { + LOGGER.warn("Failed to start world gen due to concurrency"); + newWgs.closeAsync(false); + } + dataFileHandler.addWorldGenCompleteListener(onWorldGenCompleteListener); + dataFileHandler.setGenerationQueue(newWgs.worldGenerationQueue); + } + + public void stopWorldGen(GeneratedFullDataFileHandler dataFileHandler) + { + WorldGenState worldGenState = this.worldGenStateRef.get(); + if (worldGenState == null) + { + LOGGER.warn("Attempted to stop world gen when it was not running"); + return; + } + + // shut down the world generator + while (!this.worldGenStateRef.compareAndSet(worldGenState, null)) + { + worldGenState = this.worldGenStateRef.get(); + if (worldGenState == null) + { + return; + } + } + dataFileHandler.clearGenerationQueue(); + worldGenState.closeAsync(true).join(); //TODO: Make it async. + dataFileHandler.removeWorldGenCompleteListener(onWorldGenCompleteListener); + } + + public boolean isWorldGenRunning() + { + return this.worldGenStateRef.get() != null; + } + + public void worldGenTick(DhBlockPos2D targetPosForGeneration) + { + WorldGenState worldGenState = this.worldGenStateRef.get(); + if (worldGenState != null) + { + // queue new world generation requests + worldGenState.tick(targetPosForGeneration); + } + } + + public void close() + { + // shutdown the world-gen + WorldGenState worldGenState = this.worldGenStateRef.get(); + if (worldGenState != null) + { + while (!this.worldGenStateRef.compareAndSet(worldGenState, null)) + { + worldGenState = this.worldGenStateRef.get(); + if (worldGenState == null) + { + break; + } + } + + if (worldGenState != null) + { + worldGenState.closeAsync(true).join(); //TODO: Make it async. + } + } + dataFileHandler.close(); + this.worldGenF3Message.close(); + } +} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java index 2b39ff96c..0e284681f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhClientWorld.java @@ -149,8 +149,12 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld public void clientTick() { this.eventLoop.tick(); } - @Override - public CompletableFuture saveAndFlush() + public void doWorldGen() { + // Not implemented + } + + @Override + public CompletableFuture saveAndFlush() { return CompletableFuture.allOf(this.levels.values().stream().map(DhClientLevel::saveAsync).toArray(CompletableFuture[]::new)); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/IDhClientWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/IDhClientWorld.java index ce5c72afd..486b90afa 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/IDhClientWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/IDhClientWorld.java @@ -7,6 +7,8 @@ public interface IDhClientWorld extends IDhWorld { void clientTick(); + void doWorldGen(); + default IDhClientLevel getOrLoadClientLevel(ILevelWrapper levelWrapper) { return (IDhClientLevel) this.getOrLoadLevel(levelWrapper); } } From e62ddc302b76b981cbe3635826f61510a5e5bbc1 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Thu, 24 Aug 2023 20:05:54 -0500 Subject: [PATCH 11/19] Require a ILevelWrapper when deserializing BlockStateWrappers --- .../worldGenerator/IDhApiWorldGenerator.java | 6 +++--- .../dataObjects/fullData/FullDataPointIdMap.java | 13 +++++++------ .../fullData/sources/CompleteFullDataSource.java | 5 +++-- .../sources/HighDetailIncompleteFullDataSource.java | 5 +++-- .../sources/LowDetailIncompleteFullDataSource.java | 5 +++-- .../interfaces/IStreamableFullDataSource.java | 5 +++-- .../core/wrapperInterfaces/IWrapperFactory.java | 2 +- .../wrapperInterfaces/block/IBlockStateWrapper.java | 3 +-- 8 files changed, 24 insertions(+), 20 deletions(-) diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java index 572356810..8396ff340 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/override/worldGenerator/IDhApiWorldGenerator.java @@ -85,9 +85,9 @@ public interface IDhApiWorldGenerator extends Closeable, IDhApiOverrideable * Please run your generator in game at least once to confirm the objects you are returning are correct.

* * Consumer expected inputs for each minecraft version (in order):
- * 1.18: [net.minecraft.world.level.chunk.ChunkAccess] and [net.minecraft.world.level.LevelReader]
- * 1.19: [net.minecraft.world.level.chunk.ChunkAccess] and [net.minecraft.world.level.LevelReader]
- * 1.20: [net.minecraft.world.level.chunk.ChunkAccess] and [net.minecraft.world.level.LevelReader]
+ * 1.16, 1.17, 1.18, 1.19, 1.20:
+ * - [net.minecraft.world.level.chunk.ChunkAccess]
+ * - [net.minecraft.world.level.ServerLevel] or [net.minecraft.world.level.ClientLevel]
*/ CompletableFuture generateChunks( int chunkPosMinX, int chunkPosMinZ, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java index 319e78fae..c3249d07f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java @@ -7,6 +7,7 @@ import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStre import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; @@ -182,7 +183,7 @@ public class FullDataPointIdMap } /** Creates a new IdBiomeBlockStateMap from the given UTF formatted stream */ - public static FullDataPointIdMap deserialize(DhDataInputStream inputStream, DhSectionPos pos) throws IOException, InterruptedException + public static FullDataPointIdMap deserialize(DhDataInputStream inputStream, DhSectionPos pos, ILevelWrapper levelWrapper) throws IOException, InterruptedException { int entityCount = inputStream.readInt(); @@ -193,7 +194,7 @@ public class FullDataPointIdMap for (int i = 0; i < entityCount; i++) { String entryString = inputStream.readUTF(); - Entry newEntry = Entry.deserialize(entryString); + Entry newEntry = Entry.deserialize(entryString, levelWrapper); newMap.entryList.add(newEntry); if (RUN_SERIALIZATION_DUPLICATE_VALIDATION) @@ -280,7 +281,7 @@ public class FullDataPointIdMap Entry other = (Entry) otherObj; return other.biome.serialize().equals(this.biome.serialize()) - && other.blockState.serialize().equals(this.blockState.serialize()); + && other.blockState.getSerialString().equals(this.blockState.getSerialString()); } @Override @@ -288,9 +289,9 @@ public class FullDataPointIdMap - public String serialize() { return this.biome.serialize() + BLOCK_STATE_SEPARATOR_STRING + this.blockState.serialize(); } + public String serialize() { return this.biome.serialize() + BLOCK_STATE_SEPARATOR_STRING + this.blockState.getSerialString(); } - public static Entry deserialize(String str) throws IOException, InterruptedException + public static Entry deserialize(String str, ILevelWrapper levelWrapper) throws IOException, InterruptedException { String[] stringArray = str.split(BLOCK_STATE_SEPARATOR_STRING); if (stringArray.length != 2) @@ -305,7 +306,7 @@ public class FullDataPointIdMap } IBiomeWrapper biome = WRAPPER_FACTORY.deserializeBiomeWrapper(stringArray[0]); - IBlockStateWrapper blockState = WRAPPER_FACTORY.deserializeBlockStateWrapper(stringArray[1]); + IBlockStateWrapper blockState = WRAPPER_FACTORY.deserializeBlockStateWrapper(stringArray[1], levelWrapper); return new Entry(biome, blockState); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java index d5bb3990c..d7d044dda 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/CompleteFullDataSource.java @@ -17,6 +17,7 @@ import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import org.apache.logging.log4j.Logger; @@ -232,7 +233,7 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu this.mapping.serialize(outputStream); } @Override - public FullDataPointIdMap readIdMappings(long[][] dataPoints, DhDataInputStream inputStream) throws IOException, InterruptedException + public FullDataPointIdMap readIdMappings(long[][] dataPoints, DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException { int guardByte = inputStream.readInt(); if (guardByte != IFullDataSource.DATA_GUARD_BYTE) @@ -240,7 +241,7 @@ public class CompleteFullDataSource extends FullDataArrayAccessor implements IFu throw new IOException("Invalid data content end guard for ID mapping"); } - return FullDataPointIdMap.deserialize(inputStream, this.sectionPos); + return FullDataPointIdMap.deserialize(inputStream, this.sectionPos, levelWrapper); } @Override public void setIdMapping(FullDataPointIdMap mappings) { this.mapping.mergeAndReturnRemappedEntityIds(mappings); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java index b19d820e7..3d122233e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/HighDetailIncompleteFullDataSource.java @@ -17,6 +17,7 @@ import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import org.apache.logging.log4j.Logger; @@ -350,7 +351,7 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo @Override - public FullDataPointIdMap readIdMappings(long[][][] dataPoints, DhDataInputStream inputStream) throws IOException, InterruptedException + public FullDataPointIdMap readIdMappings(long[][][] dataPoints, DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException { // mark the start of the ID data int idMappingStartByte = inputStream.readInt(); @@ -361,7 +362,7 @@ public class HighDetailIncompleteFullDataSource implements IIncompleteFullDataSo } // deserialize the ID data - return FullDataPointIdMap.deserialize(inputStream, this.sectionPos); + return FullDataPointIdMap.deserialize(inputStream, this.sectionPos, levelWrapper); } @Override public void writeIdMappings(DhDataOutputStream dataOutputStream) throws IOException diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java index 41e691c83..ac2d0cbc8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/LowDetailIncompleteFullDataSource.java @@ -17,6 +17,7 @@ import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import org.apache.logging.log4j.Logger; @@ -244,7 +245,7 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp } @Override - public FullDataPointIdMap readIdMappings(StreamDataPointContainer streamDataPointContainer, DhDataInputStream inputStream) throws IOException, InterruptedException + public FullDataPointIdMap readIdMappings(StreamDataPointContainer streamDataPointContainer, DhDataInputStream inputStream, ILevelWrapper levelWrapper) throws IOException, InterruptedException { // Id mapping int dataPresentFlag = inputStream.readInt(); @@ -252,7 +253,7 @@ public class LowDetailIncompleteFullDataSource extends FullDataArrayAccessor imp { throw new IOException("invalid ID mapping end guard"); } - return FullDataPointIdMap.deserialize(inputStream, this.sectionPos); + return FullDataPointIdMap.deserialize(inputStream, this.sectionPos, levelWrapper); } @Override public void setIdMapping(FullDataPointIdMap mappings) { this.mapping.mergeAndReturnRemappedEntityIds(mappings); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IStreamableFullDataSource.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IStreamableFullDataSource.java index 88453aece..147ad5b06 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IStreamableFullDataSource.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/sources/interfaces/IStreamableFullDataSource.java @@ -8,6 +8,7 @@ import com.seibel.distanthorizons.core.file.fullDatafile.FullDataMetaFile; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataOutputStream; import com.seibel.distanthorizons.core.util.objects.dataStreams.*; +import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import java.io.IOException; @@ -54,7 +55,7 @@ public interface IStreamableFullDataSource diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java index 14760a02f..768168188 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/block/IBlockStateWrapper.java @@ -5,8 +5,7 @@ import com.seibel.distanthorizons.api.interfaces.block.IDhApiBlockStateWrapper; /** A Minecraft version independent way of handling Blocks. */ public interface IBlockStateWrapper extends IDhApiBlockStateWrapper { - /** will only work if a level is currently loaded */ - String serialize(); + String getSerialString(); /** * Returning a value of 0 means the block is completely transparent. Date: Thu, 24 Aug 2023 21:16:13 -0500 Subject: [PATCH 12/19] Revert "Fixed config system only working with dh" This reverts commit f1b31a4cfe40bec682f4a71203a6c41428e744dd. --- .../com/seibel/distanthorizons/core/config/Config.java | 8 ++++++++ .../presets/AbstractPresetConfigEventHandler.java | 2 +- .../core/config/file/ConfigFileHandling.java | 5 +++++ 3 files changed, 14 insertions(+), 1 deletion(-) 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 4b4696a56..761db1814 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 @@ -60,6 +60,14 @@ public class Config public static ConfigCategory client = new ConfigCategory.Builder().set(Client.class).build(); + /** + * False if the config hasn't been loaded in from file yet. + * While in this state the config shouldn't be modified since it may cause file corruption.

+ * + * True if the config has been loaded and is ready to use. + */ + public static boolean loaded = false; + public static class Client diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java index 12131b0bd..1aae2f6d8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java @@ -64,7 +64,7 @@ public abstract class AbstractPresetConfigEventHandler Date: Thu, 24 Aug 2023 21:37:55 -0500 Subject: [PATCH 13/19] Require a ILevelWrapper when deserializing BiomeWrappers --- .../core/dataObjects/fullData/FullDataPointIdMap.java | 6 +++--- .../core/wrapperInterfaces/IWrapperFactory.java | 2 +- .../core/wrapperInterfaces/world/IBiomeWrapper.java | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java index c3249d07f..a12e9a8ee 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/fullData/FullDataPointIdMap.java @@ -280,7 +280,7 @@ public class FullDataPointIdMap return false; Entry other = (Entry) otherObj; - return other.biome.serialize().equals(this.biome.serialize()) + return other.biome.getSerialString().equals(this.biome.getSerialString()) && other.blockState.getSerialString().equals(this.blockState.getSerialString()); } @@ -289,7 +289,7 @@ public class FullDataPointIdMap - public String serialize() { return this.biome.serialize() + BLOCK_STATE_SEPARATOR_STRING + this.blockState.getSerialString(); } + public String serialize() { return this.biome.getSerialString() + BLOCK_STATE_SEPARATOR_STRING + this.blockState.getSerialString(); } public static Entry deserialize(String str, ILevelWrapper levelWrapper) throws IOException, InterruptedException { @@ -305,7 +305,7 @@ public class FullDataPointIdMap throw new InterruptedException(FullDataPointIdMap.class.getSimpleName() + " task interrupted."); } - IBiomeWrapper biome = WRAPPER_FACTORY.deserializeBiomeWrapper(stringArray[0]); + IBiomeWrapper biome = WRAPPER_FACTORY.deserializeBiomeWrapper(stringArray[0], levelWrapper); IBlockStateWrapper blockState = WRAPPER_FACTORY.deserializeBlockStateWrapper(stringArray[1], levelWrapper); return new Entry(biome, blockState); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java index a814b3543..f7e53aae9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/IWrapperFactory.java @@ -39,7 +39,7 @@ import java.util.HashSet; public interface IWrapperFactory extends IBindable { AbstractBatchGenerationEnvironmentWrapper createBatchGenerator(IDhLevel targetLevel); - IBiomeWrapper deserializeBiomeWrapper(String str) throws IOException; + IBiomeWrapper deserializeBiomeWrapper(String str, ILevelWrapper levelWrapper) throws IOException; IBlockStateWrapper deserializeBlockStateWrapper(String str, ILevelWrapper levelWrapper) throws IOException; IBlockStateWrapper getAirBlockStateWrapper(); /** diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IBiomeWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IBiomeWrapper.java index d2bf1072e..32f574f2f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IBiomeWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IBiomeWrapper.java @@ -29,6 +29,6 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindab public interface IBiomeWrapper extends IDhApiBiomeWrapper, IBindable { String getName(); - String serialize(); + String getSerialString(); } From d5922900b5448e87cc9d26b52370073c0193ef3a Mon Sep 17 00:00:00 2001 From: James Seibel Date: Fri, 25 Aug 2023 07:52:16 -0500 Subject: [PATCH 14/19] Attempt to fix LodRenderer freeing GL objects while they are in use --- .../bufferBuilding/ColumnRenderBuffer.java | 33 +- .../core/render/renderer/LodRenderer.java | 369 ++++++++++-------- 2 files changed, 227 insertions(+), 175 deletions(-) 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 f651207fc..91a659936 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 @@ -224,22 +224,31 @@ public class ColumnRenderBuffer extends AbstractRenderBuffer implements IDebugRe { boolean hasRendered = false; - renderContext.setupOffset(this.pos); - for (GLVertexBuffer vbo : this.vbosTransparent) + try { - if (vbo == null) - { - continue; - } + // can throw an IllegalStateException if the GL program was freed before it should've been + renderContext.setupOffset(this.pos); - if (vbo.getVertexCount() == 0) + for (GLVertexBuffer vbo : this.vbosTransparent) { - continue; + if (vbo == null) + { + continue; + } + + if (vbo.getVertexCount() == 0) + { + continue; + } + + hasRendered = true; + renderContext.drawVbo(vbo); + //LodRenderer.tickLogger.info("Vertex buffer: {}", vbo); } - - hasRendered = true; - renderContext.drawVbo(vbo); - //LodRenderer.tickLogger.info("Vertex buffer: {}", vbo); + } + catch (IllegalStateException e) + { + LOGGER.error("renderContext program doesn't exist for pos: "+this.pos, e); } return hasRendered; 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 ce8721347..4e8b1cbe9 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 @@ -50,6 +50,7 @@ import org.lwjgl.opengl.GL32; import java.awt.*; import java.time.Duration; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.ReentrantLock; /** * This is where all the magic happens.
@@ -72,11 +73,21 @@ public class LodRenderer public static boolean transparencyEnabled = true; public static boolean fakeOceanFloor = true; - public void setupOffset(DhBlockPos pos) + /** used to prevent cleaning up render resources while they are being used */ + private static final ReentrantLock renderLock = new ReentrantLock(); + + + + public void setupOffset(DhBlockPos pos) throws IllegalStateException { Vec3d cam = MC_RENDER.getCameraExactPosition(); Vec3f modelPos = new Vec3f((float) (pos.x - cam.x), (float) (pos.y - cam.y), (float) (pos.z - cam.z)); + if (!GL32.glIsProgram(this.shaderProgram.id)) + { + throw new IllegalStateException("No GL program exists with the ID: ["+this.shaderProgram.id+"]. This either means a shader program was freed while it was still in use or was never created."); + } + this.shaderProgram.bind(); this.shaderProgram.setModelPos(modelPos); } @@ -138,13 +149,24 @@ public class LodRenderer return; } - EVENT_LOGGER.info("Shutting down " + LodRenderer.class.getSimpleName() + "..."); this.rendererClosed = true; - GLProxy.getInstance().recordOpenGlCall(this::cleanup); - this.bufferHandler.close(); - EVENT_LOGGER.info("Finished shutting down " + LodRenderer.class.getSimpleName()); + // wait for the renderer to finish before closing (to prevent closing resources that are currently in use) + renderLock.lock(); + try + { + EVENT_LOGGER.info("Shutting down " + LodRenderer.class.getSimpleName() + "..."); + + this.cleanup(); + this.bufferHandler.close(); + + EVENT_LOGGER.info("Finished shutting down " + LodRenderer.class.getSimpleName()); + } + finally + { + renderLock.unlock(); + } } public void drawLODs(Mat4f baseModelViewMatrix, Mat4f baseProjectionMatrix, float partialTicks, IProfilerWrapper profiler) @@ -156,169 +178,183 @@ public class LodRenderer } - // get MC's shader program and save MC's render state so we can restore it later - LagSpikeCatcher drawSaveGLState = new LagSpikeCatcher(); - GLState minecraftGlState = new GLState(); - if (ENABLE_DUMP_GL_STATE) + + if (!renderLock.tryLock()) { - tickLogger.debug("Saving GL state: " + minecraftGlState); + // never lock the render thread, if the lock isn't available don't wait for it + return; } - drawSaveGLState.end("drawSaveGLState"); - // make sure everything has been initialized - GLProxy glProxy = GLProxy.getInstance(); - - - - //===================// - // draw params setup // - //===================// - - profiler.push("LOD draw setup"); - /*---------Set GL State--------*/ - GL32.glViewport(0, 0, MC_RENDER.getTargetFrameBufferViewportWidth(), MC_RENDER.getTargetFrameBufferViewportHeight()); - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0); - boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get(); - if (renderWireframe) + try { - GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE); - //GL32.glDisable(GL32.GL_CULL_FACE); - } - else - { - GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); - GL32.glEnable(GL32.GL_CULL_FACE); - } - GL32.glEnable(GL32.GL_DEPTH_TEST); - GL32.glDepthFunc(GL32.GL_LESS); - - - - transparencyEnabled = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled; - fakeOceanFloor = Config.Client.Advanced.Graphics.Quality.transparency.get().fakeTransparencyEnabled; - - GL32.glDisable(GL32.GL_BLEND); // We render opaque first, then transparent - GL32.glDepthMask(true); - GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT); - - /*---------Bind required objects--------*/ - // Setup LodRenderProgram and the LightmapTexture if it has not yet been done - // also binds LightmapTexture, VAO, and ShaderProgram - if (!this.isSetupComplete) - { - this.setup(); - } - else - { - LodFogConfig newFogConfig = this.shaderProgram.isShaderUsable(); - if (newFogConfig != null) + // get MC's shader program and save MC's render state so we can restore it later + LagSpikeCatcher drawSaveGLState = new LagSpikeCatcher(); + GLState minecraftGlState = new GLState(); + if (ENABLE_DUMP_GL_STATE) { - this.shaderProgram.free(); - this.shaderProgram = new LodRenderProgram(newFogConfig); - FogShader.INSTANCE.free(); - FogShader.INSTANCE = new FogShader(newFogConfig); + tickLogger.debug("Saving GL state: " + minecraftGlState); } - this.shaderProgram.bind(); - } - GL32.glActiveTexture(GL32.GL_TEXTURE0); - - /*---------Get required data--------*/ - int vanillaBlockRenderedDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH; - Mat4f modelViewProjectionMatrix = RenderUtil.createCombinedModelViewProjectionMatrix(baseProjectionMatrix, baseModelViewMatrix, partialTicks); - - /*---------Fill uniform data--------*/ - this.shaderProgram.fillUniformData(modelViewProjectionMatrix, /*Light map = GL_TEXTURE0*/ 0, - MC.getWrappedClientWorld().getMinHeight(), vanillaBlockRenderedDistance); - - // Note: Since lightmapTexture is changing every frame, it's faster to recreate it than to reuse the old one. - ILightMapWrapper lightmap = MC_RENDER.getLightmapWrapper(); - lightmap.bind(); - if (ENABLE_IBO) - { - this.quadIBO.bind(); - } - - this.bufferHandler.buildRenderListAndUpdateSections(this.getLookVector()); - - - - //===========// - // rendering // - //===========// - - LagSpikeCatcher drawLagSpikeCatcher = new LagSpikeCatcher(); - - profiler.popPush("LOD Opaque"); - // TODO: Directional culling - this.bufferHandler.renderOpaque(this); - - if (Config.Client.Advanced.Graphics.Quality.ssao.get()) - { - profiler.popPush("LOD SSAO"); - SSAORenderer.INSTANCE.render(partialTicks); + drawSaveGLState.end("drawSaveGLState"); - // TODO: Fix this file (or check the result is the same) so that SSAORenderer could be deleted - //SSAOShader.INSTANCE.render(partialTicks); // For some reason this looks slightly different :/ - } - - - profiler.popPush("LOD Fog"); - // TODO add the model view/projection matrices to the render() function - FogShader.INSTANCE.setModelViewProjectionMatrix(modelViewProjectionMatrix); - FogShader.INSTANCE.render(partialTicks); - - // DarkShader.INSTANCE.render(partialTicks); // A test shader to make the world darker - - - if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled) - { - profiler.popPush("LOD Transparent"); + // make sure everything has been initialized + GLProxy glProxy = GLProxy.getInstance(); - GL32.glEnable(GL32.GL_BLEND); - GL32.glBlendFunc(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA); - this.bufferHandler.renderTransparent(this); - GL32.glDepthMask(true); // Apparently the depth mask state is stored in the FBO, so glState fails to restore it... + + //===================// + // draw params setup // + //===================// + + profiler.push("LOD draw setup"); + /*---------Set GL State--------*/ + GL32.glViewport(0, 0, MC_RENDER.getTargetFrameBufferViewportWidth(), MC_RENDER.getTargetFrameBufferViewportHeight()); + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0); + boolean renderWireframe = Config.Client.Advanced.Debugging.renderWireframe.get(); + if (renderWireframe) + { + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_LINE); + //GL32.glDisable(GL32.GL_CULL_FACE); + } + else + { + GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); + GL32.glEnable(GL32.GL_CULL_FACE); + } + GL32.glEnable(GL32.GL_DEPTH_TEST); + GL32.glDepthFunc(GL32.GL_LESS); + + + + transparencyEnabled = Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled; + fakeOceanFloor = Config.Client.Advanced.Graphics.Quality.transparency.get().fakeTransparencyEnabled; + + GL32.glDisable(GL32.GL_BLEND); // We render opaque first, then transparent + GL32.glDepthMask(true); + GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT); + + /*---------Bind required objects--------*/ + // Setup LodRenderProgram and the LightmapTexture if it has not yet been done + // also binds LightmapTexture, VAO, and ShaderProgram + if (!this.isSetupComplete) + { + this.setup(); + } + else + { + LodFogConfig newFogConfig = this.shaderProgram.isShaderUsable(); + if (newFogConfig != null) + { + this.shaderProgram.free(); + this.shaderProgram = new LodRenderProgram(newFogConfig); + FogShader.INSTANCE.free(); + FogShader.INSTANCE = new FogShader(newFogConfig); + } + this.shaderProgram.bind(); + } + GL32.glActiveTexture(GL32.GL_TEXTURE0); + + /*---------Get required data--------*/ + int vanillaBlockRenderedDistance = MC_RENDER.getRenderDistance() * LodUtil.CHUNK_WIDTH; + Mat4f modelViewProjectionMatrix = RenderUtil.createCombinedModelViewProjectionMatrix(baseProjectionMatrix, baseModelViewMatrix, partialTicks); + + /*---------Fill uniform data--------*/ + this.shaderProgram.fillUniformData(modelViewProjectionMatrix, /*Light map = GL_TEXTURE0*/ 0, + MC.getWrappedClientWorld().getMinHeight(), vanillaBlockRenderedDistance); + + // Note: Since lightmapTexture is changing every frame, it's faster to recreate it than to reuse the old one. + ILightMapWrapper lightmap = MC_RENDER.getLightmapWrapper(); + lightmap.bind(); + if (ENABLE_IBO) + { + this.quadIBO.bind(); + } + + this.bufferHandler.buildRenderListAndUpdateSections(this.getLookVector()); + + + + //===========// + // rendering // + //===========// + + LagSpikeCatcher drawLagSpikeCatcher = new LagSpikeCatcher(); + + profiler.popPush("LOD Opaque"); + // TODO: Directional culling + this.bufferHandler.renderOpaque(this); + + if (Config.Client.Advanced.Graphics.Quality.ssao.get()) + { + profiler.popPush("LOD SSAO"); + SSAORenderer.INSTANCE.render(partialTicks); + + // TODO: Fix this file (or check the result is the same) so that SSAORenderer could be deleted + //SSAOShader.INSTANCE.render(partialTicks); // For some reason this looks slightly different :/ + } + + + profiler.popPush("LOD Fog"); + // TODO add the model view/projection matrices to the render() function + FogShader.INSTANCE.setModelViewProjectionMatrix(modelViewProjectionMatrix); FogShader.INSTANCE.render(partialTicks); - } - - drawLagSpikeCatcher.end("LodDraw"); - - - - //================// - // render cleanup // - //================// - - profiler.popPush("LOD cleanup"); - LagSpikeCatcher drawCleanup = new LagSpikeCatcher(); - lightmap.unbind(); - if (ENABLE_IBO) - { - this.quadIBO.unbind(); - } - - GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0); - - this.shaderProgram.unbind(); - - if (Config.Client.Advanced.Debugging.debugWireframeRendering.get()) - { - profiler.popPush("Debug wireframes"); - // Note: this can be very slow if a lot of boxes are being rendered - DebugRenderer.INSTANCE.render(modelViewProjectionMatrix); + + // DarkShader.INSTANCE.render(partialTicks); // A test shader to make the world darker + + + if (Config.Client.Advanced.Graphics.Quality.transparency.get().transparencyEnabled) + { + profiler.popPush("LOD Transparent"); + + GL32.glEnable(GL32.GL_BLEND); + GL32.glBlendFunc(GL32.GL_SRC_ALPHA, GL32.GL_ONE_MINUS_SRC_ALPHA); + this.bufferHandler.renderTransparent(this); + GL32.glDepthMask(true); // Apparently the depth mask state is stored in the FBO, so glState fails to restore it... + + FogShader.INSTANCE.render(partialTicks); + } + + drawLagSpikeCatcher.end("LodDraw"); + + + + //================// + // render cleanup // + //================// + profiler.popPush("LOD cleanup"); + LagSpikeCatcher drawCleanup = new LagSpikeCatcher(); + lightmap.unbind(); + if (ENABLE_IBO) + { + this.quadIBO.unbind(); + } + + GL32.glBindBuffer(GL32.GL_ARRAY_BUFFER, 0); + + this.shaderProgram.unbind(); + + if (Config.Client.Advanced.Debugging.debugWireframeRendering.get()) + { + profiler.popPush("Debug wireframes"); + // Note: this can be very slow if a lot of boxes are being rendered + DebugRenderer.INSTANCE.render(modelViewProjectionMatrix); + profiler.popPush("LOD cleanup"); + } + + GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT); + + minecraftGlState.restore(); + drawCleanup.end("LodDrawCleanup"); + + // end of internal LOD profiling + profiler.pop(); + tickLogger.incLogTries(); + + } + finally + { + renderLock.unlock(); } - - GL32.glClear(GL32.GL_DEPTH_BUFFER_BIT); - - minecraftGlState.restore(); - drawCleanup.end("LodDrawCleanup"); - - // end of internal LOD profiling - profiler.pop(); - tickLogger.incLogTries(); - } @@ -376,7 +412,7 @@ public class LodRenderer //======================// /** - * cleanup and free all render objects. REQUIRES to be in render thread + * cleanup and free all render objects. MUST be on the render thread * (Many objects are Native, outside of JVM, and need manual cleanup) */ private void cleanup() @@ -393,13 +429,20 @@ public class LodRenderer } this.isSetupComplete = false; - EVENT_LOGGER.info("Renderer Cleanup Started"); - this.shaderProgram.free(); - if (this.quadIBO != null) + + GLProxy.getInstance().recordOpenGlCall(() -> { - this.quadIBO.destroy(false); - } - EVENT_LOGGER.info("Renderer Cleanup Complete"); + EVENT_LOGGER.info("Renderer Cleanup Started"); + + this.shaderProgram.free(); + this.shaderProgram = null; + if (this.quadIBO != null) + { + this.quadIBO.destroy(false); + } + + EVENT_LOGGER.info("Renderer Cleanup Complete"); + }); } } From cdc6635a7634a2e57d25817bd5290b9f44b01f70 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Fri, 25 Aug 2023 21:05:47 -0500 Subject: [PATCH 15/19] Refactor --- .../core/api/internal/ClientApi.java | 2 + .../generation/IWorldGenerationQueue.java | 3 + .../core/generation/WorldGenerationQueue.java | 2 +- .../core/level/DhClientServerLevel.java | 4 +- .../core/level/DhServerLevel.java | 2 +- .../core/level/ServerLevelModule.java | 21 +- .../core/level/WorldGenModule.java | 222 ++++++++++-------- 7 files changed, 139 insertions(+), 117 deletions(-) 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 6934834b1..cfcaf8881 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 @@ -322,7 +322,9 @@ public class ClientApi // Ignore local world gen, as it's managed by server ticking if (!(clientWorld instanceof DhClientServerWorld)) + { SharedApi.worldGenTick(clientWorld::doWorldGen); + } } profiler.pop(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java index e1aa2e376..7181ae80e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/IWorldGenerationQueue.java @@ -11,11 +11,13 @@ import java.util.concurrent.CompletableFuture; public interface IWorldGenerationQueue extends Closeable { + /** the largest numerical detail level */ byte largestDataDetail(); CompletableFuture submitGenTask(DhLodPos pos, byte requiredDataDetail, IWorldGenTaskTracker tracker); void cancelGenTasks(Iterable positions); + /** @param targetPos the position that world generation should be centered around, generally this will be the player's position. */ void runCurrentGenTasksUntilBusy(DhBlockPos2D targetPos); int getWaitingTaskCount(); @@ -23,4 +25,5 @@ public interface IWorldGenerationQueue extends Closeable CompletableFuture startClosing(boolean cancelCurrentGeneration, boolean alsoInterruptRunning); void close(); + } 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 0a3c8477f..51e34783f 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 @@ -160,7 +160,7 @@ public class WorldGenerationQueue implements IWorldGenerationQueue, IDebugRender @Override public void cancelGenTasks(Iterable positions) { - // TODO Should anything be here? + // TODO Should we cancel generation of chunks that were loaded by the player? } //===============// 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 acc6ce14a..8417a7f74 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 @@ -42,7 +42,7 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS LOGGER.warn("unable to create data folder."); } this.serverLevelWrapper = serverLevelWrapper; - this.serverside = new ServerLevelModule(this, serverLevelWrapper, saveStructure); + this.serverside = new ServerLevelModule(this, saveStructure); this.clientside = new ClientLevelModule(this); LOGGER.info("Started " + DhClientServerLevel.class.getSimpleName() + " for " + serverLevelWrapper + " with saves at " + saveStructure); } @@ -129,7 +129,7 @@ public class DhClientServerLevel extends DhLevel implements IDhClientLevel, IDhS } @Override - public IClientLevelWrapper getClientLevelWrapper() { return serverside.levelWrapper.tryGetClientLevelWrapper(); } + public IClientLevelWrapper getClientLevelWrapper() { return this.serverLevelWrapper.tryGetClientLevelWrapper(); } @Override public void clearRenderCache() 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 907dac539..36a950644 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 @@ -27,7 +27,7 @@ public class DhServerLevel extends DhLevel implements IDhServerLevel LOGGER.warn("unable to create data folder."); } this.serverLevelWrapper = serverLevelWrapper; - serverside = new ServerLevelModule(this, serverLevelWrapper, saveStructure); + this.serverside = new ServerLevelModule(this, saveStructure); LOGGER.info("Started DHLevel for {} with saves at {}", serverLevelWrapper, saveStructure); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java index adc85dcca..f3537f03c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/ServerLevelModule.java @@ -16,8 +16,7 @@ public class ServerLevelModule { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - public final IServerLevelWrapper levelWrapper; - public final IDhServerLevel parent; + public final IDhServerLevel parentServerLevel; public final AbstractSaveStructure saveStructure; public final GeneratedFullDataFileHandler dataFileHandler; public final AppliedConfigState worldGeneratorEnabledConfig; @@ -26,24 +25,22 @@ public class ServerLevelModule - public ServerLevelModule(IDhServerLevel parent, IServerLevelWrapper levelWrapper, AbstractSaveStructure saveStructure) + public ServerLevelModule(IDhServerLevel parentServerLevel, AbstractSaveStructure saveStructure) { - this.parent = parent; - this.levelWrapper = levelWrapper; + this.parentServerLevel = parentServerLevel; this.saveStructure = saveStructure; - this.dataFileHandler = new GeneratedFullDataFileHandler(parent, saveStructure); + this.dataFileHandler = new GeneratedFullDataFileHandler(parentServerLevel, saveStructure); this.worldGeneratorEnabledConfig = new AppliedConfigState<>(Config.Client.Advanced.WorldGenerator.enableDistantGeneration); - this.worldGenModule = new WorldGenModule(this.dataFileHandler, this.parent); + this.worldGenModule = new WorldGenModule(this.dataFileHandler, this.parentServerLevel); } - //===============// - // data handling // - //===============// + + public void close() { // shutdown the world-gen this.worldGenModule.close(); - dataFileHandler.close(); + this.dataFileHandler.close(); } @@ -52,7 +49,7 @@ public class ServerLevelModule // helper classes // //================// - public static class WorldGenState extends WorldGenModule.WorldGenState + public static class WorldGenState extends WorldGenModule.AbstractWorldGenState { WorldGenState(IDhServerLevel level) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java index 97b0c9232..1f3b26c1b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java @@ -18,10 +18,127 @@ public class WorldGenModule implements Closeable private final GeneratedFullDataFileHandler dataFileHandler; private final GeneratedFullDataFileHandler.IOnWorldGenCompleteListener onWorldGenCompleteListener; - private final AtomicReference worldGenStateRef = new AtomicReference<>(); + private final AtomicReference worldGenStateRef = new AtomicReference<>(); private final F3Screen.DynamicMessage worldGenF3Message; - public static abstract class WorldGenState + + + public WorldGenModule(GeneratedFullDataFileHandler dataFileHandler, GeneratedFullDataFileHandler.IOnWorldGenCompleteListener onWorldGenCompleteListener) + { + this.dataFileHandler = dataFileHandler; + this.onWorldGenCompleteListener = onWorldGenCompleteListener; + this.worldGenF3Message = new F3Screen.DynamicMessage(() -> + { + AbstractWorldGenState worldGenState = this.worldGenStateRef.get(); + if (worldGenState != null) + { + int waitingCount = worldGenState.worldGenerationQueue.getWaitingTaskCount(); + int inProgressCount = worldGenState.worldGenerationQueue.getInProgressTaskCount(); + + return "World Gen Tasks: "+waitingCount+", (in progress: "+inProgressCount+")"; + } + else + { + return "World Gen Disabled"; + } + }); + + } + + + + //===================// + // world gen control // + //===================// + + public void startWorldGen(GeneratedFullDataFileHandler dataFileHandler, AbstractWorldGenState newWgs) + { + // create the new world generator + if (!this.worldGenStateRef.compareAndSet(null, newWgs)) + { + LOGGER.warn("Failed to start world gen due to concurrency"); + newWgs.closeAsync(false); + } + dataFileHandler.addWorldGenCompleteListener(this.onWorldGenCompleteListener); + dataFileHandler.setGenerationQueue(newWgs.worldGenerationQueue); + } + + public void stopWorldGen(GeneratedFullDataFileHandler dataFileHandler) + { + AbstractWorldGenState worldGenState = this.worldGenStateRef.get(); + if (worldGenState == null) + { + LOGGER.warn("Attempted to stop world gen when it was not running"); + return; + } + + // shut down the world generator + while (!this.worldGenStateRef.compareAndSet(worldGenState, null)) + { + worldGenState = this.worldGenStateRef.get(); + if (worldGenState == null) + { + return; + } + } + dataFileHandler.clearGenerationQueue(); + worldGenState.closeAsync(true).join(); //TODO: Make it async. + dataFileHandler.removeWorldGenCompleteListener(this.onWorldGenCompleteListener); + } + + /** @param targetPosForGeneration the position that world generation should be centered around */ + public void worldGenTick(DhBlockPos2D targetPosForGeneration) + { + AbstractWorldGenState worldGenState = this.worldGenStateRef.get(); + if (worldGenState != null) + { + // queue new world generation requests + worldGenState.tick(targetPosForGeneration); + } + } + + @Override + public void close() + { + // shutdown the world-gen + AbstractWorldGenState worldGenState = this.worldGenStateRef.get(); + if (worldGenState != null) + { + while (!this.worldGenStateRef.compareAndSet(worldGenState, null)) + { + worldGenState = this.worldGenStateRef.get(); + if (worldGenState == null) + { + break; + } + } + + if (worldGenState != null) + { + worldGenState.closeAsync(true).join(); //TODO: Make it async. + } + } + + this.dataFileHandler.close(); + this.worldGenF3Message.close(); + } + + + + //=========// + // getters // + //=========// + + public boolean isWorldGenRunning() { return this.worldGenStateRef.get() != null; } + + + + //================// + // helper classes // + //================// + + /** Handles the {@link IWorldGenerationQueue} and any other necessary world gen information. */ + public static abstract class AbstractWorldGenState { public IWorldGenerationQueue worldGenerationQueue; @@ -41,105 +158,8 @@ public class WorldGenModule implements Closeable }); } - public void tick(DhBlockPos2D targetPosForGeneration) - { - worldGenerationQueue.runCurrentGenTasksUntilBusy(targetPosForGeneration); - } + /** @param targetPosForGeneration the position that world generation should be centered around */ + public void tick(DhBlockPos2D targetPosForGeneration) { this.worldGenerationQueue.runCurrentGenTasksUntilBusy(targetPosForGeneration); } } - public WorldGenModule(GeneratedFullDataFileHandler dataFileHandler, GeneratedFullDataFileHandler.IOnWorldGenCompleteListener onWorldGenCompleteListener) - { - this.dataFileHandler = dataFileHandler; - this.onWorldGenCompleteListener = onWorldGenCompleteListener; - this.worldGenF3Message = new F3Screen.DynamicMessage(() -> - { - WorldGenState worldGenState = this.worldGenStateRef.get(); - if (worldGenState != null) - { - int waiting = worldGenState.worldGenerationQueue.getWaitingTaskCount(); - int inProgress = worldGenState.worldGenerationQueue.getInProgressTaskCount(); - - return "World Gen Tasks: "+waiting+", (in progress: "+inProgress+")"; - } - else - { - return "World Gen Disabled"; - } - }); - - } - - public void startWorldGen(GeneratedFullDataFileHandler dataFileHandler, WorldGenState newWgs) - { - // create the new world generator - if (!this.worldGenStateRef.compareAndSet(null, newWgs)) - { - LOGGER.warn("Failed to start world gen due to concurrency"); - newWgs.closeAsync(false); - } - dataFileHandler.addWorldGenCompleteListener(onWorldGenCompleteListener); - dataFileHandler.setGenerationQueue(newWgs.worldGenerationQueue); - } - - public void stopWorldGen(GeneratedFullDataFileHandler dataFileHandler) - { - WorldGenState worldGenState = this.worldGenStateRef.get(); - if (worldGenState == null) - { - LOGGER.warn("Attempted to stop world gen when it was not running"); - return; - } - - // shut down the world generator - while (!this.worldGenStateRef.compareAndSet(worldGenState, null)) - { - worldGenState = this.worldGenStateRef.get(); - if (worldGenState == null) - { - return; - } - } - dataFileHandler.clearGenerationQueue(); - worldGenState.closeAsync(true).join(); //TODO: Make it async. - dataFileHandler.removeWorldGenCompleteListener(onWorldGenCompleteListener); - } - - public boolean isWorldGenRunning() - { - return this.worldGenStateRef.get() != null; - } - - public void worldGenTick(DhBlockPos2D targetPosForGeneration) - { - WorldGenState worldGenState = this.worldGenStateRef.get(); - if (worldGenState != null) - { - // queue new world generation requests - worldGenState.tick(targetPosForGeneration); - } - } - - public void close() - { - // shutdown the world-gen - WorldGenState worldGenState = this.worldGenStateRef.get(); - if (worldGenState != null) - { - while (!this.worldGenStateRef.compareAndSet(worldGenState, null)) - { - worldGenState = this.worldGenStateRef.get(); - if (worldGenState == null) - { - break; - } - } - - if (worldGenState != null) - { - worldGenState.closeAsync(true).join(); //TODO: Make it async. - } - } - dataFileHandler.close(); - this.worldGenF3Message.close(); - } } From 2e647b5781d654c0164c29579856602992da8664 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Fri, 25 Aug 2023 21:17:42 -0500 Subject: [PATCH 16/19] Remove deprecated getX() and getZ() from DhChunkPos --- .../SubDimensionLevelMatcher.java | 8 ++--- .../distanthorizons/core/pos/DhChunkPos.java | 32 ++++++++----------- .../distanthorizons/core/util/LodUtil.java | 6 ++-- .../distanthorizons/core/util/RenderUtil.java | 8 ++--- .../minecraft/IMinecraftRenderWrapper.java | 4 +-- 5 files changed, 26 insertions(+), 32 deletions(-) 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 index 4ef8698fd..a5e45ed90 100644 --- 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 @@ -188,12 +188,12 @@ public class SubDimensionLevelMatcher implements AutoCloseable { // the chunk isn't empty but the LOD is... - String message = "Error: the chunk at (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") has a height of [" + newlyLoadedChunk.getHeight() + "] but the LOD generated is empty!"; + String message = "Error: the chunk at (" + playerChunkPos.x + "," + playerChunkPos.z + ") has a height of [" + newlyLoadedChunk.getHeight() + "] but the LOD generated is empty!"; LOGGER.error(message); } else { - String message = "Warning: The chunk at (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") is empty."; + String message = "Warning: The chunk at (" + playerChunkPos.x + "," + playerChunkPos.z + ") is empty."; LOGGER.warn(message); } return null; @@ -264,7 +264,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable // stop if the test chunk doesn't contain any data if (!testLodDataExists) { - String message = "The test chunk for dimension folder [" + LodUtil.shortenString(testLevelFolder.getName(), 8) + "] and chunk pos (" + playerChunkPos.getX() + "," + playerChunkPos.getZ() + ") is empty. This is expected if the position is outside the sub-dimension's generated area."; + String message = "The test chunk for dimension folder [" + LodUtil.shortenString(testLevelFolder.getName(), 8) + "] and chunk pos (" + playerChunkPos.x + "," + playerChunkPos.z + ") is empty. This is expected if the position is outside the sub-dimension's generated area."; LOGGER.info(message); continue; } @@ -272,7 +272,7 @@ public class SubDimensionLevelMatcher implements AutoCloseable // 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() + "]"); + LOGGER.info("Last known player pos: [" + testPlayerData.playerBlockPos.x + "," + testPlayerData.playerBlockPos.y + "," + testPlayerData.playerBlockPos.z + "]"); // check if the block positions are close int playerBlockDist = testPlayerData.playerBlockPos.getManhattanDistance(playerData.playerBlockPos); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java index b8d12ea55..c083e7b0f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhChunkPos.java @@ -19,8 +19,6 @@ package com.seibel.distanthorizons.core.pos; -import java.util.Objects; - public class DhChunkPos { public final int x; // Low 32 bits @@ -30,6 +28,7 @@ public class DhChunkPos public final int hashCode; + public DhChunkPos(int x, int z) { this.x = x; @@ -48,29 +47,24 @@ public class DhChunkPos // >> 4 is the Same as div 16 this(blockPos.x >> 4, blockPos.z >> 4); } - public DhChunkPos(long packed) { this(getX(packed), getZ(packed)); } + public DhChunkPos(long packed) { this(getXFromPackedLong(packed), getZFromPackedLong(packed)); } - public DhBlockPos center() { return new DhBlockPos(8 + x << 4, 0, 8 + z << 4); } - public DhBlockPos corner() { return new DhBlockPos(x << 4, 0, z << 4); } + public DhBlockPos center() { return new DhBlockPos(8 + this.x << 4, 0, 8 + this.z << 4); } + public DhBlockPos corner() { return new DhBlockPos(this.x << 4, 0, this.z << 4); } public static long toLong(int x, int z) { return ((long) x & 0xFFFFFFFFL) << 32 | (long) z & 0xFFFFFFFFL; } - public static int getX(long chunkPos) { return (int) (chunkPos >> 32); } - public static int getZ(long chunkPos) { return (int) (chunkPos & 0xFFFFFFFFL); } + private static int getXFromPackedLong(long chunkPos) { return (int) (chunkPos >> 32); } + private static int getZFromPackedLong(long chunkPos) { return (int) (chunkPos & 0xFFFFFFFFL); } - @Deprecated - public int getX() { return x; } - @Deprecated - public int getZ() { return z; } + public int getMinBlockX() { return this.x << 4; } + public int getMinBlockZ() { return this.z << 4; } - public int getMinBlockX() { return x << 4; } - public int getMinBlockZ() { return z << 4; } + public DhBlockPos2D getMinBlockPos() { return new DhBlockPos2D(this.x << 4, this.z << 4); } - public DhBlockPos2D getMinBlockPos() { return new DhBlockPos2D(x << 4, z << 4); } - - public long getLong() { return toLong(x, z); } + public long getLong() { return toLong(this.x, this.z); } @Override public boolean equals(Object obj) @@ -79,14 +73,14 @@ public class DhChunkPos { return true; } - else if (obj == null || getClass() != obj.getClass()) + else if (obj == null || this.getClass() != obj.getClass()) { return false; } else { DhChunkPos that = (DhChunkPos) obj; - return x == that.x && z == that.z; + return this.x == that.x && this.z == that.z; } } @@ -94,7 +88,7 @@ public class DhChunkPos public int hashCode() { return this.hashCode; } @Override - public String toString() { return "C[" + x + "," + z + "]"; } + public String toString() { return "C[" + this.x + "," + this.z + "]"; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java index 487a0dcf7..407097569 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/LodUtil.java @@ -221,11 +221,11 @@ public class LodUtil public Pos2D next() { DhChunkPos pos = posIter.next(); - return new Pos2D(pos.getX(), pos.getZ()); + return new Pos2D(pos.x, pos.z); } }, - MC_CLIENT.getPlayerChunkPos().getX() - renderDist, - MC_CLIENT.getPlayerChunkPos().getZ() - renderDist, + MC_CLIENT.getPlayerChunkPos().x - renderDist, + MC_CLIENT.getPlayerChunkPos().z - renderDist, renderDist * 2 + 1); } 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 e89a66960..d0a5813db 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 @@ -58,11 +58,11 @@ public class RenderUtil */ public static boolean isChunkPosInLoadedArea(DhChunkPos pos, DhChunkPos center) { - return (pos.getX() >= center.getX() - MC_RENDER.getRenderDistance() - && pos.getX() <= center.getX() + MC_RENDER.getRenderDistance()) + return (pos.x >= center.x - MC_RENDER.getRenderDistance() + && pos.x <= center.x + MC_RENDER.getRenderDistance()) && - (pos.getZ() >= center.getZ() - MC_RENDER.getRenderDistance() - && pos.getZ() <= center.getZ() + MC_RENDER.getRenderDistance()); + (pos.z >= center.z - MC_RENDER.getRenderDistance() + && pos.z <= center.z + MC_RENDER.getRenderDistance()); } /** diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java index edf67c7f6..d63af950a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftRenderWrapper.java @@ -113,8 +113,8 @@ public interface IMinecraftRenderWrapper extends IBindable int chunkDist = this.getRenderDistance() + 1; // For some reason having '+1' is actually closer to real value DhChunkPos centerChunkPos = mcWrapper.getPlayerChunkPos(); - int centerChunkX = centerChunkPos.getX(); - int centerChunkZ = centerChunkPos.getZ(); + int centerChunkX = centerChunkPos.x; + int centerChunkZ = centerChunkPos.z; int chunkDist2Mul4 = chunkDist * chunkDist * 4; // add every position within render distance From a0bc44ca06f7a19b7e2bad6047e68386cc6ac753 Mon Sep 17 00:00:00 2001 From: coolGi Date: Sat, 26 Aug 2023 15:20:08 +0930 Subject: [PATCH 17/19] Proper fix for checking if the config is loaded --- .../distanthorizons/core/config/Config.java | 8 ------- .../core/config/ConfigBase.java | 8 ++++++- .../AbstractPresetConfigEventHandler.java | 3 ++- .../core/config/file/ConfigFileHandling.java | 21 +++++++++++++------ 4 files changed, 24 insertions(+), 16 deletions(-) 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 761db1814..4b4696a56 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 @@ -60,14 +60,6 @@ public class Config public static ConfigCategory client = new ConfigCategory.Builder().set(Client.class).build(); - /** - * False if the config hasn't been loaded in from file yet. - * While in this state the config shouldn't be modified since it may cause file corruption.

- * - * True if the config has been loaded and is ready to use. - */ - public static boolean loaded = false; - public static class Client diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/ConfigBase.java b/core/src/main/java/com/seibel/distanthorizons/core/config/ConfigBase.java index 4b6df2e75..863d3adfa 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/ConfigBase.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/ConfigBase.java @@ -26,11 +26,13 @@ public class ConfigBase public static ConfigBase INSTANCE; public ConfigFileHandling configFileINSTANCE; - public static final Logger LOGGER = LogManager.getLogger(ConfigBase.class.getSimpleName()); + private final Logger LOGGER; public final String modID; public final String modName; public final int configVersion; + public boolean isLoaded = false; + /** @@ -78,6 +80,8 @@ public class ConfigBase public ConfigBase(String modID, String modName, Class config, int configVersion) { + this.LOGGER = LogManager.getLogger(this.getClass().getSimpleName() + ", " + modID); + LOGGER.info("Initialising config for " + modName); this.modID = modID; this.modName = modName; @@ -88,6 +92,8 @@ public class ConfigBase // File handling (load from file) this.configFileINSTANCE = new ConfigFileHandling(this); this.configFileINSTANCE.loadFromFile(); + + isLoaded = true; LOGGER.info("Config for " + modName + " initialised"); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java index 1aae2f6d8..7e6ceaca7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/eventHandlers/presets/AbstractPresetConfigEventHandler.java @@ -1,6 +1,7 @@ package com.seibel.distanthorizons.core.config.eventHandlers.presets; import com.seibel.distanthorizons.core.config.Config; +import com.seibel.distanthorizons.core.config.ConfigBase; import com.seibel.distanthorizons.core.config.ConfigEntryWithPresetOptions; import com.seibel.distanthorizons.core.config.listeners.IConfigListener; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; @@ -64,7 +65,7 @@ public abstract class AbstractPresetConfigEventHandler Date: Sat, 26 Aug 2023 17:28:07 +0930 Subject: [PATCH 18/19] Fixed up how file handling works in the config --- .../core/config/ConfigBase.java | 1 + .../core/config/file/ConfigFileHandling.java | 261 +++++++++--------- .../config/file/ConfigTypeConverters.java | 26 ++ .../core/config/types/AbstractConfigType.java | 6 +- .../core/config/types/ConfigEntry.java | 6 +- 5 files changed, 164 insertions(+), 136 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/ConfigBase.java b/core/src/main/java/com/seibel/distanthorizons/core/config/ConfigBase.java index 863d3adfa..eb0f54305 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/ConfigBase.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/ConfigBase.java @@ -18,6 +18,7 @@ import java.util.*; * * @author coolGi * @author Ran + * @version 2023-8-26 */ // Init the config after singletons have been blinded public class ConfigBase diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java b/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java index 1204809cb..8fc4fa631 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigFileHandling.java @@ -18,19 +18,17 @@ import java.nio.file.Path; * Handles reading and writing config files. * * @author coolGi - * @version 2023-7-16 + * @version 2023-8-26 */ public class ConfigFileHandling { - private static final Logger LOGGER = ConfigBase.LOGGER; - public final ConfigBase configBase; public final Path configPath; private final Logger LOGGER; /** This is the object for night-config */ - private final CommentedFileConfig configFileConfig; + private final CommentedFileConfig nightConfig; public ConfigFileHandling(ConfigBase configBase) { @@ -40,50 +38,47 @@ public class ConfigFileHandling configPath = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class) .getInstallationDirectory().toPath().resolve("config").resolve(this.configBase.modName + ".toml"); - this.configFileConfig = CommentedFileConfig.builder(configPath.toFile()).build(); + this.nightConfig = CommentedFileConfig.builder(configPath.toFile()).build(); } /** Saves the entire config to the file */ public void saveToFile() { - CommentedFileConfig config = CommentedFileConfig.builder(configPath.toFile()).build(); + saveToFile(this.nightConfig); + } + /** Saves the entire config to the file */ + public void saveToFile(CommentedFileConfig nightConfig) + { if (!Files.exists(configPath)) // Try to check if the config exists - try - { - if (!this.configPath.getParent().toFile().exists()) - { - Files.createDirectory(this.configPath.getParent()); - } - Files.createFile(configPath); - } - catch (IOException ex) - { - ex.printStackTrace(); - } + { + reCreateFile(configPath); + } + + + loadNightConfig(nightConfig); - loadConfig(config); for (AbstractConfigType entry : this.configBase.entries) { if (ConfigEntry.class.isAssignableFrom(entry.getClass())) { - createComment((ConfigEntry) entry, config); - saveEntry((ConfigEntry) entry, config); + createComment((ConfigEntry) entry, nightConfig); + saveEntry((ConfigEntry) entry, nightConfig); } } try { - config.save(); + nightConfig.save(); } catch (Exception e) { // If it fails to save, crash game SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Failed to save config at [" + configPath.toString() + "]", e); } - config.close(); } + /** * Loads the entire config from the file * @@ -91,46 +86,50 @@ public class ConfigFileHandling */ public void loadFromFile() { - CommentedFileConfig config = CommentedFileConfig.builder(configPath.toFile()).build(); + loadFromFile(nightConfig); + } + /** + * Loads the entire config from the file + * + * @apiNote This overwrites any value currently stored in the config + */ + public void loadFromFile(CommentedFileConfig nightConfig) + { // Attempt to load the file and if it fails then save config to file - try + if (Files.exists(configPath)) { - if (Files.exists(configPath)) - config.load(); - else - { - saveToFile(); - return; - } + loadNightConfig(nightConfig); } - catch (Exception e) + else { - e.printStackTrace(); - saveToFile(); + reCreateFile(configPath); return; } + // Load all the entries for (AbstractConfigType entry : this.configBase.entries) { - if (ConfigEntry.class.isAssignableFrom(entry.getClass())) + if ( + ConfigEntry.class.isAssignableFrom(entry.getClass()) && + entry.getAppearance().showInFile + ) { - createComment((ConfigEntry) entry, config); - loadEntry((ConfigEntry) entry, config); + createComment((ConfigEntry) entry, nightConfig); + loadEntry((ConfigEntry) entry, nightConfig); } } try { - config.save(); + nightConfig.save(); } catch (Exception e) { // If it fails to save, crash game SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Failed to save config at [" + configPath.toString() + "]", e); } - config.close(); } @@ -139,146 +138,148 @@ public class ConfigFileHandling // Save an entry when only given the entry public void saveEntry(ConfigEntry entry) { - CommentedFileConfig config = CommentedFileConfig.builder(configPath.toFile()).build(); - loadConfig(config); - saveEntry(entry, config); - config.save(); - config.close(); + saveEntry(entry, nightConfig); + nightConfig.save(); } - // Save an entry - @SuppressWarnings("unchecked") + /** Save an entry */ public void saveEntry(ConfigEntry entry, CommentedFileConfig workConfig) { if (!entry.getAppearance().showInFile) return; if (entry.getTrueValue() == null) throw new IllegalArgumentException("Entry [" + entry.getNameWCategory() + "] is null, this may be a problem with [" + configBase.modName + "]. Please contact the authors"); - Class originalClass = ConfigTypeConverters.isClassConvertable(entry.getType()); - if (originalClass != null) - { - workConfig.set(entry.getNameWCategory(), ConfigTypeConverters.convertToString(originalClass, entry.getTrueValue())); - return; - } - workConfig.set(entry.getNameWCategory(), entry.getTrueValue()); + workConfig.set(entry.getNameWCategory(), ConfigTypeConverters.attemptToConvertToString(entry.getType(), entry.getTrueValue())); } - // Loads an entry when only given the entry + /** Loads an entry when only given the entry */ public void loadEntry(ConfigEntry entry) { - CommentedFileConfig config = CommentedFileConfig.builder(configPath.toFile()).autosave().build(); - loadConfig(config); - loadEntry(entry, config); - config.close(); - + loadEntry(entry, nightConfig); } - // Loads an entry - @SuppressWarnings("unchecked") // Suppress due to its always safe - public void loadEntry(ConfigEntry entry, CommentedFileConfig workConfig) + /** Loads an entry */ + @SuppressWarnings("unchecked") + public void loadEntry(ConfigEntry entry, CommentedFileConfig nightConfig) { - if (!entry.getAppearance().showInFile) return; + if (!entry.getAppearance().showInFile) + return; - if (workConfig.contains(entry.getNameWCategory())) + if (!nightConfig.contains(entry.getNameWCategory())) { - try + saveEntry(entry, nightConfig); + return; + } + + + try + { + if (entry.getType().isEnum()) { - if (entry.getType().isEnum()) - { - entry.pureSet((T) (workConfig.getEnum(entry.getNameWCategory(), (Class) entry.getType()))); - return; - } - Class originalClass = ConfigTypeConverters.isClassConvertable(entry.getType()); - if (originalClass != null) - { - entry.pureSet((T) ConfigTypeConverters.convertFromString(originalClass, workConfig.get(entry.getNameWCategory()))); - return; - } - - if (entry.getType() == workConfig.get(entry.getNameWCategory()).getClass()) - { // If the types are the same - entry.pureSet((T) workConfig.get(entry.getNameWCategory())); - entry.clampWithinRange(); - return; - } - - LOGGER.warn("Entry [" + entry.getNameWCategory() + "] is invalid. Expected " + entry.getType() + " but got " + workConfig.get(entry.getNameWCategory()).getClass() + ". Using default value."); - saveEntry(entry, workConfig); + entry.pureSet((T) (nightConfig.getEnum(entry.getNameWCategory(), (Class) entry.getType()))); + return; } - catch (Exception e) - { -// e.printStackTrace(); - LOGGER.warn("Entry [" + entry.getNameWCategory() + "] had an invalid value when loading the config. Using default value."); - saveEntry(entry, workConfig); + + entry.pureSet((T) ConfigTypeConverters.attemptToConvertFromString(entry.getType(), nightConfig.get(entry.getNameWCategory()))); + + if (entry.getTrueValue() == null) { + LOGGER.warn("Entry [" + entry.getNameWCategory() + "] returned as null from the config. Using default value."); + entry.pureSet(entry.getDefaultValue()); } } - else + catch (Exception e) { - saveEntry(entry, workConfig); +// e.printStackTrace(); + LOGGER.warn("Entry [" + entry.getNameWCategory() + "] had an invalid value when loading the config. Using default value."); + entry.pureSet(entry.getDefaultValue()); } } // Creates the comment for an entry when only given the entry public void createComment(ConfigEntry entry) { - CommentedFileConfig config = CommentedFileConfig.builder(configPath.toFile()).autosave().build(); - loadConfig(config); - createComment(entry, config); - config.close(); + createComment(entry, nightConfig); } // Creates a comment for an entry - public void createComment(ConfigEntry entry, CommentedFileConfig workConfig) + public void createComment(ConfigEntry entry, CommentedFileConfig nightConfig) { - if (!entry.getAppearance().showInFile) + if ( + !entry.getAppearance().showInFile || + entry.getComment() == null + ) return; - if (entry.getComment() != null) - { - workConfig.setComment(entry.getNameWCategory(), " " + entry.getComment().replaceAll("\n", "\n ") + "\n "); - } + + nightConfig.setComment(entry.getNameWCategory(), " " + entry.getComment().replaceAll("\n", "\n ") + "\n "); } + /** - * Does config.load(); but with error checking + * Uses {@link ConfigFileHandling#nightConfig} to do {@link CommentedFileConfig#load()} but with error checking * * @apiNote This overwrites any value currently stored in the config */ - public void loadConfig(CommentedFileConfig config) + public void loadNightConfig() + { + loadNightConfig(this.nightConfig); + } + /** + * Does {@link CommentedFileConfig#load()} but with error checking + * + * @apiNote This overwrites any value currently stored in the config + */ + public void loadNightConfig(CommentedFileConfig nightConfig) { try { - config.load(); - } - catch (Exception e) - { - System.out.println("Loading file failed because of this expectation:\n" + e); try - { // Now try remaking the file and loading it - if (!this.configPath.getParent().toFile().exists()) - { - Files.createDirectory(this.configPath.getParent()); - } - - boolean fileDeleted = Files.deleteIfExists(this.configPath); - System.out.println("File at [" + this.configPath + "] was " + (fileDeleted ? "" : "not ") + "able to be deleted."); - - - Files.createFile(this.configPath); - - config.load(); - } - catch (IOException ex) { - System.out.println("Creating file failed"); - ex.printStackTrace(); - SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Loading file and resetting config file failed at path [" + configPath + "]. Please check the file is ok and you have the permissions", ex); + if (!Files.exists(this.configPath)) + Files.createFile(this.configPath); + nightConfig.load(); } + catch (Exception e) + { + LOGGER.warn("Loading file failed because of this expectation:\n" + e); + + reCreateFile(this.configPath); + + nightConfig.load(); + } + } + catch (Exception ex) + { + System.out.println("Creating file failed"); + LOGGER.error(ex); + SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).crashMinecraft("Loading file and resetting config file failed at path [" + configPath + "]. Please check the file is ok and you have the permissions", ex); } } + + public static void reCreateFile(Path path) + { + try + { + Files.deleteIfExists(path); + + if (!path.getParent().toFile().exists()) + { + Files.createDirectory(path.getParent()); + } + Files.createFile(path); + } + catch (IOException ex) + { + ex.printStackTrace(); + } + } + + + + // ========== API (server) STUFF ========== // - /** ALWAYS CLEAR WHEN NOT ON SERVER!!!! */ + /* * ALWAYS CLEAR WHEN NOT ON SERVER!!!! */ // We are not using this stuff, so comment it out for now (if we ever do need it then we can uncomment it) /* @SuppressWarnings("unchecked") diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigTypeConverters.java b/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigTypeConverters.java index 32b6e5d1e..e35359770 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigTypeConverters.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/file/ConfigTypeConverters.java @@ -37,6 +37,32 @@ public class ConfigTypeConverters return null; } + public static Object attemptToConvertToString(Object value) + { + return attemptToConvertToString(value.getClass(), value); + } + public static Object attemptToConvertToString(Class clazz, Object value) + { + Class convertablClass = isClassConvertable(clazz); + if (convertablClass != null) { + return convertToString(convertablClass, value); + } + return value; + } + + public static Object attemptToConvertFromString(Object value) + { + return attemptToConvertFromString(value.getClass(), value); + } + public static Object attemptToConvertFromString(Class clazz, Object value) + { + Class convertablClass = isClassConvertable(clazz); + if (convertablClass != null) { + return convertFromString(convertablClass, (String) value); + } + return value; + } + public static String convertToString(Class clazz, Object value) { try diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/AbstractConfigType.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/AbstractConfigType.java index 40a709245..f7aaa3b15 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/AbstractConfigType.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/AbstractConfigType.java @@ -13,6 +13,7 @@ public abstract class AbstractConfigType { public String category = ""; // This should only be set once in the init public String name; // This should only be set once in the init + protected final T defaultValue; protected T value; public ConfigBase configBase; @@ -22,8 +23,9 @@ public abstract class AbstractConfigType protected AbstractConfigType(EConfigEntryAppearance appearance, T value) { - this.appearance = appearance; + this.defaultValue = value; this.value = value; + this.appearance = appearance; } @@ -65,7 +67,7 @@ public abstract class AbstractConfigType // Gets the class of T public Class getType() { - return value.getClass(); + return this.defaultValue.getClass(); } protected static abstract class Builder diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java index cf3a820d1..2fbd85471 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/ConfigEntry.java @@ -19,7 +19,6 @@ import java.util.Arrays; */ public class ConfigEntry extends AbstractConfigType> implements IConfigEntry { - private final T defaultValue; private String comment; private T min; private T max; @@ -41,7 +40,6 @@ public class ConfigEntry extends AbstractConfigType> implem { super(appearance, value); - this.defaultValue = value; this.comment = comment; this.min = min; this.max = max; @@ -54,7 +52,7 @@ public class ConfigEntry extends AbstractConfigType> implem /** Gets the default value of the option */ @Override - public T getDefaultValue() { return this.defaultValue; } + public T getDefaultValue() { return super.defaultValue; } @Override public void setApiValue(T newApiValue) @@ -70,7 +68,7 @@ public class ConfigEntry extends AbstractConfigType> implem /** * DONT USE THIS IN YOUR CODE
* Sets the value without informing the rest of the code (ie, doesnt call listeners, or saves the value).
- * Should only be used when loading the config from the file + * Should only be used when loading the config from the file (in places like the {@link com.seibel.distanthorizons.core.config.file.ConfigFileHandling} or {@link com.seibel.distanthorizons.core.config.ConfigBase}) */ public void pureSet(T newValue) { super.set(newValue); From 9e43076853b7c20c0b58f003411b7703294b17f1 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 26 Aug 2023 08:51:20 -0500 Subject: [PATCH 19/19] Add OpenGL error log config --- .../enums/config/EGLErrorHandlingMode.java | 30 +++++++++ .../distanthorizons/core/config/Config.java | 20 ++++++ .../core/render/glObject/GLProxy.java | 61 ++++++++++++------- .../assets/distanthorizons/lang/en_us.json | 18 +++++- 4 files changed, 106 insertions(+), 23 deletions(-) create mode 100644 api/src/main/java/com/seibel/distanthorizons/api/enums/config/EGLErrorHandlingMode.java diff --git a/api/src/main/java/com/seibel/distanthorizons/api/enums/config/EGLErrorHandlingMode.java b/api/src/main/java/com/seibel/distanthorizons/api/enums/config/EGLErrorHandlingMode.java new file mode 100644 index 000000000..6fb4c7d99 --- /dev/null +++ b/api/src/main/java/com/seibel/distanthorizons/api/enums/config/EGLErrorHandlingMode.java @@ -0,0 +1,30 @@ +/* + * This file is part of the Distant Horizons mod (formerly the LOD Mod), + * licensed under the GNU LGPL v3 License. + * + * Copyright (C) 2020-2022 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.api.enums.config; + +/** + * @since API 1.0.0 + */ +public enum EGLErrorHandlingMode +{ + IGNORE, + LOG, + LOG_THROW; +} 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 4b4696a56..e15885a01 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 @@ -1093,6 +1093,26 @@ public class Config .addListener(UnsafeValuesConfigListener.INSTANCE) .build(); + public static ConfigEntry overrideVanillaGLLogger = new ConfigEntry.Builder() + .set(ModInfo.IS_DEV_BUILD) + .comment("" + + "Requires a reboot to change. \n" + + "") + .build(); + + public static ConfigEntry glErrorHandlingMode = new ConfigEntry.Builder() + .set(ModInfo.IS_DEV_BUILD ? EGLErrorHandlingMode.LOG : EGLErrorHandlingMode.IGNORE) + .comment("" + + "Defines how OpenGL errors are handled. \n" + + "May incorrectly catch OpenGL errors thrown by other mods. \n" + + "\n" + + EGLErrorHandlingMode.IGNORE + ": Do nothing. \n" + + EGLErrorHandlingMode.LOG + ": write an error to the log. \n" + + EGLErrorHandlingMode.LOG_THROW + ": write to the log and throw an exception. \n" + + " Warning: this should only be enabled when debugging the LOD renderer \n" + + " as it may break Minecraft's renderer when an exception is thrown. \n" + + "") + .build(); // can be set to public inorder to show in the config file and UI public static ConfigCategory exampleConfigScreen = new ConfigCategory.Builder() 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 f5d935993..b30b40894 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 @@ -25,6 +25,7 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; +import com.seibel.distanthorizons.api.enums.config.EGLErrorHandlingMode; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; import com.seibel.distanthorizons.core.config.Config; @@ -65,8 +66,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftCli */ public class GLProxy { - public static final boolean OVERRIDE_VANILLA_GL_LOGGER = ModInfo.IS_DEV_BUILD; - private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private ExecutorService workerThread = Executors.newSingleThreadExecutor(new ThreadFactoryBuilder().setNameFormat(GLProxy.class.getSimpleName() + "-Worker-Thread").build()); @@ -141,7 +140,7 @@ public class GLProxy } GL_LOGGER.info("minecraftGlCapabilities:\n" + getVersionInfo(minecraftGlCapabilities)); - if (OVERRIDE_VANILLA_GL_LOGGER) + if (Config.Client.Advanced.Debugging.overrideVanillaGLLogger.get()) { GLUtil.setupDebugMessageCallback(new PrintStream(new GLMessageOutputStream(GLProxy::logMessage, vanillaDebugMessageBuilder), true)); } @@ -461,29 +460,47 @@ public class GLProxy private static void logMessage(GLMessage msg) { - GLMessage.ESeverity s = msg.severity; - if (msg.type == GLMessage.EType.ERROR || - msg.type == GLMessage.EType.UNDEFINED_BEHAVIOR) + EGLErrorHandlingMode errorHandlingMode = Config.Client.Advanced.Debugging.glErrorHandlingMode.get(); + if (errorHandlingMode == EGLErrorHandlingMode.IGNORE) { - GL_LOGGER.error("GL ERROR {} from {}: {}", msg.id, msg.source, msg.message); - throw new RuntimeException("GL ERROR: " + msg.toString()); + return; } - RuntimeException e = new RuntimeException("GL MESSAGE: " + msg.toString()); - switch (s) + + + if (msg.type == GLMessage.EType.ERROR || msg.type == GLMessage.EType.UNDEFINED_BEHAVIOR) { - case HIGH: - GL_LOGGER.error("{}", e); - break; - case MEDIUM: - GL_LOGGER.warn("{}", e); - break; - case LOW: - GL_LOGGER.info("{}", e); - break; - case NOTIFICATION: - GL_LOGGER.debug("{}", e); - break; + // critical error + + GL_LOGGER.error("GL ERROR " + msg.id + " from " + msg.source + ": " + msg.message); + + if (errorHandlingMode == EGLErrorHandlingMode.LOG_THROW) + { + throw new RuntimeException("GL ERROR: " + msg); + } + + } + else + { + // non-critical log + + GLMessage.ESeverity severity = msg.severity; + RuntimeException ex = new RuntimeException("GL MESSAGE: " + msg); + switch (severity) + { + case HIGH: + GL_LOGGER.error("{}", ex); + break; + case MEDIUM: + GL_LOGGER.warn("{}", ex); + break; + case LOW: + GL_LOGGER.info("{}", ex); + break; + case NOTIFICATION: + GL_LOGGER.debug("{}", ex); + break; + } } } 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 49441406b..bd549717e 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -388,7 +388,16 @@ "Allow Unsafe UI Values", "distanthorizons.config.client.advanced.debugging.allowUnsafeValues.@tooltip": "If enabled, very limited config input validation will be performed. \n\nWarning: enabling this can cause instability or crashing, use at your own risk. \nNote: this is option isn't saved between sessions.", - + "distanthorizons.config.client.advanced.debugging.overrideVanillaGLLogger": + "Override Vanilla OpenGL Logger", + "distanthorizons.config.client.advanced.debugging.overrideVanillaGLLogger.@tooltip": + "Requires a reboot to change.", + "distanthorizons.config.client.advanced.debugging.glErrorHandlingMode": + "OpenGL Error Handling Mode", + "distanthorizons.config.client.advanced.debugging.glErrorHandlingMode.@tooltip": + "Defines how OpenGL errors are handled. \nMay incorrectly catch OpenGL errors thrown by other mods.", + + "distanthorizons.config.client.advanced.buffers": "Buffers", "distanthorizons.config.client.advanced.buffers.gpuUploadMethod": @@ -687,6 +696,13 @@ "distanthorizons.config.enum.EDebugRendering.SHOW_RENDER_SOURCE_FLAG": "Show render source flag", + "distanthorizons.config.enum.EGLErrorHandlingMode.IGNORE": + "Ignore", + "distanthorizons.config.enum.EGLErrorHandlingMode.LOG": + "Log", + "distanthorizons.config.enum.EGLErrorHandlingMode.LOG_THROW": + "Log-Throw", + "distanthorizons.config.enum.ELoggerMode.DISABLED": "Disabled", "distanthorizons.config.enum.ELoggerMode.LOG_ALL_TO_FILE":