From 415f0bb8669641254b8f58c8f1a655cf7ec11edd Mon Sep 17 00:00:00 2001 From: James Seibel Date: Thu, 31 Oct 2024 20:28:43 -0500 Subject: [PATCH 01/26] Remove unneeded ClientApi events --- .../core/api/internal/ClientApi.java | 22 ---------- .../core/render/renderer/TestRenderer.java | 40 ++++++++++--------- 2 files changed, 22 insertions(+), 40 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 e34b99959..0abcede4e 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 @@ -312,28 +312,6 @@ public class ClientApi // render events // //===============// - public void rendererShutdownEvent() - { - LOGGER.info("Renderer shutting down."); - - IProfilerWrapper profiler = MC_CLIENT.getProfiler(); - profiler.push("DH-RendererShutdown"); - - profiler.pop(); - } - - public void rendererStartupEvent() - { - LOGGER.info("Renderer starting up."); - - IProfilerWrapper profiler = MC_CLIENT.getProfiler(); - profiler.push("DH-RendererStartup"); - - // make sure the GLProxy is created before the LodBufferBuilder needs it - GLProxy.getInstance(); - profiler.pop(); - } - public void clientTickEvent() { IProfilerWrapper profiler = MC_CLIENT.getProfiler(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/TestRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/TestRenderer.java index 9050f3083..0d423ced2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/TestRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/TestRenderer.java @@ -58,19 +58,23 @@ public class TestRenderer public void init() { - if (init) return; + if (this.init) + { + return; + } + logger.info("init"); - init = true; - va = AbstractVertexAttribute.create(); - va.bind(); + this.init = true; + this.va = AbstractVertexAttribute.create(); + this.va.bind(); // Pos - va.setVertexAttribute(0, 0, VertexPointer.addVec2Pointer(false)); + this.va.setVertexAttribute(0, 0, VertexPointer.addVec2Pointer(false)); // Color - va.setVertexAttribute(0, 1, VertexPointer.addVec4Pointer(false)); - va.completeAndCheck(Float.BYTES * 6); - basicShader = new ShaderProgram("shaders/test/vert.vert", "shaders/test/frag.frag", + this.va.setVertexAttribute(0, 1, VertexPointer.addVec4Pointer(false)); + this.va.completeAndCheck(Float.BYTES * 6); + this.basicShader = new ShaderProgram("shaders/test/vert.vert", "shaders/test/frag.frag", "fragColor", new String[]{"vPosition", "color"}); - createBuffer(); + this.createBuffer(); } // Render a square with uv color @@ -97,8 +101,8 @@ public class TestRenderer private void createBuffer() { - sharedContextBuffer = createTextingBuffer(); - sameContextBuffer = createTextingBuffer(); + this.sharedContextBuffer = createTextingBuffer(); + this.sameContextBuffer = createTextingBuffer(); } public void render() @@ -106,7 +110,7 @@ public class TestRenderer spamLogger.debug("rendering"); GLState state = new GLState(); - init(); + this.init(); GL32.glBindFramebuffer(GL32.GL_FRAMEBUFFER, MC_RENDER.getTargetFrameBuffer()); GL32.glViewport(0, 0, MC_RENDER.getTargetFrameBufferViewportWidth(), MC_RENDER.getTargetFrameBufferViewportHeight()); GL32.glPolygonMode(GL32.GL_FRONT_AND_BACK, GL32.GL_FILL); @@ -116,20 +120,20 @@ public class TestRenderer GL32.glDisable(GL32.GL_BLEND); //GL32.glDisable(GL32.GL_SCISSOR_TEST); - basicShader.bind(); - va.bind(); + this.basicShader.bind(); + this.va.bind(); // Switch between the two buffers per second if (System.currentTimeMillis() % 2000 < 1000) { - sameContextBuffer.bind(); - va.bindBufferToAllBindingPoints(sameContextBuffer.getId()); + this.sameContextBuffer.bind(); + this.va.bindBufferToAllBindingPoints(this.sameContextBuffer.getId()); spamLogger.debug("same context buffer"); } else { - sameContextBuffer.bind(); - va.bindBufferToAllBindingPoints(sharedContextBuffer.getId()); + this.sameContextBuffer.bind(); + this.va.bindBufferToAllBindingPoints(this.sharedContextBuffer.getId()); spamLogger.debug("shared context buffer"); } // Render the square From 5da0314556597668c7ae5449244d2bca6ab705ed Mon Sep 17 00:00:00 2001 From: James Seibel Date: Fri, 1 Nov 2024 18:05:33 -0500 Subject: [PATCH 02/26] Rename DummyRunExecutorService -> RunOnThisThreadExecutorService --- ...va => RunOnThisThreadExecutorService.java} | 46 +++++++++---------- 1 file changed, 23 insertions(+), 23 deletions(-) rename core/src/main/java/com/seibel/distanthorizons/core/util/objects/{DummyRunExecutorService.java => RunOnThisThreadExecutorService.java} (79%) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/DummyRunExecutorService.java b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/RunOnThisThreadExecutorService.java similarity index 79% rename from core/src/main/java/com/seibel/distanthorizons/core/util/objects/DummyRunExecutorService.java rename to core/src/main/java/com/seibel/distanthorizons/core/util/objects/RunOnThisThreadExecutorService.java index 407cf3673..f9e84d197 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/objects/DummyRunExecutorService.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/objects/RunOnThisThreadExecutorService.java @@ -19,6 +19,8 @@ package com.seibel.distanthorizons.core.util.objects; +import org.jetbrains.annotations.NotNull; + import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -30,45 +32,43 @@ import java.util.concurrent.ExecutorService; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; -public class DummyRunExecutorService implements ExecutorService +/** + * This runs the given {@link Runnable}'s on the same + * thread as they're queued. + * This can be used to change MC threads to run on DH threads. + * This is done to prevent concurrent issues with world generation + * and reduce putting undue load on MC threads. + */ +public class RunOnThisThreadExecutorService implements ExecutorService { private boolean shutdownCalled = false; - @Override - public void execute(Runnable command) - { - command.run(); - } + @Override - public void shutdown() - { - shutdownCalled = true; - } + public void execute(Runnable command) { command.run(); } @Override + public void shutdown() { this.shutdownCalled = true; } + + @Override + @NotNull public List shutdownNow() { - shutdownCalled = true; - return new ArrayList(); + this.shutdownCalled = true; + return new ArrayList<>(); } @Override - public boolean isShutdown() - { - return shutdownCalled; - } + public boolean isShutdown() { return this.shutdownCalled; } @Override - public boolean isTerminated() - { - return shutdownCalled; - } + public boolean isTerminated() { return this.shutdownCalled; } @Override public boolean awaitTermination(long timeout, TimeUnit unit) { - shutdownCalled = true; + this.shutdownCalled = true; return true; } @@ -119,7 +119,7 @@ public class DummyRunExecutorService implements ExecutorService List> futures = new ArrayList>(tasks.size()); for (Callable t : tasks) { - futures.add(submit(t)); + futures.add(this.submit(t)); } return futures; } @@ -151,7 +151,7 @@ public class DummyRunExecutorService implements ExecutorService @Override public T invokeAny(Collection> tasks, long timeout, TimeUnit unit) throws ExecutionException { - return invokeAny(tasks); + return this.invokeAny(tasks); } } \ No newline at end of file From 072fc0cb66d6e559f660f66f48d6cdcc8ddd5c43 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Fri, 1 Nov 2024 18:06:17 -0500 Subject: [PATCH 03/26] remove unused part of RenderUtil.shouldLodsRender() --- .../com/seibel/distanthorizons/core/util/RenderUtil.java | 8 -------- 1 file changed, 8 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java index 054cc8936..37dc6957f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/RenderUtil.java @@ -189,14 +189,6 @@ public class RenderUtil return false; //Level is not ready yet. } - /* if (MC_RENDER.isFogStateSpecial()) - { - // if the player is blind/under-water, don't render LODs, - // and don't change minecraft's fog - // which blindness relies on. - return false; - } */ - if (MC_RENDER.getLightmapWrapper(levelWrapper) == null) { return false; From 7e88ec4cc1e9fbea049a2bc840bb879927814e7d Mon Sep 17 00:00:00 2001 From: James Seibel Date: Fri, 1 Nov 2024 18:07:42 -0500 Subject: [PATCH 04/26] Improve ChunkLightStorage error logging --- .../wrapperInterfaces/chunk/ChunkLightStorage.java | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/ChunkLightStorage.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/ChunkLightStorage.java index a17c8b06d..90f6de38a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/ChunkLightStorage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/ChunkLightStorage.java @@ -106,10 +106,18 @@ public class ChunkLightStorage if (this.lightSections != null) { - LightSection lightSection = this.lightSections[BitShiftUtil.divideByPowerOfTwo(y - this.minY, 4)]; - if (lightSection != null) + int sectionIndex = BitShiftUtil.divideByPowerOfTwo(y - this.minY, 4); + try { - return lightSection.get(x, y, z); + LightSection lightSection = this.lightSections[sectionIndex]; + if (lightSection != null) + { + return lightSection.get(x, y, z); + } + } + catch (IndexOutOfBoundsException e) + { + throw new IndexOutOfBoundsException("Failed to get light at x:["+x+"], y:["+y+"], z:["+z+"], index:["+sectionIndex+"]. MinY:["+this.minY+"], maxY:["+this.maxY+"], section length:["+this.lightSections.length+"]. Original error: ["+e.getMessage()+"]."); } } From 6fccaab8414d134f2edcad9f01137fd23a8aa2d7 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 2 Nov 2024 11:19:10 -0500 Subject: [PATCH 05/26] Fix potential RejectedExecutionException error in ServerLevel --- .../core/level/AbstractDhServerLevel.java | 54 +++++++++++-------- 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java index c8873f338..dfb21ed31 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java @@ -425,36 +425,44 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I LOGGER.warn("Unable to send FullDataPartialUpdateMessage - getNetworkCompressionExecutor() is null"); return this.getFullDataProvider().updateDataSourceAsync(data); } - CompletableFuture.runAsync(() -> + + try { - Objects.requireNonNull(this.beaconBeamRepo); - try (FullDataPayload payload = new FullDataPayload(data, this.beaconBeamRepo.getAllBeamsForPos(data.getPos()))) + CompletableFuture.runAsync(() -> { - for (ServerPlayerState serverPlayerState : this.serverPlayerStateManager.getReadyPlayers()) + Objects.requireNonNull(this.beaconBeamRepo); + try (FullDataPayload payload = new FullDataPayload(data, this.beaconBeamRepo.getAllBeamsForPos(data.getPos()))) { - if (serverPlayerState.getServerPlayer().getLevel() != this.serverLevelWrapper) + for (ServerPlayerState serverPlayerState : this.serverPlayerStateManager.getReadyPlayers()) { - continue; - } - - if (!serverPlayerState.sessionConfig.isRealTimeUpdatesEnabled()) - { - continue; - } - - Vec3d playerPosition = serverPlayerState.getServerPlayer().getPosition(); - int distanceFromPlayer = DhSectionPos.getChebyshevBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; - if (distanceFromPlayer >= serverPlayerState.getServerPlayer().getViewDistance() - && distanceFromPlayer <= serverPlayerState.sessionConfig.getMaxUpdateDistanceRadius()) - { - serverPlayerState.fullDataPayloadSender.sendInChunks(payload, () -> + if (serverPlayerState.getServerPlayer().getLevel() != this.serverLevelWrapper) { - serverPlayerState.networkSession.sendMessage(new FullDataPartialUpdateMessage(this.serverLevelWrapper, payload)); - }); + continue; + } + + if (!serverPlayerState.sessionConfig.isRealTimeUpdatesEnabled()) + { + continue; + } + + Vec3d playerPosition = serverPlayerState.getServerPlayer().getPosition(); + int distanceFromPlayer = DhSectionPos.getChebyshevBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; + if (distanceFromPlayer >= serverPlayerState.getServerPlayer().getViewDistance() + && distanceFromPlayer <= serverPlayerState.sessionConfig.getMaxUpdateDistanceRadius()) + { + serverPlayerState.fullDataPayloadSender.sendInChunks(payload, () -> + { + serverPlayerState.networkSession.sendMessage(new FullDataPartialUpdateMessage(this.serverLevelWrapper, payload)); + }); + } } } - } - }, executor); + }, executor); + } + catch (RejectedExecutionException ignore) + { + // the executor was shut down, it should be back up shortly and able to accept new jobs + } return this.getFullDataProvider().updateDataSourceAsync(data); From b7fccae64d26f0386be51dac8861967dcd587920 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 2 Nov 2024 11:19:29 -0500 Subject: [PATCH 06/26] Prep for MC 1.21.3 support --- .../transformers/LodDataBuilder.java | 14 +++++------ .../core/generation/DhLightingEngine.java | 4 ++-- .../distanthorizons/core/util/ColorUtil.java | 13 ++++++----- .../chunk/ChunkLightStorage.java | 7 +++--- .../chunk/IChunkWrapper.java | 23 ++++++++++--------- 5 files changed, 32 insertions(+), 29 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java index f67c97862..8e2edcd90 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/LodDataBuilder.java @@ -141,7 +141,7 @@ public class LodDataBuilder for (int relBlockZ = 0; relBlockZ < LodUtil.CHUNK_WIDTH; relBlockZ++) { LongArrayList longs = new LongArrayList(chunkWrapper.getHeight() / 4); - int lastY = chunkWrapper.getMaxBuildHeight(); + int lastY = chunkWrapper.getExclusiveMaxBuildHeight(); IBiomeWrapper biome = chunkWrapper.getBiome(relBlockX, lastY, relBlockZ); IBlockStateWrapper blockState = AIR; int mappedId = dataSource.mapping.addIfNotPresentAndGetId(biome, blockState); @@ -149,7 +149,7 @@ public class LodDataBuilder byte blockLight; byte skyLight; - if (lastY < chunkWrapper.getMaxBuildHeight()) + if (lastY < chunkWrapper.getExclusiveMaxBuildHeight()) { // FIXME: The lastY +1 offset is to reproduce the old behavior. Remove this when we get per-face lighting blockLight = (byte) chunkWrapper.getDhBlockLight(relBlockX, lastY + 1, relBlockZ); @@ -167,7 +167,7 @@ public class LodDataBuilder int y = chunkWrapper.getLightBlockingHeightMapValue(relBlockX, relBlockZ); // go up until we reach open air or the world limit IBlockStateWrapper topBlockState = previousBlockState = chunkWrapper.getBlockState(relBlockX, y, relBlockZ, mcBlockPos, previousBlockState); - while (!topBlockState.isAir() && y < chunkWrapper.getMaxBuildHeight()) + while (!topBlockState.isAir() && y < chunkWrapper.getExclusiveMaxBuildHeight()) { try { @@ -180,7 +180,7 @@ public class LodDataBuilder { if (!getTopErrorLogged) { - LOGGER.warn("Unexpected issue in LodDataBuilder, future errors won't be logged. Chunk [" + chunkWrapper.getChunkPos() + "] with max height: [" + chunkWrapper.getMaxBuildHeight() + "] had issue getting block at pos [" + relBlockX + "," + y + "," + relBlockZ + "] error: " + e.getMessage(), e); + LOGGER.warn("Unexpected issue in LodDataBuilder, future errors won't be logged. Chunk [" + chunkWrapper.getChunkPos() + "] with max height: [" + chunkWrapper.getExclusiveMaxBuildHeight() + "] had issue getting block at pos [" + relBlockX + "," + y + "," + relBlockZ + "] error: " + e.getMessage(), e); getTopErrorLogged = true; } @@ -208,7 +208,7 @@ public class LodDataBuilder // check if this block is visible from any direction || blockVisible(chunkWrapper, relBlockX, y, relBlockZ)) { - longs.add(FullDataPointUtil.encode(mappedId, lastY - y, y + 1 - chunkWrapper.getMinBuildHeight(), blockLight, skyLight)); + longs.add(FullDataPointUtil.encode(mappedId, lastY - y, y + 1 - chunkWrapper.getInclusiveMinBuildHeight(), blockLight, skyLight)); biome = newBiome; blockState = newBlockState; @@ -219,7 +219,7 @@ public class LodDataBuilder } } } - longs.add(FullDataPointUtil.encode(mappedId, lastY - y, y + 1 - chunkWrapper.getMinBuildHeight(), blockLight, skyLight)); + longs.add(FullDataPointUtil.encode(mappedId, lastY - y, y + 1 - chunkWrapper.getInclusiveMinBuildHeight(), blockLight, skyLight)); dataSource.setSingleColumn(longs, relBlockX + chunkOffsetX, @@ -289,7 +289,7 @@ public class LodDataBuilder { return true; } - if (testBlockPos.getY() < chunkWrapper.getMinBuildHeight() || testBlockPos.getY() > chunkWrapper.getMaxBuildHeight()) + if (testBlockPos.getY() < chunkWrapper.getInclusiveMinBuildHeight() || testBlockPos.getY() > chunkWrapper.getExclusiveMaxBuildHeight()) { return true; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java index bfb18c5fa..8a1584a73 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/DhLightingEngine.java @@ -204,7 +204,7 @@ public class DhLightingEngine IBlockStateWrapper previousBlockState = null; int maxY = chunk.getMaxNonEmptyHeight(); - int minY = chunk.getMinBuildHeight(); + int minY = chunk.getInclusiveMinBuildHeight(); // get the adjacent chunk's sky lights for (int relX = 0; relX < LodUtil.CHUNK_WIDTH; relX++) // relative block pos @@ -322,7 +322,7 @@ public class DhLightingEngine continue; } - if (relNeighbourBlockPos.getY() < neighbourChunk.getMinNonEmptyHeight() || relNeighbourBlockPos.getY() > neighbourChunk.getMaxBuildHeight()) + if (relNeighbourBlockPos.getY() < neighbourChunk.getMinNonEmptyHeight() || relNeighbourBlockPos.getY() > neighbourChunk.getExclusiveMaxBuildHeight()) { // the light pos is outside the chunk's min/max height, // this can happen if given a chunk that hasn't finished generating diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java index 2e18c15f1..cc1a5a0dd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/ColorUtil.java @@ -23,18 +23,19 @@ import java.awt.*; /** * Handles the bit-wise math used when - * dealing with colors stored as integers. + * dealing with colors stored as integers.

* + * Minecraft color format is: 0xAA BB GG RR
+ * DH mod color format is: 0xAA RR GG BB
+ * OpenGL RGBA format native order: 0xRR GG BB AA
+ * OpenGL RGBA format Java Order: 0xAA BB GG RR
+ * * @author Cola * @author Leonardo Amato * @version 2023-5-15 */ public class ColorUtil { - //note: Minecraft color format is: 0xAA BB GG RR - //________ DH mod color format is: 0xAA RR GG BB - //OpenGL RGBA format native order: 0xRR GG BB AA - //_ OpenGL RGBA format Java Order: 0xAA BB GG RR public static final int INVISIBLE = argbToInt(0, 0, 0, 0); @@ -215,7 +216,7 @@ public class ColorUtil } public static Color toColorObjRGB(int color) { return new Color(getRed(color), getGreen(color), getBlue(color)); } - public static Color toColorObjRGBA(int color) { return new Color(getRed(color), getGreen(color), getBlue(color), getAlpha(color)); } + public static Color toColorObjARGB(int color) { return new Color(getRed(color), getGreen(color), getBlue(color), getAlpha(color)); } public static int toColorInt(Color color) { return argbToInt(color.getAlpha(), color.getRed(), color.getGreen(), color.getBlue()); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/ChunkLightStorage.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/ChunkLightStorage.java index 90f6de38a..5d60cf2cd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/ChunkLightStorage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/ChunkLightStorage.java @@ -60,7 +60,7 @@ public class ChunkLightStorage // constructor // //=============// - public static ChunkLightStorage createSkyLightStorage(IChunkWrapper chunkWrapper) { return createSkyLightStorage(chunkWrapper.getMinBuildHeight(), chunkWrapper.getMaxBuildHeight()); } + public static ChunkLightStorage createSkyLightStorage(IChunkWrapper chunkWrapper) { return createSkyLightStorage(chunkWrapper.getInclusiveMinBuildHeight(), chunkWrapper.getExclusiveMaxBuildHeight()); } public static ChunkLightStorage createSkyLightStorage(int minY, int maxY) { return new ChunkLightStorage( @@ -68,7 +68,7 @@ public class ChunkLightStorage // positions above should be lit but positions below should be unlit LodUtil.MAX_MC_LIGHT, LodUtil.MIN_MC_LIGHT); } - public static ChunkLightStorage createBlockLightStorage(IChunkWrapper chunkWrapper) { return createBlockLightStorage(chunkWrapper.getMinBuildHeight(), chunkWrapper.getMaxBuildHeight()); } + public static ChunkLightStorage createBlockLightStorage(IChunkWrapper chunkWrapper) { return createBlockLightStorage(chunkWrapper.getInclusiveMinBuildHeight(), chunkWrapper.getExclusiveMaxBuildHeight()); } public static ChunkLightStorage createBlockLightStorage(int minY, int maxY) { return new ChunkLightStorage( @@ -134,7 +134,8 @@ public class ChunkLightStorage //populate array if it doesn't exist. if (this.lightSections == null) { - this.lightSections = new LightSection[BitShiftUtil.divideByPowerOfTwo(this.maxY - this.minY, 4)]; + int arrayLength = (this.maxY - this.minY) / 16; + this.lightSections = new LightSection[arrayLength]; } int index = (y - this.minY) >> 4; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java index c615d4e96..8e27aa719 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java @@ -27,7 +27,6 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IMutableBlockPosWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; import com.seibel.distanthorizons.core.util.LodUtil; @@ -47,18 +46,20 @@ public interface IChunkWrapper extends IBindable DhChunkPos getChunkPos(); - default int getHeight() { return this.getMaxBuildHeight() - this.getMinBuildHeight(); } - int getMinBuildHeight(); - int getMaxBuildHeight(); + default int getHeight() { return this.getExclusiveMaxBuildHeight() - this.getInclusiveMinBuildHeight(); } + /** inclusive (IE if returning -64 the min block can be placed at -64) */ + int getInclusiveMinBuildHeight(); + /** exclusive (IE if returning 320 the max block can be placed at 319) */ + int getExclusiveMaxBuildHeight(); /** * returns the Y level for the last non-empty section in this chunk, - * or {@link IChunkWrapper#getMinBuildHeight()} if this chunk is completely empty. + * or {@link IChunkWrapper#getInclusiveMinBuildHeight()} if this chunk is completely empty. */ int getMinNonEmptyHeight(); /** * returns the Y level for the first non-empty section in this chunk, - * or {@link IChunkWrapper#getMaxBuildHeight()} if this chunk is completely empty. + * or {@link IChunkWrapper#getExclusiveMaxBuildHeight()} if this chunk is completely empty. */ int getMaxNonEmptyHeight(); @@ -99,7 +100,7 @@ public interface IChunkWrapper extends IBindable default boolean blockPosInsideChunk(int x, int y, int z) { return (x >= this.getMinBlockX() && x <= this.getMaxBlockX() - && y >= this.getMinBuildHeight() && y < this.getMaxBuildHeight() + && y >= this.getInclusiveMinBuildHeight() && y < this.getExclusiveMaxBuildHeight() && z >= this.getMinBlockZ() && z <= this.getMaxBlockZ()); } default boolean blockPosInsideChunk(DhBlockPos2D blockPos) @@ -144,8 +145,8 @@ public interface IChunkWrapper extends IBindable // FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the constructor - int minHeight = this.getMinBuildHeight(); - int maxHeight = this.getMaxBuildHeight() + 1; + int minHeight = this.getInclusiveMinBuildHeight(); + int maxHeight = this.getExclusiveMaxBuildHeight() + 1; if (x < 0 || x >= LodUtil.CHUNK_WIDTH || z < 0 || z >= LodUtil.CHUNK_WIDTH @@ -167,7 +168,7 @@ public interface IChunkWrapper extends IBindable */ default int relativeBlockPosToIndex(int xRel, int y, int zRel) { - int yRel = y - this.getMinBuildHeight(); + int yRel = y - this.getInclusiveMinBuildHeight(); return (zRel * LodUtil.CHUNK_WIDTH * this.getHeight()) + (yRel * LodUtil.CHUNK_WIDTH) + xRel; } @@ -183,7 +184,7 @@ public interface IChunkWrapper extends IBindable index -= (zRel * LodUtil.CHUNK_WIDTH * this.getHeight()); final int y = index / LodUtil.CHUNK_WIDTH; - final int yRel = y + this.getMinBuildHeight(); + final int yRel = y + this.getInclusiveMinBuildHeight(); final int xRel = index % LodUtil.CHUNK_WIDTH; return new DhBlockPos(xRel, yRel, zRel); From f3af6ce74b990fe7ffcde9705b88f8b0d49ba93c Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 2 Nov 2024 13:06:27 -0500 Subject: [PATCH 07/26] add recalculate heightmap config (disabled by default) --- .../seibel/distanthorizons/core/config/Config.java | 13 +++++++++++++ .../assets/distanthorizons/lang/en_us.json | 4 ++++ 2 files changed, 17 insertions(+) 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 c8440cd9d..1dd476e92 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 @@ -1268,6 +1268,19 @@ public class Config + "Expected Compression Ratio: 0.7\n" + "") .build(); + + public static ConfigEntry recalculateChunkHeightmaps = new ConfigEntry.Builder() + .set(false) + .comment("" + + "True: Recalculate chunk height maps before chunks can be used by DH.\n" + + " This can fix problems with worlds created by World Painter or \n" + + " other external tools where the heightmap format may be incorrect. \n" + + "False: Assume any height maps handled by Minecraft are correct. \n" + + "\n" + + "Fastest: False\n" + + "Most Compatible: True\n" + + "") + .build(); } public static class MultiThreading diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 10236c0ca..80675ad7b 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -571,6 +571,10 @@ "Lossy World Compression", "distanthorizons.config.common.lodBuilding.worldCompression.@tooltip": "How should block data be compressed when creating LOD data? \nThis setting will only affect new or updated LOD data, \nany data already generated when this setting is changed will be \nunaffected until it is modified or re-loaded. \n\nMost Accurate: Merge Same Blocks \nHighest Compression: Visually Equal", + "distanthorizons.config.common.lodBuilding.recalculateChunkHeightmaps": + "Recalculate Chunk Heightmaps", + "distanthorizons.config.common.lodBuilding.recalculateChunkHeightmaps.@tooltip": + "True: Recalculate chunk height maps before chunks can be used by DH. This can fix problems with worlds created by external tools. \nFalse: Assume any height maps handled by Minecraft are correct. \n\nFastest: False\nMost Compatible: True", "distanthorizons.config.common.lodBuilding.showMigrationChatWarning": "Log Migration In Chat", From 573c9912db65885b0e1e33af908f7daedf51fb85 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 2 Nov 2024 13:08:26 -0500 Subject: [PATCH 08/26] add IChunkWrapper debug method --- .../chunk/IChunkWrapper.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java index 8e27aa719..0b81e7096 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/chunk/IChunkWrapper.java @@ -158,6 +158,27 @@ public interface IChunkWrapper extends IBindable throw new IndexOutOfBoundsException(errorMessage); } } + /** used to prevent accidentally attempting to get/set values outside this chunk's boundaries */ + default void throwIndexOutOfBoundsIfRelativePosOutsideChunkBounds(int x, int z) throws IndexOutOfBoundsException + { + if (!RUN_RELATIVE_POS_INDEX_VALIDATION) + { + return; + } + + + // FIXME +1 is to handle the fact that LodDataBuilder adds +1 to all block lighting calculations, also done in the constructor + int minHeight = this.getInclusiveMinBuildHeight(); + int maxHeight = this.getExclusiveMaxBuildHeight() + 1; + + if (x < 0 || x >= LodUtil.CHUNK_WIDTH + || z < 0 || z >= LodUtil.CHUNK_WIDTH) + { + String errorMessage = "Relative position [" + x + "," + z + "] out of bounds. \n" + + "X/Z must be between 0 and 15 (inclusive)."; + throw new IndexOutOfBoundsException(errorMessage); + } + } /** From 534684328ee5fd7dd5143093abaa140ee6ea72af Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sun, 3 Nov 2024 19:20:14 +0500 Subject: [PATCH 09/26] Fix Flashback crash on dimension loading --- .../core/api/internal/ServerApi.java | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java index 94890eb9c..4435b3ae6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java @@ -22,12 +22,9 @@ package com.seibel.distanthorizons.core.api.internal; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelLoadEvent; import com.seibel.distanthorizons.api.methods.events.abstractEvents.DhApiLevelUnloadEvent; import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage; +import com.seibel.distanthorizons.core.world.*; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; -import com.seibel.distanthorizons.core.world.AbstractDhWorld; -import com.seibel.distanthorizons.core.world.DhClientServerWorld; -import com.seibel.distanthorizons.core.world.DhServerWorld; -import com.seibel.distanthorizons.core.world.IDhServerWorld; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; @@ -148,6 +145,11 @@ public class ServerApi public void serverPlayerJoinEvent(IServerPlayerWrapper player) { + if (DhApiWorldProxy.INSTANCE.getReadOnly()) + { + return; + } + IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); LOGGER.info("Player [${player.getName()}] joined."); if (serverWorld != null) @@ -157,6 +159,11 @@ public class ServerApi } public void serverPlayerDisconnectEvent(IServerPlayerWrapper player) { + if (DhApiWorldProxy.INSTANCE.getReadOnly()) + { + return; + } + IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); LOGGER.info("Player [${player.getName()}] disconnected."); if (serverWorld != null) @@ -166,6 +173,11 @@ public class ServerApi } public void serverPlayerLevelChangeEvent(IServerPlayerWrapper player, IServerLevelWrapper originLevel, IServerLevelWrapper destinationLevel) { + if (DhApiWorldProxy.INSTANCE.getReadOnly()) + { + return; + } + IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); LOGGER.info("Player [${player.getName()}] changed level: [${originLevel.getKeyedLevelDimensionName()}] -> [${destinationLevel.getKeyedLevelDimensionName()}]."); if (serverWorld != null) @@ -176,6 +188,11 @@ public class ServerApi public void pluginMessageReceived(IServerPlayerWrapper player, @NotNull AbstractNetworkMessage message) { + if (DhApiWorldProxy.INSTANCE.getReadOnly()) + { + return; + } + IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); if (serverWorld != null) { From 1dffedccb91b5f78d6057ef3d6782ad484d0f315 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 4 Nov 2024 18:29:03 -0600 Subject: [PATCH 10/26] Add hashed seed to server level folders to replace multiverse similarity --- .../core/api/internal/ClientApi.java | 4 +- .../api/internal/ClientPluginChannelApi.java | 2 +- .../FullDataToRenderDataTransformer.java | 4 +- .../FullDataSourceProviderV2.java | 34 +- .../GeneratedFullDataSourceProvider.java | 2 +- .../structure/ClientOnlySaveStructure.java | 8 +- .../file/subDimMatching/SubDimCompare.java | 100 ----- .../SubDimensionLevelMatcher.java | 386 ------------------ .../SubDimensionPlayerData.java | 145 ------- .../core/level/AbstractDhServerLevel.java | 10 +- .../core/level/DhClientLevel.java | 2 +- .../AbstractFullDataNetworkRequestQueue.java | 2 +- .../core/render/LodQuadTree.java | 2 +- .../core/world/AbstractDhServerWorld.java | 2 +- .../core/world/DhClientServerWorld.java | 4 +- .../core/world/DhClientWorld.java | 2 +- .../world/ILevelWrapper.java | 13 +- 17 files changed, 50 insertions(+), 672 deletions(-) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimCompare.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionPlayerData.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 0abcede4e..00897f8e4 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -207,7 +207,7 @@ public class ClientApi { try { - LOGGER.info("Unloading client level [" + level + "]-["+level.getDimensionName()+"]."); + LOGGER.info("Unloading client level [" + level.getClass().getSimpleName() + "]-["+level.getLevelIdString()+"]."); if (level instanceof IServerKeyedClientLevel) { @@ -253,7 +253,7 @@ public class ClientApi try { - LOGGER.info("Loading client level [" + levelWrapper + "]-["+levelWrapper.getDimensionName()+"]."); + LOGGER.info("Loading client level [" + levelWrapper + "]-["+levelWrapper.getLevelIdString()+"]."); AbstractDhWorld world = SharedApi.getAbstractDhWorld(); if (world != null) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index 9bc607734..cf8fd1b91 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -101,7 +101,7 @@ public class ClientPluginChannelApi } else { - LOGGER.info("Unloading non-keyed level: [" + clientLevel.getDimensionName() + "]."); + LOGGER.info("Unloading non-keyed level: [" + clientLevel.getLevelIdString() + "]."); this.levelUnloadHandler.accept(clientLevel); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java index bbfba806d..8af8bf9eb 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 @@ -236,10 +236,10 @@ public class FullDataToRenderDataTransformer if (!brokenPos.contains(fullDataMapping.getPos())) { brokenPos.add(fullDataMapping.getPos()); - String dimName = level.getLevelWrapper().getDimensionName(); + String levelId = level.getLevelWrapper().getLevelIdString(); LOGGER.warn("Unable to get data point with id ["+id+"] " + "(Max possible ID: ["+fullDataMapping.getMaxValidId()+"]) " + - "for pos ["+fullDataMapping.getPos()+"] in dimension ["+dimName+"]. " + + "for pos ["+fullDataMapping.getPos()+"] in level ["+levelId+"]. " + "Error: ["+e.getMessage()+"]. " + "Further errors for this position won't be logged."); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java index b9360801c..cb8538c51 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java @@ -130,16 +130,16 @@ public class FullDataSourceProviderV2 DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showFullDataUpdateStatus); - String dimensionName = level.getLevelWrapper().getDimensionName(); + String levelId = level.getLevelWrapper().getLevelIdString(); // start migrating any legacy data sources present in the background - this.migrationThreadPool = ThreadUtil.makeRateLimitedThreadPool(1, MIGRATION_THREAD_NAME_PREFIX + "["+dimensionName+"]", Config.Common.MultiThreading.runTimeRatioForUpdatePropagatorThreads.get(), Thread.MIN_PRIORITY, (Semaphore) null); + this.migrationThreadPool = ThreadUtil.makeRateLimitedThreadPool(1, MIGRATION_THREAD_NAME_PREFIX + "["+levelId+"]", Config.Common.MultiThreading.runTimeRatioForUpdatePropagatorThreads.get(), Thread.MIN_PRIORITY, (Semaphore) null); this.migrationThreadPool.execute(this::convertLegacyDataSources); // update propagation doesn't need to be run on the server since only the highest detail level is needed if (SharedApi.getEnvironment() != EWorldEnvironment.SERVER_ONLY) { - this.updateQueueProcessor = ThreadUtil.makeSingleThreadPool("Parent Update Queue ["+dimensionName+"]"); + this.updateQueueProcessor = ThreadUtil.makeSingleThreadPool("Parent Update Queue ["+levelId+"]"); this.updateQueueProcessor.execute(this::runUpdateQueue); } else @@ -346,8 +346,8 @@ public class FullDataSourceProviderV2 private void convertLegacyDataSources() { - String dimensionName = this.level.getLevelWrapper().getDimensionName(); - LOGGER.info("Attempting to migrate data sources for: ["+dimensionName+"]-["+this.saveDir+"]..."); + String levelId = this.level.getLevelWrapper().getLevelIdString(); + LOGGER.info("Attempting to migrate data sources for: ["+levelId+"]-["+this.saveDir+"]..."); @@ -366,7 +366,7 @@ public class FullDataSourceProviderV2 this.showMigrationStartMessage(); - LOGGER.info("deleting [" + dimensionName + "] - ["+totalDeleteCount+"] unused data sources..."); + LOGGER.info("deleting [" + levelId + "] - ["+totalDeleteCount+"] unused data sources..."); this.legacyDeletionCount = totalDeleteCount; ArrayList unusedDataPosList = this.legacyFileHandler.repo.getUnusedDataSourcePositionStringList(50); @@ -384,7 +384,7 @@ public class FullDataSourceProviderV2 long endStart = System.currentTimeMillis(); long deleteTime = endStart - startTime; - LOGGER.info("Deleting [" + dimensionName + "] - [" + unusedCount + "/" + totalDeleteCount + "] in ["+deleteTime+"]ms ..."); + LOGGER.info("Deleting [" + levelId + "] - [" + unusedCount + "/" + totalDeleteCount + "] in ["+deleteTime+"]ms ..."); // a slight delay is added to prevent accidentally locking the database when deleting a lot of rows @@ -397,7 +397,7 @@ public class FullDataSourceProviderV2 } catch (InterruptedException ignore){} } - LOGGER.info("Done deleting [" + dimensionName + "] - ["+totalDeleteCount+"] unused data sources."); + LOGGER.info("Done deleting [" + levelId + "] - ["+totalDeleteCount+"] unused data sources."); } @@ -422,7 +422,7 @@ public class FullDataSourceProviderV2 int progressCount = 0; while (!legacyDataSourceList.isEmpty() && this.migrationThreadRunning.get()) { - LOGGER.info("Migrating [" + dimensionName + "] - [" + progressCount + "/" + totalMigrationCount + "]..."); + LOGGER.info("Migrating [" + levelId + "] - [" + progressCount + "/" + totalMigrationCount + "]..."); ArrayList> updateFutureList = new ArrayList<>(); for (int i = 0; i < legacyDataSourceList.size() && this.migrationThreadRunning.get(); i++) @@ -484,7 +484,7 @@ public class FullDataSourceProviderV2 } catch (Exception e) { - LOGGER.info("migration stopped due to error for: ["+dimensionName+"]-["+this.saveDir+"], error: ["+e.getMessage()+"].", e); + LOGGER.info("migration stopped due to error for: ["+levelId+"]-["+this.saveDir+"], error: ["+e.getMessage()+"].", e); this.showMigrationEndMessage(false); this.migrationStoppedWithError = true; } @@ -492,13 +492,13 @@ public class FullDataSourceProviderV2 { if (this.migrationThreadRunning.get()) { - LOGGER.info("migration complete for: ["+dimensionName+"]-["+this.saveDir+"]."); + LOGGER.info("migration complete for: ["+levelId+"]-["+this.saveDir+"]."); this.showMigrationEndMessage(true); this.migrationCount = 0; } else { - LOGGER.info("migration stopped for: ["+dimensionName+"]-["+this.saveDir+"]."); + LOGGER.info("migration stopped for: ["+levelId+"]-["+this.saveDir+"]."); this.showMigrationEndMessage(false); this.migrationStoppedWithError = true; } @@ -525,9 +525,9 @@ public class FullDataSourceProviderV2 } this.migrationStartMessageQueued = true; - String dimName = this.level.getLevelWrapper().getDimensionName(); + String levelId = this.level.getLevelWrapper().getLevelIdString(); ClientApi.INSTANCE.showChatMessageNextFrame( - "Old Distant Horizons data is being migrated for ["+dimName+"]. \n" + + "Old Distant Horizons data is being migrated for ["+levelId+"]. \n" + "While migrating LODs may load slowly \n" + "and DH world gen will be disabled. \n" + "You can see migration progress in the F3 menu." @@ -536,16 +536,16 @@ public class FullDataSourceProviderV2 private void showMigrationEndMessage(boolean success) { - String dimName = this.level.getLevelWrapper().getDimensionName(); + String levelId = this.level.getLevelWrapper().getLevelIdString(); if (success) { - ClientApi.INSTANCE.showChatMessageNextFrame("Distant Horizons data migration for ["+dimName+"] completed."); + ClientApi.INSTANCE.showChatMessageNextFrame("Distant Horizons data migration for ["+levelId+"] completed."); } else { ClientApi.INSTANCE.showChatMessageNextFrame( - "Distant Horizons data migration for ["+dimName+"] stopped. \n" + + "Distant Horizons data migration for ["+levelId+"] stopped. \n" + "Some data may not have been migrated." ); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java index 65fb7b59a..37b6ac504 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java @@ -145,7 +145,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im { boolean oldQueueExists = this.worldGenQueueRef.compareAndSet(null, newWorldGenQueue); LodUtil.assertTrue(oldQueueExists, "previous world gen queue is still here!"); - LOGGER.info("Set world gen queue for level [" + this.level.getLevelWrapper().getDimensionName() + "]."); + LOGGER.info("Set world gen queue for level [" + this.level.getLevelWrapper().getLevelIdString() + "]."); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java index a6feccefb..fb4db25ce 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java @@ -80,14 +80,14 @@ public class ClientOnlySaveStructure implements ISaveStructure if (newLevelWrapper instanceof IServerKeyedClientLevel) { IServerKeyedClientLevel keyedClientLevel = (IServerKeyedClientLevel) newLevelWrapper; - LOGGER.info("Loading level [" + newLevelWrapper.getDimensionName() + "] with key: [" + keyedClientLevel.getServerLevelKey() + "]."); + LOGGER.info("Loading level [" + newLevelWrapper.getLevelIdString() + "] with key: [" + keyedClientLevel.getServerLevelKey() + "]."); // This world was identified by the server directly, so we can know for sure which folder to use. - saveFolder = getSaveFolderFromDimensionName(keyedClientLevel.getServerLevelKey()); + saveFolder = getSaveFolderByLevelId(keyedClientLevel.getServerLevelKey()); } else { // get the default folder - saveFolder = getSaveFolderFromDimensionName(levelWrapper.getDimensionName()); + saveFolder = getSaveFolderByLevelId(levelWrapper.getLevelIdString()); } // Allow API users to override the save folder @@ -160,7 +160,7 @@ public class ClientOnlySaveStructure implements ISaveStructure } - private static File getSaveFolderFromDimensionName(String dimensionName) + private static File getSaveFolderByLevelId(String dimensionName) { String path = MC_SHARED.getInstallationDirectory().getPath() + File.separatorChar + SERVER_DATA_FOLDER_NAME + File.separatorChar diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimCompare.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimCompare.java deleted file mode 100644 index 3b73031aa..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimCompare.java +++ /dev/null @@ -1,100 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.file.subDimMatching; - -import com.seibel.distanthorizons.core.config.Config; -import org.jetbrains.annotations.NotNull; - -import java.io.File; - -/** - * Contains data used to compare different sub LodDimensions. - * Sub Dimensions are the different folders under a dimension. - * For example: "\Distant_Horizons_server_data\server_1\dim_the_nether\6fb97c01-e4c7-4634-87db-36b1e490e4c3" - * is a sub dimension for the server "server_1" in the nether. - * - * @author James Seibel - * @version 2022-12-17 - */ -public class SubDimCompare implements Comparable -{ - /** - * the maximum distance in blocks a player can be away from the - * given dimension and still be considered in the same place. - */ - public static int MAX_SIMILAR_PLAYER_POS_DISTANCE_IN_BLOCKS = 3; - - public int equalDataPoints = 0; - public int totalDataPoints = 0; - public int playerPosDist = 0; - public File folder = null; - - - public SubDimCompare(int newEqualDataPoints, int newTotalDataPoints, int newPlayerPosDistance, File newSubDimFolder) - { - this.equalDataPoints = newEqualDataPoints; - this.totalDataPoints = newTotalDataPoints; - this.playerPosDist = newPlayerPosDistance; - - this.folder = newSubDimFolder; - } - - /** returns a number between 0 (no equal datapoint) and 1 (totally equal) */ - public double getPercentEqual() - { - // its possible the comparison didn't find any data points - if (this.totalDataPoints != 0) - { - return (double) this.equalDataPoints / (double) this.totalDataPoints; - } - else - { - return 0; - } - } - - - @Override - public int compareTo(@NotNull SubDimCompare other) - { - if (this.equalDataPoints != other.equalDataPoints) - { - // compare based on data points - return Double.compare(this.getPercentEqual(), other.getPercentEqual()); - } - else - { - // break ties based on player position - return Integer.compare(this.playerPosDist, other.playerPosDist); - } - } - - /** Returns true if this sub dimension is close enough to be considered a valid sub dimension */ - public boolean isValidSubDim() - { - double minimumSimilarityRequired = 0.5;//Config.Client.Advanced.Multiplayer.multiverseSimilarityRequiredPercent.get(); - return this.getPercentEqual() >= minimumSimilarityRequired - || this.playerPosDist <= MAX_SIMILAR_PLAYER_POS_DISTANCE_IN_BLOCKS; - } - - @Override - public String toString() { return this.equalDataPoints + "/" + this.totalDataPoints + ": " + this.getPercentEqual() + " playerPos: " + this.playerPosDist; } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java deleted file mode 100644 index d98287125..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionLevelMatcher.java +++ /dev/null @@ -1,386 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.file.subDimMatching; - -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; -import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; -import com.seibel.distanthorizons.core.generation.DhLightingEngine; -import com.seibel.distanthorizons.core.level.DhClientLevel; -import com.seibel.distanthorizons.core.level.IDhLevel; -import com.seibel.distanthorizons.core.logging.ConfigBasedLogger; -import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; -import com.seibel.distanthorizons.core.pos.DhChunkPos; -import com.seibel.distanthorizons.core.pos.DhSectionPos; -import com.seibel.distanthorizons.core.util.FullDataPointUtil; -import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.util.ThreadUtil; -import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; -import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; -import com.seibel.distanthorizons.coreapi.util.StringUtil; -import it.unimi.dsi.fastutil.longs.LongArrayList; -import org.apache.logging.log4j.LogManager; -import org.apache.logging.log4j.Logger; - -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.UUID; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.atomic.AtomicBoolean; - -/** - * Used to allow multiple levels using the same dimension type.
- * This is specifically needed for servers running the Multiverse plugin (or similar). - * - * @author James Seibel - * @version 12-17-2022 - */ -@Deprecated -public class SubDimensionLevelMatcher implements AutoCloseable -{ - private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - public static final Logger LOGGER = DhLoggerBuilder.getLogger(); - //new ConfigBasedLogger(LogManager.getLogger(), - //() -> Config.Common.Logging.logFileSubDimEvent.get()); - - private final ExecutorService matcherThread = ThreadUtil.makeSingleThreadPool("Sub Dimension Matcher"); - - private SubDimensionPlayerData playerData = null; - private SubDimensionPlayerData firstSeenPlayerData = null; - - /** If true the LodDimensionFileHelper is attempting to determine the folder for this dimension */ - private final AtomicBoolean determiningWorldFolder = new AtomicBoolean(false); - private final IClientLevelWrapper currentClientLevel; - private volatile File foundLevelFile = null; - private final List potentialLevelFolders; - private final File levelsFolder; - - - - //=============// - // constructor // - //=============// - - public SubDimensionLevelMatcher(IClientLevelWrapper targetLevel, File levelsFolder, List potentialLevelFolders) - { - this.currentClientLevel = targetLevel; - this.potentialLevelFolders = potentialLevelFolders; - this.levelsFolder = levelsFolder; - - if (potentialLevelFolders.size() == 0) - { - String newId = UUID.randomUUID().toString(); - LOGGER.info("No potential level files found. Creating a new sub dimension with the ID ["+ StringUtil.shortenString(newId, 8)+"]..."); - this.foundLevelFile = this.CreateSubDimFolder(newId); - } - } - - - - //==============// - // level finder // - //==============// - - public boolean isFindingLevel(ILevelWrapper level) { return Objects.equals(level, this.currentClientLevel); } - - /** May return null if the level isn't known yet */ - public File tryGetLevel() - { - this.tryGetLevelInternalAsync(); - return this.foundLevelFile; - } - private void tryGetLevelInternalAsync() - { - if (this.foundLevelFile != null) - { - return; - } - - // prevent multiple threads running at the same time - if (this.determiningWorldFolder.getAndSet(true)) - { - return; - } - - - this.matcherThread.submit(() -> - { - try - { - // attempt to get the file handler - File saveDir = this.attemptToDetermineSubDimensionFolder(); - if (saveDir != null) - { - this.foundLevelFile = saveDir; - } - } - catch (IOException e) - { - LOGGER.error("Unable to set the dimension file handler for level [" + this.currentClientLevel + "]. Error: ", e); - } - finally - { - // make sure we unlock this method - this.determiningWorldFolder.set(false); - } - }); - } - - /** - * Currently this method checks a single chunk (where the player is) - * and compares it against the same chunk position in the other dimension worlds to - * guess which world the player is in. - * - * @throws IOException if the folder doesn't exist or can't be accessed - */ - public File attemptToDetermineSubDimensionFolder() throws IOException - { - // Update PlayerData - SubDimensionPlayerData newPlayerData = SubDimensionPlayerData.tryGetPlayerData(MC_CLIENT); - if (newPlayerData != null) - { - if (this.firstSeenPlayerData == null) - { - this.firstSeenPlayerData = newPlayerData; - } - this.playerData = newPlayerData; - } - - - - //================================// - // generate a LOD to test against // - //================================// - - // attempt to get a chunk at the player's pos - IChunkWrapper newlyLoadedChunk = MC_CLIENT.getWrappedClientLevel().tryGetChunk(new DhChunkPos(this.playerData.playerBlockPos)); - if (newlyLoadedChunk == null) - { - return null; - } - DhLightingEngine.INSTANCE.lightChunk(newlyLoadedChunk, new ArrayList<>(), MC_CLIENT.getWrappedClientLevel().hasSkyLight() ? LodUtil.MAX_MC_LIGHT : LodUtil.MIN_MC_LIGHT); - - // build the chunk LOD - FullDataSourceV2 newChunkSizedFullDataView = FullDataSourceV2.createFromChunk(newlyLoadedChunk); - // convert to a data source for easier comparing - FullDataSourceV2 newDataSource = FullDataSourceV2.createEmpty(DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, this.playerData.playerBlockPos)); - newDataSource.update(newChunkSizedFullDataView); - - - - //================================// - // test each known sub-dim folder // - //================================// - - // log the start of this attempt - LOGGER.info("Attempting to determine sub-dimension for [" + MC_CLIENT.getWrappedClientLevel().getDimensionName() + "]"); - LOGGER.info("Player block pos in dimension: [" + this.playerData.playerBlockPos.getX() + "," + this.playerData.playerBlockPos.getY() + "," + this.playerData.playerBlockPos.getZ() + "]"); - LOGGER.info("Potential Sub Dimension folders: [" + this.potentialLevelFolders.size() + "]"); - - SubDimCompare mostSimilarSubDim = null; - for (File testLevelFolder : this.potentialLevelFolders) - { - LOGGER.info("Testing level folder: [" + StringUtil.shortenString(testLevelFolder.getName(), 8) + "]"); - - FullDataSourceV2 testFullDataSource = null; - try - { - // get the data source to compare against - try (IDhLevel tempLevel = new DhClientLevel(new ClientOnlySaveStructure(), this.currentClientLevel, testLevelFolder, false, null)) - { - testFullDataSource = tempLevel.getFullDataProvider().getAsync(DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, this.playerData.playerBlockPos)).join(); - if (testFullDataSource == null) - { - continue; - } - } - - - // confirm both data sources have the same section pos - long newSectionChunkPos = DhSectionPos.convertToDetailLevel(newDataSource.getPos(), DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL); - long testSectionChunkPos = DhSectionPos.convertToDetailLevel(testFullDataSource.getPos(), DhSectionPos.SECTION_CHUNK_DETAIL_LEVEL); - LodUtil.assertTrue(newSectionChunkPos == testSectionChunkPos, "data source positions don't match"); - - - - // compare the data sources - int equalDataPoints = 0; - int totalDataPointCount = 0; - for (int x = 0; x < FullDataSourceV2.WIDTH; x++) - { - for (int z = 0; z < FullDataSourceV2.WIDTH; z++) - { - LongArrayList newColumn = newDataSource.get(x, z); - LongArrayList testColumn = testFullDataSource.get(x, z); - - if (newColumn != null && testColumn != null) - { - // compare each data point in the column - - FullDataPointIdMap newDataMap = newDataSource.mapping; - FullDataPointIdMap testDataMap = testFullDataSource.mapping; - - // use min to prevent going out of bounds - int minColumnIndex = Math.min(newColumn.size(), testColumn.size()); - for (int i = 0; i < minColumnIndex; i++) - { - long newDataPoint = newColumn.getLong(i); - long testDataPoint = testColumn.getLong(i); - - int newId = FullDataPointUtil.getId(newDataPoint); - int testId = FullDataPointUtil.getId(testDataPoint); - - - // bottom Y - int newBottom = FullDataPointUtil.getBottomY(newDataPoint); - int testBottom = FullDataPointUtil.getBottomY(testDataPoint); - if (newBottom == testBottom) - { - equalDataPoints++; - } - totalDataPointCount++; - - // height - int newHeight = FullDataPointUtil.getHeight(newDataPoint); - int testHeight = FullDataPointUtil.getHeight(testDataPoint); - if (newHeight == testHeight) - { - equalDataPoints++; - } - totalDataPointCount++; - - // biome - IBiomeWrapper newBiome = newDataMap.getBiomeWrapper(newId); - IBiomeWrapper testBiome = testDataMap.getBiomeWrapper(testId); - if (newBiome.equals(testBiome)) - { - equalDataPoints++; - } - totalDataPointCount++; - - // block - IBlockStateWrapper newBlock = newDataMap.getBlockStateWrapper(newId); - IBlockStateWrapper testBlock = testDataMap.getBlockStateWrapper(testId); - if (newBlock.equals(testBlock)) - { - equalDataPoints++; - } - totalDataPointCount++; - - // ignore light values - // since we are using the DH lighting engine and only 1 chunk the values will never be the same - } - } - else if (newColumn != null) - { - // missing test column - totalDataPointCount += newColumn.size(); - } - else - { - // new column present, test absent, can't compare - } - } - } - - - - // get the player data for this dimension folder - SubDimensionPlayerData testPlayerData = new SubDimensionPlayerData(testLevelFolder); - LOGGER.info("Last known player pos: [" + testPlayerData.playerBlockPos.getX() + "," + testPlayerData.playerBlockPos.getY() + "," + testPlayerData.playerBlockPos.getZ() + "]"); - - // check if the block positions are close - int playerBlockDist = testPlayerData.playerBlockPos.getManhattanDistance(this.playerData.playerBlockPos); - LOGGER.info("Player block position distance between saved sub dimension and first seen is [" + playerBlockDist + "]"); - - // determine if this world is closer to the newly loaded world - SubDimCompare subDimCompare = new SubDimCompare(equalDataPoints, totalDataPointCount, playerBlockDist, testLevelFolder); - if (mostSimilarSubDim == null || subDimCompare.compareTo(mostSimilarSubDim) > 0) - { - mostSimilarSubDim = subDimCompare; - } - - - String subDimShortName = StringUtil.shortenString(testLevelFolder.getName(), 8); // variables are separated out for easier debugging - String equalPercent = StringUtil.shortenString(mostSimilarSubDim.getPercentEqual()+"", 5); - LOGGER.info("Sub dimension ["+subDimShortName+"...] is current dimension probability: "+equalPercent+" ("+equalDataPoints+"/"+totalDataPointCount+")"); - } - catch (Exception e) - { - // this sub dimension isn't formatted correctly - // for now we are just assuming it is an unrelated file - LOGGER.warn("Error checking level: "+e.getMessage(), e); - } - finally - { - if (testFullDataSource != null) - { - try { testFullDataSource.close(); } catch (Exception ignore) {} - } - } - } - - - - //================================// - // return the found sub dimension // - //================================// - - // the first seen player data is no longer needed, the sub dimension has been determined - this.firstSeenPlayerData = null; - if (mostSimilarSubDim != null && mostSimilarSubDim.isValidSubDim()) - { - // we found a sub dim folder that is similar, use it - - LOGGER.info("Sub Dimension set to: [" + StringUtil.shortenString(mostSimilarSubDim.folder.getName(), 8) + "...] with an equality of [" + mostSimilarSubDim.getPercentEqual() + "]"); - return mostSimilarSubDim.folder; - } - else - { - // no sub dim folder, create a new one - - String newId = UUID.randomUUID().toString(); - - double highestEqualityPercent = mostSimilarSubDim != null ? mostSimilarSubDim.getPercentEqual() : 0; - String message = "No suitable sub dimension found. The highest equality was [" + StringUtil.shortenString(highestEqualityPercent + "", 5) + "]. Creating a new sub dimension with ID: " + StringUtil.shortenString(newId, 8) + "..."; - LOGGER.info(message); - - File folder = this.CreateSubDimFolder(newId); - folder.mkdirs(); - return folder; - } - } - - - private File CreateSubDimFolder(String subDimId) { return new File(this.levelsFolder.getPath() + File.separatorChar + this.currentClientLevel.getDimensionName(), subDimId); } - - @Override - public void close() { this.matcherThread.shutdownNow(); } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionPlayerData.java b/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionPlayerData.java deleted file mode 100644 index e62e25bc3..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/subDimMatching/SubDimensionPlayerData.java +++ /dev/null @@ -1,145 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.file.subDimMatching; - - -import com.electronwill.nightconfig.core.file.CommentedFileConfig; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; -import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; - -import org.jetbrains.annotations.Nullable; -import java.io.File; - -/** - * Data container for any player data we can use to differentiate one dimension from another. - * - * @author James Seibel - * @version 2022-3-26 - */ -public class SubDimensionPlayerData -{ - public static final IWrapperFactory FACTORY = SingletonInjector.INSTANCE.get(IWrapperFactory.class); - - public static final String PLAYER_DATA_FILE_NAME = "_playerData.toml"; - - - public static final String PLAYER_BLOCK_POS_X_PATH = "playerBlockPosX"; - public static final String PLAYER_BLOCK_POS_Y_PATH = "playerBlockPosY"; - public static final String PLAYER_BLOCK_POS_Z_PATH = "playerBlockPosZ"; - public DhBlockPos playerBlockPos; - - // not implemented yet - public static final String WORLD_SPAWN_POS_X_PATH = "worldSpawnBlockPosX"; - public static final String WORLD_SPAWN_POS_Y_PATH = "worldSpawnBlockPosY"; - public static final String WORLD_SPAWN_POS_Z_PATH = "worldSpawnBlockPosZ"; - /** - * The client world has access to a spawn point, so this should be possible to fill in. - * I'm not sure what this will look like for worlds that don't have a spawn point. - */ - public DhBlockPos worldSpawnPointBlockPos; - - - - @Nullable - public static SubDimensionPlayerData tryGetPlayerData(IMinecraftClientWrapper mcClient) - { - if (!mcClient.playerExists()) - { - return null; - } - - try - { - return new SubDimensionPlayerData(mcClient); - } - catch (RuntimeException e) - { - // Player no longer exists due to concurrency. FIXME: Remember here is called not on main thread!!! - return null; - } - } - - private SubDimensionPlayerData(IMinecraftClientWrapper mc) - { - this.updateData(mc); - } - - public SubDimensionPlayerData(File dimensionFolder) - { - File file = getFileForDimensionFolder(dimensionFolder); - try (CommentedFileConfig toml = CommentedFileConfig.builder(file).build()) - { - toml.load(); - - // get the player block pos if it is specified - if (toml.contains(PLAYER_BLOCK_POS_X_PATH) - && toml.contains(PLAYER_BLOCK_POS_Y_PATH) - && toml.contains(PLAYER_BLOCK_POS_Z_PATH)) - { - int x = toml.getIntOrElse(PLAYER_BLOCK_POS_X_PATH, 0); - int y = toml.getIntOrElse(PLAYER_BLOCK_POS_Y_PATH, 0); - int z = toml.getIntOrElse(PLAYER_BLOCK_POS_Z_PATH, 0); - this.playerBlockPos = new DhBlockPos(x, y, z); - } - else - { - this.playerBlockPos = new DhBlockPos(0, 0, 0); - } - } - } - - - - public static File getFileForDimensionFolder(File file) { return new File(file.getPath() + File.separatorChar + PLAYER_DATA_FILE_NAME); } - - - /** Should be called often to make sure this object is up to date with the player's info */ - public void updateData(IMinecraftClientWrapper mc) - { - this.playerBlockPos = mc.getPlayerBlockPos(); - - if (this.playerBlockPos == null) - { - throw new RuntimeException("No player block pos!"); - } - } - - /** Writes everything from this object to the file given. */ - public void toTomlFile(CommentedFileConfig toml) - { - // player block pos - toml.add(PLAYER_BLOCK_POS_X_PATH, this.playerBlockPos.getX()); - toml.add(PLAYER_BLOCK_POS_Y_PATH, this.playerBlockPos.getY()); - toml.add(PLAYER_BLOCK_POS_Z_PATH, this.playerBlockPos.getZ()); - - toml.save(); - } - - - @Override - public String toString() - { - return "PlayerBlockPos: [" + this.playerBlockPos.getX() + "," + this.playerBlockPos.getY() + "," + this.playerBlockPos.getZ() + "]"; - } - -} - diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java index dfb21ed31..d08e8070c 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java @@ -114,7 +114,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I continue; } - NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getDimensionName()+"] Fulfilled request group ["+entry.getKey()+"]"); + NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getLevelIdString()+"] Fulfilled request group ["+entry.getKey()+"]"); // Make this group unavailable for adding into this.requestGroupByPos.remove(entry.getKey()); @@ -228,7 +228,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I FullDataSourceRequestMessage requestMessage = requestGroup.requestMessages.remove(msg.futureId); if (requestGroup.requestMessages.isEmpty()) { - NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getDimensionName()+"] Cancelled request group ["+DhSectionPos.toString(requestMessage.sectionPos)+"]."); + NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getLevelIdString()+"] Cancelled request group ["+DhSectionPos.toString(requestMessage.sectionPos)+"]."); this.requestGroupByPos.remove(requestMessage.sectionPos); this.serverside.fullDataFileHandler.removeRetrievalRequestIf(pos -> pos == requestMessage.sectionPos); } @@ -307,7 +307,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I { DataSourceRequestGroup newGroup = new DataSourceRequestGroup(); this.tryFulfillDataSourceRequestGroup(newGroup, pos); - NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getDimensionName()+"] Created request group for pos ["+DhSectionPos.toString(pos)+"]."); + NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getLevelIdString()+"] Created request group for pos ["+DhSectionPos.toString(pos)+"]."); return newGroup; }); @@ -353,8 +353,8 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I new InvalidLevelException( "Generation not allowed. " + "Requested dimension: ["+((ILevelRelatedMessage) message).getLevelName()+"], " + - "player dimension: ["+message.getSession().serverPlayer.getLevel().getDimensionName()+"], " + - "handler dimension: ["+this.getLevelWrapper().getDimensionName()+"]" + "player dimension: ["+message.getSession().serverPlayer.getLevel().getLevelIdString()+"], " + + "handler dimension: ["+this.getLevelWrapper().getLevelIdString()+"]" ) ); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index b5823ddda..4fd2ee944 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -338,7 +338,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel //================// @Override - public String toString() { return "DhClientLevel{"+this.getClientLevelWrapper().getDimensionName()+"}"; } + public String toString() { return "DhClientLevel{"+this.getClientLevelWrapper().getLevelIdString()+"}"; } @Override public void close() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java index 6c762a0ab..dfb9e9e62 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java @@ -291,7 +291,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende public void addDebugMenuStringsToList(List messageList) { - messageList.add(this.getQueueName() + " [" + this.level.getClientLevelWrapper().getDimensionName() + "]"); + messageList.add(this.getQueueName() + " [" + this.level.getClientLevelWrapper().getLevelIdString() + "]"); messageList.add("Requests: " + this.finishedRequests + " / " + (this.getWaitingTaskCount() + this.finishedRequests.get()) + " (failed: " + this.failedRequests + ", rate limit: " + this.getRequestRateLimit() + ")"); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java index c71bd2c03..2d7eac30d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java @@ -147,7 +147,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen } catch (Exception e) { - LOGGER.error("Quad Tree tick exception for dimension: " + this.level.getLevelWrapper().getDimensionName() + ", exception: " + e.getMessage(), e); + LOGGER.error("Quad Tree tick exception for level: [" + this.level.getLevelWrapper().getLevelIdString() + "], error: [" + e.getMessage() + "].", e); } finally { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java index 780c68a5f..eefa32141 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java @@ -110,7 +110,7 @@ public abstract class AbstractDhServerWorld Date: Tue, 5 Nov 2024 07:16:09 -0600 Subject: [PATCH 11/26] Fix Concurrent modification in DhServerWorld --- .../core/world/AbstractDhServerWorld.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java index eefa32141..0b02b85c6 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java @@ -12,14 +12,26 @@ import org.jetbrains.annotations.NotNull; import java.util.HashMap; import java.util.HashSet; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; public abstract class AbstractDhServerWorld extends AbstractDhWorld implements IDhServerWorld { - protected final HashMap dhLevelByLevelWrapper = new HashMap<>(); + /** + * Concurrent since levels can be added/remove while other processing is happening. + * (Otherwise we may need to just put the logic in a lock. + */ + protected final ConcurrentHashMap dhLevelByLevelWrapper = new ConcurrentHashMap<>(); public final LocalSaveStructure saveStructure = new LocalSaveStructure(); private final ServerPlayerStateManager serverPlayerStateManager; + + + //=============// + // constructor // + //=============// + public AbstractDhServerWorld(EWorldEnvironment worldEnvironment) { super(worldEnvironment); @@ -31,7 +43,6 @@ public abstract class AbstractDhServerWorld) this.dhLevelByLevelWrapper.values().stream().distinct()::iterator) + Iterator it = this.dhLevelByLevelWrapper.values().stream().distinct().iterator(); + while (it.hasNext()) { + TDhServerLevel level = it.next(); level.registerNetworkHandlers(playerState); } From 14db049148dd9d1db4d0d9df6ca13a5c7de2a2b0 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Tue, 5 Nov 2024 07:32:44 -0600 Subject: [PATCH 12/26] Fix unnecessary logging for JarUtil jarFile getting Closes #733 --- .../distanthorizons/core/jar/JarUtils.java | 37 ++++++++++++++----- 1 file changed, 27 insertions(+), 10 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/jar/JarUtils.java b/core/src/main/java/com/seibel/distanthorizons/core/jar/JarUtils.java index f607b98fb..20633553e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/jar/JarUtils.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/jar/JarUtils.java @@ -29,6 +29,7 @@ import org.jetbrains.annotations.Nullable; import java.io.*; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import java.nio.charset.StandardCharsets; import java.security.MessageDigest; import java.util.Objects; @@ -46,27 +47,44 @@ public class JarUtils @Nullable public static File jarFile = null; + + + //=============// + // constructor // + //=============// + static { try { - jarFile = new File(JarUtils.class.getProtectionDomain().getCodeSource().getLocation().toURI()); // Always safe + // this will fail in development environments due to how the jars are compiled + // this may also fail in forge production + URI jarUri = JarUtils.class.getProtectionDomain().getCodeSource().getLocation().toURI(); + jarFile = new File(jarUri); } - catch (Exception e) + catch (Exception eGetUri) { try { - LOGGER.warn("Unable to get the jar file, trying backup method... Error: "+e.getMessage(), e); jarFile = SingletonInjector.INSTANCE.get(IModChecker.class).modLocation(ModInfo.ID); } - catch (Exception f) + catch (Exception eGetModLoc) { - LOGGER.warn("Backup jar file getter failed. Error: "+f.getMessage(), f); + // only log if both methods fail since it isn't a problem unless both + // methods fail + LOGGER.warn("Unable to get jar file via URI or Mod Checker Location."); + LOGGER.warn("URI Error: ["+ eGetUri.getMessage()+"]", eGetUri); + LOGGER.warn("Mod Location Error: ["+ eGetModLoc.getMessage()+"]", eGetModLoc); } } } + + //=========// + // methods // + //=========// + /** * Gets the URI of a resource * @@ -75,9 +93,7 @@ public class JarUtils * @throws URISyntaxException If the file doesnt exist */ public static URI accessFileURI(String resource) throws URISyntaxException - { - return Objects.requireNonNull(JarUtils.class.getResource(resource)).toURI(); - } + { return Objects.requireNonNull(JarUtils.class.getResource(resource)).toURI(); } /** * Get a file within the mods resources @@ -146,7 +162,7 @@ public class JarUtils while ((bytesCount = fis.read(byteArray)) != -1) { digest.update(byteArray, 0, bytesCount); - } ; + } //close the stream; We don't need it now. fis.close(); @@ -174,7 +190,8 @@ public class JarUtils /** Please use the EPlatform enum instead */ @Deprecated public static OperatingSystem getOperatingSystem() - { // Get the os and turn it into that enum + { + // Get the os and turn it into that enum switch (EPlatform.get()) { case WINDOWS: From 32b9e723d1b146e29fc214bd9063399b3323ae3e Mon Sep 17 00:00:00 2001 From: James Seibel Date: Wed, 6 Nov 2024 07:08:17 -0600 Subject: [PATCH 13/26] Fix crashing after server shutdown in serverPlayerDisconnectEvent --- .../core/api/internal/ServerApi.java | 14 +++++++------- .../core/generation/WorldGenerationQueue.java | 2 +- .../AbstractFullDataNetworkRequestQueue.java | 2 +- .../core/world/DhApiWorldProxy.java | 14 +++++++------- 4 files changed, 16 insertions(+), 16 deletions(-) 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 4435b3ae6..d852c1504 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ServerApi.java @@ -145,13 +145,13 @@ public class ServerApi public void serverPlayerJoinEvent(IServerPlayerWrapper player) { - if (DhApiWorldProxy.INSTANCE.getReadOnly()) + if (DhApiWorldProxy.INSTANCE.worldLoaded() && DhApiWorldProxy.INSTANCE.getReadOnly()) { return; } IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); - LOGGER.info("Player [${player.getName()}] joined."); + LOGGER.info("Player ["+player.getName()+"] joined."); if (serverWorld != null) { serverWorld.addPlayer(player); @@ -159,13 +159,13 @@ public class ServerApi } public void serverPlayerDisconnectEvent(IServerPlayerWrapper player) { - if (DhApiWorldProxy.INSTANCE.getReadOnly()) + if (DhApiWorldProxy.INSTANCE.worldLoaded() && DhApiWorldProxy.INSTANCE.getReadOnly()) { return; } IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); - LOGGER.info("Player [${player.getName()}] disconnected."); + LOGGER.info("Player ["+player.getName()+"] disconnected."); if (serverWorld != null) { serverWorld.removePlayer(player); @@ -173,13 +173,13 @@ public class ServerApi } public void serverPlayerLevelChangeEvent(IServerPlayerWrapper player, IServerLevelWrapper originLevel, IServerLevelWrapper destinationLevel) { - if (DhApiWorldProxy.INSTANCE.getReadOnly()) + if (DhApiWorldProxy.INSTANCE.worldLoaded() && DhApiWorldProxy.INSTANCE.getReadOnly()) { return; } IDhServerWorld serverWorld = SharedApi.getIDhServerWorld(); - LOGGER.info("Player [${player.getName()}] changed level: [${originLevel.getKeyedLevelDimensionName()}] -> [${destinationLevel.getKeyedLevelDimensionName()}]."); + LOGGER.info("Player ["+player.getName()+"] changed level: ["+originLevel.getKeyedLevelDimensionName()+"] -> ["+destinationLevel.getKeyedLevelDimensionName()+"]."); if (serverWorld != null) { serverWorld.changePlayerLevel(player, originLevel, destinationLevel); @@ -188,7 +188,7 @@ public class ServerApi public void pluginMessageReceived(IServerPlayerWrapper player, @NotNull AbstractNetworkMessage message) { - if (DhApiWorldProxy.INSTANCE.getReadOnly()) + if (DhApiWorldProxy.INSTANCE.worldLoaded() && DhApiWorldProxy.INSTANCE.getReadOnly()) { return; } 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 d700ea058..c2510e5a0 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 @@ -190,7 +190,7 @@ public class WorldGenerationQueue implements IFullDataSourceRetrievalQueue, IDeb try { // loop until the generator is shutdown - while (!Thread.interrupted() && !DhApiWorldProxy.INSTANCE.getReadOnly()) + while (!Thread.interrupted() && DhApiWorldProxy.INSTANCE.worldLoaded() && !DhApiWorldProxy.INSTANCE.getReadOnly()) { this.generator.preGeneratorTaskStart(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java index dfb9e9e62..2731a0b94 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java @@ -129,7 +129,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende public synchronized boolean tick(DhBlockPos2D targetPos) { - if (DhApiWorldProxy.INSTANCE.getReadOnly()) + if (DhApiWorldProxy.INSTANCE.worldLoaded() && DhApiWorldProxy.INSTANCE.getReadOnly()) { return false; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/DhApiWorldProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/world/DhApiWorldProxy.java index ce17bf93c..4f70b967f 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/DhApiWorldProxy.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/DhApiWorldProxy.java @@ -72,13 +72,13 @@ public class DhApiWorldProxy implements IDhApiWorldProxy public boolean worldLoaded() { return SharedApi.getAbstractDhWorld() != null; } @Override - public void setReadOnly(boolean readOnly) { this.setReadOnly(readOnly, true); } + public void setReadOnly(boolean readOnly) throws IllegalStateException { this.setReadOnly(readOnly, true); } /** * Not part of the public API. * Normal API users shouldn't be able to change the upcoming world state * this is only here so DH can revert the readonly value after the world is unloaded */ - public void setReadOnly(boolean readOnly, boolean throwIfWorldUnloaded) + public void setReadOnly(boolean readOnly, boolean throwIfWorldUnloaded) throws IllegalStateException { if (throwIfWorldUnloaded && SharedApi.getAbstractDhWorld() == null) { @@ -102,7 +102,7 @@ public class DhApiWorldProxy implements IDhApiWorldProxy } @Override - public boolean getReadOnly() + public boolean getReadOnly() throws IllegalStateException { if (SharedApi.getAbstractDhWorld() == null) { @@ -120,7 +120,7 @@ public class DhApiWorldProxy implements IDhApiWorldProxy //================// @Override - public IDhApiLevelWrapper getSinglePlayerLevel() + public IDhApiLevelWrapper getSinglePlayerLevel() throws IllegalStateException { if (SharedApi.getAbstractDhWorld() == null) { @@ -138,7 +138,7 @@ public class DhApiWorldProxy implements IDhApiWorldProxy @Override - public Iterable getAllLoadedLevelWrappers() + public Iterable getAllLoadedLevelWrappers() throws IllegalStateException { AbstractDhWorld world = SharedApi.getAbstractDhWorld(); if (world == null) @@ -155,7 +155,7 @@ public class DhApiWorldProxy implements IDhApiWorldProxy } @Override - public Iterable getAllLoadedLevelsForDimensionType(IDhApiDimensionTypeWrapper dimensionTypeWrapper) + public Iterable getAllLoadedLevelsForDimensionType(IDhApiDimensionTypeWrapper dimensionTypeWrapper) throws IllegalStateException { AbstractDhWorld world = SharedApi.getAbstractDhWorld(); if (world == null) @@ -176,7 +176,7 @@ public class DhApiWorldProxy implements IDhApiWorldProxy } @Override - public Iterable getAllLoadedLevelsWithDimensionNameLike(String dimensionName) + public Iterable getAllLoadedLevelsWithDimensionNameLike(String dimensionName) throws IllegalStateException { AbstractDhWorld world = SharedApi.getAbstractDhWorld(); if (world == null) From bbd6f2ea89baa5ed0546f3b2278425b2d078c947 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Fri, 8 Nov 2024 07:39:58 -0600 Subject: [PATCH 14/26] Move some buffer building logic off the render thread --- .../bufferBuilding/ColumnRenderBuffer.java | 70 ++++++++----------- .../ColumnRenderBufferBuilder.java | 2 +- 2 files changed, 29 insertions(+), 43 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 cc44a96fd..b3d0dde3f 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 @@ -89,24 +89,48 @@ public class ColumnRenderBuffer implements AutoCloseable //==================// /** Should be run on a DH thread. */ - public void uploadBuffer(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod) throws InterruptedException + public void makeAndUploadBuffers(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod) throws InterruptedException { LodUtil.assertTrue(DhApi.isDhThread(), "Buffer uploading needs to be done on a DH thread to prevent locking up any MC threads."); + // make the buffers + ArrayList opaqueBuffers = builder.makeOpaqueVertexBuffers(); + ArrayList transparentBuffers = builder.makeTransparentVertexBuffers(); + + vbos = resizeBuffer(vbos, opaqueBuffers.size()); + vbosTransparent = resizeBuffer(vbosTransparent, transparentBuffers.size()); + + // upload on MC's render thread CompletableFuture uploadFuture = new CompletableFuture<>(); - MC_CLIENT.executeOnRenderThread(() -> + GLProxy.getInstance().queueRunningOnRenderThread(() -> { try { - this.uploadBuffers(builder, gpuUploadMethod); + uploadBuffersDirect(vbos, opaqueBuffers, gpuUploadMethod); + uploadBuffersDirect(vbosTransparent, transparentBuffers, gpuUploadMethod); + this.buffersUploaded = true; uploadFuture.complete(null); } catch (InterruptedException e) { throw new CompletionException(e); } + finally + { + // all the buffers must be manually freed to prevent memory leaks + + for (ByteBuffer buffer : opaqueBuffers) + { + MemoryUtil.memFree(buffer); + } + + for (ByteBuffer buffer : transparentBuffers) + { + MemoryUtil.memFree(buffer); + } + } }); @@ -126,43 +150,7 @@ public class ColumnRenderBuffer implements AutoCloseable //LOGGER.warn("Error uploading builder ["+builder+"] synchronously. Error: "+e.getMessage(), e); } } - private void uploadBuffers(LodQuadBuilder builder, EDhApiGpuUploadMethod method) throws InterruptedException - { - // uploading mapped buffers used to be done here, - // however due to a memory leak and complication with the previous code, - // now we only allow direct uploading. - // (There's also insufficient data to state whether mapped buffers are necessary - // for DH to upload without stuttering the main thread) - - this.vbos = makeAndUploadBuffers(builder, method, this.vbos, builder.makeOpaqueVertexBuffers()); - this.vbosTransparent = makeAndUploadBuffers(builder, method, this.vbosTransparent, builder.makeTransparentVertexBuffers()); - - this.buffersUploaded = true; - } - /** This resizes and returns the vbo array if necessary based on the amount of data needed for this area. */ - private static GLVertexBuffer[] makeAndUploadBuffers(LodQuadBuilder builder, EDhApiGpuUploadMethod method, GLVertexBuffer[] vbos, ArrayList buffers) throws InterruptedException - { - try - { - vbos = resizeBuffer(vbos, buffers.size()); - uploadBuffersDirect(vbos, buffers, method); - } - finally - { - // all the buffers must be manually freed to prevent memory leaks - if (buffers != null) - { - for (ByteBuffer buffer : buffers) - { - MemoryUtil.memFree(buffer); - } - } - } - - // return the array in case it was resized - return vbos; - } - public static GLVertexBuffer[] resizeBuffer(GLVertexBuffer[] vbos, int newSize) + private static GLVertexBuffer[] resizeBuffer(GLVertexBuffer[] vbos, int newSize) { if (vbos.length == newSize) { @@ -228,8 +216,6 @@ public class ColumnRenderBuffer implements AutoCloseable - - //========// // render // //========// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java index 61fb96081..e4a661c26 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java @@ -135,7 +135,7 @@ public class ColumnRenderBufferBuilder ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getMinY(), DhSectionPos.getMinCornerBlockZ(pos))); try { - buffer.uploadBuffer(quadBuilder, GLProxy.getInstance().getGpuUploadMethod()); + buffer.makeAndUploadBuffers(quadBuilder, GLProxy.getInstance().getGpuUploadMethod()); if (buffer.buffersUploaded) { return buffer; From 13b9e9b0d9d4f4d1cf1da2f6e669981688dafb1f Mon Sep 17 00:00:00 2001 From: James Seibel Date: Fri, 8 Nov 2024 07:40:37 -0600 Subject: [PATCH 15/26] (test) remove GLProxy render thread timeout This should cause upload tasks to finish much faster but will cause stuttering if too many tasks build up --- .../seibel/distanthorizons/core/render/glObject/GLProxy.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java index 93b0db3f5..f83ecf7bc 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 @@ -243,7 +243,7 @@ public class GLProxy // only try running for 4ms at a time to (hopefully) prevent random lag spikes if (runDuration > 4_000_000) { - break; + //break; } From 0fb71316318faaada0db48f74496996bd3da3df0 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Fri, 8 Nov 2024 07:41:02 -0600 Subject: [PATCH 16/26] deprectate MC_CLIENT.executeOnRenderThread() Use GLProxy instead --- .../minecraft/IMinecraftClientWrapper.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java index bec5ddcff..fcb22b01e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/minecraft/IMinecraftClientWrapper.java @@ -25,6 +25,7 @@ import java.util.UUID; import com.seibel.distanthorizons.core.enums.EDhDirection; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.pos.DhChunkPos; +import com.seibel.distanthorizons.core.render.glObject.GLProxy; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; @@ -147,7 +148,11 @@ public interface IMinecraftClientWrapper extends IBindable Object getOptionsObject(); - /** Executes the given task on Minecraft's render thread. */ + /** + * Executes the given task on Minecraft's render thread. + * @deprecated use {@link GLProxy#runningOnRenderThread()} instead + */ + @Deprecated void executeOnRenderThread(Runnable runnable); } From ea1d79a1a6a55eda1c60d1d723a9347bfa80b0a9 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Fri, 8 Nov 2024 07:41:22 -0600 Subject: [PATCH 17/26] Add todo to GenericObjRenderer about potential crash --- .../core/render/renderer/generic/GenericObjectRenderer.java | 1 + 1 file changed, 1 insertion(+) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java index 4e1887b8d..c1dff04d2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java @@ -582,6 +582,7 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister shaderProgram.fillSharedDirectUniformData(renderEventParam, shading, boxGroup, camPos); + // TODO handle empty arrays (concurrency issue?) for (DhApiRenderableBox box : boxGroup) { this.renderBox(shaderProgram, renderEventParam, boxGroup, box, camPos); From 6699c4c45289f94ea19dc9b98fa6018b7dcfb0c2 Mon Sep 17 00:00:00 2001 From: coolGi Date: Sat, 9 Nov 2024 14:42:47 +0000 Subject: [PATCH 18/26] Updated readme to use the new DH links --- Readme.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Readme.md b/Readme.md index 71b9a1cb0..66dc761b3 100644 --- a/Readme.md +++ b/Readme.md @@ -1,10 +1,10 @@ -# +# This repo is for the Distant Horizons mod. The purpose of this submodule is to isolate code that isn't tied to a specific version of minecraft. This prevents us from having duplicate code; reducing errors and helping us port to different versions faster and easier. Check out the mod's main GitLab page here: -https://gitlab.com/jeseibel/distant-horizons +https://gitlab.com/distant-horizons-team/distant-horizons ## source code installation From adce15f648f339de0362e436f0a51d296404e951 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 9 Nov 2024 08:48:18 -0600 Subject: [PATCH 19/26] Improve Buffer uploading speed and remove buffer upload thread --- .../bufferBuilding/ColumnRenderBuffer.java | 79 +++++++++---------- .../ColumnRenderBufferBuilder.java | 65 +++------------ .../core/generation/WorldGenerationQueue.java | 6 +- .../core/logging/f3/F3Screen.java | 2 - .../core/render/LodRenderSection.java | 5 +- .../core/render/glObject/GLProxy.java | 2 +- .../core/util/threading/ThreadPoolUtil.java | 7 -- 7 files changed, 57 insertions(+), 109 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 b3d0dde3f..d61394ad9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java @@ -19,9 +19,7 @@ package com.seibel.distanthorizons.core.dataObjects.render.bufferBuilding; -import com.seibel.distanthorizons.api.DhApi; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; -import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; import com.seibel.distanthorizons.core.render.glObject.GLProxy; @@ -30,7 +28,6 @@ import com.seibel.distanthorizons.core.render.renderer.LodRenderer; import com.seibel.distanthorizons.core.util.LodUtil; import com.seibel.distanthorizons.core.util.objects.StatsMap; import com.seibel.distanthorizons.api.enums.config.EDhApiGpuUploadMethod; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import org.apache.logging.log4j.Logger; import org.lwjgl.system.MemoryUtil; @@ -46,9 +43,6 @@ import java.util.concurrent.*; public class ColumnRenderBuffer implements AutoCloseable { private static final Logger LOGGER = DhLoggerBuilder.getLogger(); - private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); - - private static final long MAX_BUFFER_UPLOAD_TIMEOUT_NANOSECONDS = 1_000_000; /** number of bytes a single quad takes */ public static final int QUADS_BYTE_SIZE = LodUtil.LOD_VERTEX_FORMAT.getByteSize() * 4; @@ -59,63 +53,81 @@ public class ColumnRenderBuffer implements AutoCloseable - - public final DhBlockPos pos; + public final DhBlockPos blockPos; public boolean buffersUploaded = false; private GLVertexBuffer[] vbos; private GLVertexBuffer[] vbosTransparent; + private CompletableFuture uploadFuture = null; + //==============// // constructors // //==============// - public ColumnRenderBuffer(DhBlockPos pos) + public ColumnRenderBuffer(DhBlockPos blockPos) { - this.pos = pos; + this.blockPos = blockPos; this.vbos = new GLVertexBuffer[0]; this.vbosTransparent = new GLVertexBuffer[0]; } - - //==================// // buffer uploading // //==================// /** Should be run on a DH thread. */ - public void makeAndUploadBuffers(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod) throws InterruptedException + public synchronized CompletableFuture makeAndUploadBuffersAsync(LodQuadBuilder builder, EDhApiGpuUploadMethod gpuUploadMethod) { - LodUtil.assertTrue(DhApi.isDhThread(), "Buffer uploading needs to be done on a DH thread to prevent locking up any MC threads."); + if (this.uploadFuture != null) + { + return this.uploadFuture; + } + this.uploadFuture = new CompletableFuture<>(); + // make the buffers ArrayList opaqueBuffers = builder.makeOpaqueVertexBuffers(); ArrayList transparentBuffers = builder.makeTransparentVertexBuffers(); - vbos = resizeBuffer(vbos, opaqueBuffers.size()); - vbosTransparent = resizeBuffer(vbosTransparent, transparentBuffers.size()); + this.vbos = resizeBuffer(this.vbos, opaqueBuffers.size()); + this.vbosTransparent = resizeBuffer(this.vbosTransparent, transparentBuffers.size()); // upload on MC's render thread - CompletableFuture uploadFuture = new CompletableFuture<>(); GLProxy.getInstance().queueRunningOnRenderThread(() -> { try { - uploadBuffersDirect(vbos, opaqueBuffers, gpuUploadMethod); - uploadBuffersDirect(vbosTransparent, transparentBuffers, gpuUploadMethod); + if (Thread.interrupted()) + { + throw new InterruptedException(); + } + + uploadBuffersDirect(this.vbos, opaqueBuffers, gpuUploadMethod); + uploadBuffersDirect(this.vbosTransparent, transparentBuffers, gpuUploadMethod); this.buffersUploaded = true; - uploadFuture.complete(null); + + this.uploadFuture.complete(this); + this.uploadFuture = null; } - catch (InterruptedException e) + catch (InterruptedException ignore) { - throw new CompletionException(e); + this.uploadFuture.complete(this); + this.uploadFuture = null; + } + catch (Exception e) + { + LOGGER.error("Unexpected issue uploading buffer ["+this.blockPos +"], error: ["+e.getMessage()+"].", e); + + this.uploadFuture.completeExceptionally(e); + this.uploadFuture = null; } finally { @@ -133,22 +145,7 @@ public class ColumnRenderBuffer implements AutoCloseable } }); - - try - { - // wait for the upload to finish - uploadFuture.get(5_000, TimeUnit.MILLISECONDS); - } - catch (ExecutionException e) - { - LOGGER.warn("Error uploading builder ["+builder+"] synchronously. Error: "+e.getMessage(), e); - } - catch (TimeoutException e) - { - // timeouts can be ignored because it generally means the - // MC Render thread executor was closed - //LOGGER.warn("Error uploading builder ["+builder+"] synchronously. Error: "+e.getMessage(), e); - } + return this.uploadFuture; } private static GLVertexBuffer[] resizeBuffer(GLVertexBuffer[] vbos, int newSize) { @@ -224,7 +221,7 @@ public class ColumnRenderBuffer implements AutoCloseable public boolean renderOpaque(LodRenderer renderContext, DhApiRenderParam renderEventParam) { boolean hasRendered = false; - renderContext.setModelViewMatrixOffset(this.pos, renderEventParam); + renderContext.setModelViewMatrixOffset(this.blockPos, renderEventParam); for (GLVertexBuffer vbo : this.vbos) { if (vbo == null) @@ -252,7 +249,7 @@ public class ColumnRenderBuffer implements AutoCloseable try { // can throw an IllegalStateException if the GL program was freed before it should've been - renderContext.setModelViewMatrixOffset(this.pos, renderEventParam); + renderContext.setModelViewMatrixOffset(this.blockPos, renderEventParam); for (GLVertexBuffer vbo : this.vbosTransparent) { @@ -273,7 +270,7 @@ public class ColumnRenderBuffer implements AutoCloseable } catch (IllegalStateException e) { - LOGGER.error("renderContext program doesn't exist for pos: "+this.pos, e); + LOGGER.error("renderContext program doesn't exist for pos: "+this.blockPos, e); } return hasRendered; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java index e4a661c26..17f37ad5e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBufferBuilder.java @@ -115,62 +115,17 @@ public class ColumnRenderBufferBuilder LodQuadBuilder quadBuilder ) { - // TODO put into a single future/thread so it can be easily canceled - ThreadPoolExecutor bufferUploaderExecutor = ThreadPoolUtil.getBufferUploaderExecutor(); - if (bufferUploaderExecutor == null || bufferUploaderExecutor.isTerminated()) + ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getMinY(), DhSectionPos.getMinCornerBlockZ(pos))); + CompletableFuture uploadFuture = buffer.makeAndUploadBuffersAsync(quadBuilder, GLProxy.getInstance().getGpuUploadMethod()); + uploadFuture.whenComplete((uploadedBuffer, exception) -> { - // one or more of the thread pools has been shut down - CompletableFuture future = new CompletableFuture<>(); - future.cancel(true); - return future; - } - - - try - { - return CompletableFuture.supplyAsync(() -> - { - try - { - ColumnRenderBuffer buffer = new ColumnRenderBuffer(new DhBlockPos(DhSectionPos.getMinCornerBlockX(pos), clientLevel.getMinY(), DhSectionPos.getMinCornerBlockZ(pos))); - try - { - buffer.makeAndUploadBuffers(quadBuilder, GLProxy.getInstance().getGpuUploadMethod()); - if (buffer.buffersUploaded) - { - return buffer; - } - else - { - buffer.close(); - return null; - } - } - catch (Exception e) - { - buffer.close(); - throw e; - } - } - catch (InterruptedException e) - { - throw UncheckedInterruptedException.convert(e); - } - catch (Throwable e3) - { - LOGGER.error("LodNodeBufferBuilder was unable to upload buffer for pos ["+DhSectionPos.toString(pos)+"], error: [" + e3.getMessage() + "].", e3); - throw e3; - } - }, bufferUploaderExecutor); - } - catch (RejectedExecutionException ignore) - { - // shouldn't happen, but just in case - - CompletableFuture future = new CompletableFuture<>(); - future.cancel(true); - return future; - } + // clean up if not uploaded + if (!uploadedBuffer.buffersUploaded) + { + uploadedBuffer.close(); + } + }); + return uploadFuture; } private static void makeLodRenderData( LodQuadBuilder quadBuilder, ColumnRenderSource renderSource, IDhClientLevel clientLevel, diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java index c2510e5a0..05fefd901 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/WorldGenerationQueue.java @@ -26,7 +26,11 @@ import com.seibel.distanthorizons.api.objects.data.DhApiChunk; import com.seibel.distanthorizons.api.objects.data.IDhApiFullDataSource; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.generation.tasks.*; +import com.seibel.distanthorizons.core.generation.tasks.IWorldGenTaskTracker; +import com.seibel.distanthorizons.core.generation.tasks.InProgressWorldGenTaskGroup; +import com.seibel.distanthorizons.core.generation.tasks.WorldGenResult; +import com.seibel.distanthorizons.core.generation.tasks.WorldGenTask; +import com.seibel.distanthorizons.core.generation.tasks.WorldGenTaskGroup; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D; import com.seibel.distanthorizons.core.pos.DhChunkPos; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java index 4246ef68f..8c1011974 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/logging/f3/F3Screen.java @@ -77,7 +77,6 @@ public class F3Screen ThreadPoolExecutor updatePool = ThreadPoolUtil.getUpdatePropagatorExecutor(); ThreadPoolExecutor lodBuilderPool = ThreadPoolUtil.getChunkToLodBuilderExecutor(); ThreadPoolExecutor bufferBuilderPool = ThreadPoolUtil.getBufferBuilderExecutor(); - ThreadPoolExecutor bufferUploaderPool = ThreadPoolUtil.getBufferUploaderExecutor(); AbstractDhWorld world = SharedApi.getAbstractDhWorld(); Iterable levelIterator = world.getAllLoadedLevels(); @@ -96,7 +95,6 @@ public class F3Screen messageList.add(getThreadPoolStatString("Update Propagator", updatePool)); messageList.add(getThreadPoolStatString("LOD Builder", lodBuilderPool)); messageList.add(getThreadPoolStatString("Buffer Builder", bufferBuilderPool)); - messageList.add(getThreadPoolStatString("Buffer Uploader", bufferUploaderPool)); messageList.add(""); // chunk updates messageList.add(SharedApi.INSTANCE.getDebugMenuString()); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java index 334cba2cc..7e4bb84fc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodRenderSection.java @@ -274,10 +274,11 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable this.bufferUploadFuture = ColumnRenderBufferBuilder.uploadBuffersAsync(this.level, this.pos, lodQuadBuilder); return this.bufferUploadFuture.thenCompose((buffer) -> { + // needed to clean up the old data ColumnRenderBuffer previousBuffer = this.renderBuffer; - // upload complete, clean up the old data if - this.renderBuffer = buffer; + // upload complete + this.renderBuffer = buffer.buffersUploaded ? buffer : null; this.buildAndUploadRenderDataToGpuFuture = null; this.bufferBuildFuture = null; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java index f83ecf7bc..93b0db3f5 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 @@ -243,7 +243,7 @@ public class GLProxy // only try running for 4ms at a time to (hopefully) prevent random lag spikes if (runDuration > 4_000_000) { - //break; + break; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java index 39151eb93..c78dbefd7 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/util/threading/ThreadPoolUtil.java @@ -56,11 +56,6 @@ public class ThreadPoolUtil @Nullable public static ThreadPoolExecutor getWorldGenExecutor() { return worldGenThreadPool.executor; } - public static final String BUFFER_UPLOADER_THREAD_NAME = "Buffer Uploader"; - private static ThreadPoolExecutor bufferUploaderThreadPool; - @Nullable - public static ThreadPoolExecutor getBufferUploaderExecutor() { return bufferUploaderThreadPool; } - public static final String CLEANUP_THREAD_NAME = "Cleanup"; private static ThreadPoolExecutor cleanupThreadPool; @Nullable @@ -118,7 +113,6 @@ public class ThreadPoolUtil updatePropagatorThreadPool = new ConfigThreadPool(UPDATE_PROPAGATOR_THREAD_FACTORY, Config.Common.MultiThreading.numberOfUpdatePropagatorThreads, Config.Common.MultiThreading.runTimeRatioForUpdatePropagatorThreads, null); worldGenThreadPool = new ConfigThreadPool(WORLD_GEN_THREAD_FACTORY, Config.Common.MultiThreading.numberOfWorldGenerationThreads, Config.Common.MultiThreading.runTimeRatioForWorldGenerationThreads, null); networkCompressionThreadPool = new ConfigThreadPool(NETWORK_COMPRESSION_THREAD_FACTORY, Config.Common.MultiThreading.numberOfNetworkCompressionThreads, Config.Common.MultiThreading.runTimeRatioForNetworkCompressionThreads, null); - bufferUploaderThreadPool = ThreadUtil.makeSingleThreadPool(BUFFER_UPLOADER_THREAD_NAME); cleanupThreadPool = ThreadUtil.makeSingleThreadPool(CLEANUP_THREAD_NAME); beaconCullingThreadPool = ThreadUtil.makeSingleThreadPool(BEACON_CULLING_THREAD_NAME); @@ -160,7 +154,6 @@ public class ThreadPoolUtil updatePropagatorThreadPool.shutdownExecutorService(); worldGenThreadPool.shutdownExecutorService(); networkCompressionThreadPool.shutdownExecutorService(); - bufferUploaderThreadPool.shutdown(); cleanupThreadPool.shutdown(); beaconCullingThreadPool.shutdown(); From b0f2918daff30aba85e1dc7e546d01369909d7b1 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 9 Nov 2024 09:48:37 -0600 Subject: [PATCH 20/26] Fix GLProxy renderThread skipping some tasks --- .../distanthorizons/core/render/glObject/GLProxy.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java index 93b0db3f5..7159d4366 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/GLProxy.java @@ -238,16 +238,16 @@ public class GLProxy Runnable runnable = this.renderThreadRunnableQueue.poll(); while(runnable != null) { + runnable.run(); + + // only try running for 4ms (240 FPS) at a time to prevent random lag spikes long currentTime = System.nanoTime(); long runDuration = currentTime - startTime; - // only try running for 4ms at a time to (hopefully) prevent random lag spikes if (runDuration > 4_000_000) { break; } - - runnable.run(); runnable = this.renderThreadRunnableQueue.poll(); } } From 6321a6f9af574b4dc485bc95647d372c5149861a Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 9 Nov 2024 09:51:54 -0600 Subject: [PATCH 21/26] Attempt to fix concurrency in generic render direct rendering --- .../generic/GenericObjectRenderer.java | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java index c1dff04d2..4d5cd3e98 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/generic/GenericObjectRenderer.java @@ -582,10 +582,23 @@ public class GenericObjectRenderer implements IDhApiCustomRenderRegister shaderProgram.fillSharedDirectUniformData(renderEventParam, shading, boxGroup, camPos); - // TODO handle empty arrays (concurrency issue?) - for (DhApiRenderableBox box : boxGroup) + for (int i = 0; i < boxGroup.size(); i++) { - this.renderBox(shaderProgram, renderEventParam, boxGroup, box, camPos); + try + { + DhApiRenderableBox box = boxGroup.get(i); + if (box != null) + { + this.renderBox(shaderProgram, renderEventParam, boxGroup, box, camPos); + } + } + catch (IndexOutOfBoundsException e) + { + // Concurrency issue, the list was modified while rendering + // this can probably be ignored. + // However, if it does become a problem we can add locks to the box group. + break; + } } } private void renderBox( From 52452e356dc995837b25557185d8ed48929e3b3d Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 9 Nov 2024 09:53:28 -0600 Subject: [PATCH 22/26] fix upload canceling --- .../render/bufferBuilding/ColumnRenderBuffer.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 d61394ad9..f8e2b6202 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/render/bufferBuilding/ColumnRenderBuffer.java @@ -105,15 +105,18 @@ public class ColumnRenderBuffer implements AutoCloseable { try { - if (Thread.interrupted()) + // skip this event if requested + if (Thread.interrupted() || this.uploadFuture.isCancelled()) { throw new InterruptedException(); } + // upload on the render thread uploadBuffersDirect(this.vbos, opaqueBuffers, gpuUploadMethod); uploadBuffersDirect(this.vbosTransparent, transparentBuffers, gpuUploadMethod); this.buffersUploaded = true; + // success this.uploadFuture.complete(this); this.uploadFuture = null; } From bd264086e314415faa6ea7dc81a540790bde3ab6 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Sat, 9 Nov 2024 22:36:25 +0500 Subject: [PATCH 23/26] Remove seed hash from local & server worlds, and expose DH's level identifier to API --- .../interfaces/world/IDhApiLevelWrapper.java | 7 ++++++ .../core/api/internal/ClientApi.java | 4 ++-- .../api/internal/ClientPluginChannelApi.java | 2 +- .../distanthorizons/core/config/Config.java | 21 +++--------------- .../FullDataToRenderDataTransformer.java | 5 +---- .../FullDataSourceProviderV2.java | 8 +++---- .../GeneratedFullDataSourceProvider.java | 2 +- .../structure/ClientOnlySaveStructure.java | 4 ++-- .../core/level/AbstractDhServerLevel.java | 10 ++++----- .../core/level/DhClientLevel.java | 4 ++-- .../core/level/DhClientServerLevel.java | 2 +- .../core/level/DhServerLevel.java | 2 +- .../AbstractFullDataNetworkRequestQueue.java | 3 +-- .../messages/ILevelRelatedMessage.java | 2 +- .../FullDataSourceRequestMessage.java | 2 +- .../core/render/LodQuadTree.java | 2 +- .../core/world/AbstractDhServerWorld.java | 3 +-- .../core/world/DhApiWorldProxy.java | 2 +- .../core/world/DhClientServerWorld.java | 4 ++-- .../core/world/DhClientWorld.java | 2 +- .../minecraft/IMinecraftSharedWrapper.java | 3 --- .../world/ILevelWrapper.java | 10 ++++----- .../world/IServerLevelWrapper.java | 22 +++++++++++-------- 23 files changed, 56 insertions(+), 70 deletions(-) diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java index 658a483b0..53d1161f6 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/world/IDhApiLevelWrapper.java @@ -39,6 +39,13 @@ public interface IDhApiLevelWrapper extends IDhApiUnsafeWrapper /** @since API 4.0.0 */ String getDimensionName(); + + /** + * Returns a string intended to uniquely identify this level. + * + * @since API 4.0.0 + */ + String getDhIdentifier(); EDhApiLevelType getLevelType(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java index 00897f8e4..8e5cde40a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientApi.java @@ -207,7 +207,7 @@ public class ClientApi { try { - LOGGER.info("Unloading client level [" + level.getClass().getSimpleName() + "]-["+level.getLevelIdString()+"]."); + LOGGER.info("Unloading client level [" + level.getClass().getSimpleName() + "]-[" + level.getDhIdentifier() + "]."); if (level instanceof IServerKeyedClientLevel) { @@ -253,7 +253,7 @@ public class ClientApi try { - LOGGER.info("Loading client level [" + levelWrapper + "]-["+levelWrapper.getLevelIdString()+"]."); + LOGGER.info("Loading client level [" + levelWrapper + "]-[" + levelWrapper.getDhIdentifier() + "]."); AbstractDhWorld world = SharedApi.getAbstractDhWorld(); if (world != null) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java index cf8fd1b91..05926b4f8 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/internal/ClientPluginChannelApi.java @@ -101,7 +101,7 @@ public class ClientPluginChannelApi } else { - LOGGER.info("Unloading non-keyed level: [" + clientLevel.getLevelIdString() + "]."); + LOGGER.info("Unloading non-keyed level: [" + clientLevel.getDhIdentifier() + "]."); this.levelUnloadHandler.accept(clientLevel); } 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 1dd476e92..6f807ce13 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 @@ -1542,11 +1542,11 @@ public class Config public static ConfigEntry levelKeyPrefix = new ConfigEntry.Builder() .setServersideShortName("levelKeyPrefix") - .set(getDefaultLevelKeyPrefix()) + .set("") .comment("" + "Prefix of the level keys sent to the clients.\n" - + "If the mod is running behind a proxy, each backend should use a unique value (an empty string is allowed for one of the servers).\n" - + "This value may be auto-generated if the mod is installed before the first start of the server.\n" + + "If the mod is running behind a proxy, each backend should use a unique value.\n" + + "If this value is empty, level key will be based on the server's seed hash.\n" + "") .build(); @@ -1633,21 +1633,6 @@ public class Config } } - private static String getDefaultLevelKeyPrefix() - { - IMinecraftSharedWrapper mcWrapper = SingletonInjector.INSTANCE.get(IMinecraftSharedWrapper.class); - if (mcWrapper.isDedicatedServer()) - { - return mcWrapper.isWorldNew() - ? "server" + ThreadLocalRandom.current().nextInt(1, 1000) - : ""; - } - else - { - return SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class).getUsername(); - } - } - /** Guesses whether a dev environment is used based on the current folder path */ private static boolean isRunningInDevEnvironment() { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java index 8af8bf9eb..b763e25ea 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/dataObjects/transformers/FullDataToRenderDataTransformer.java @@ -20,7 +20,6 @@ package com.seibel.distanthorizons.core.dataObjects.transformers; import com.seibel.distanthorizons.api.enums.config.EDhApiBlocksToAvoid; -import com.seibel.distanthorizons.api.enums.config.EDhApiVerticalQuality; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dataObjects.fullData.FullDataPointIdMap; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; @@ -40,12 +39,10 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrappe import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IBiomeWrapper; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; -import com.seibel.distanthorizons.coreapi.util.MathUtil; import it.unimi.dsi.fastutil.longs.LongArrayList; import it.unimi.dsi.fastutil.longs.LongOpenHashSet; import org.apache.logging.log4j.Logger; -import java.text.ParseException; import java.util.HashSet; /** @@ -236,7 +233,7 @@ public class FullDataToRenderDataTransformer if (!brokenPos.contains(fullDataMapping.getPos())) { brokenPos.add(fullDataMapping.getPos()); - String levelId = level.getLevelWrapper().getLevelIdString(); + String levelId = level.getLevelWrapper().getDhIdentifier(); LOGGER.warn("Unable to get data point with id ["+id+"] " + "(Max possible ID: ["+fullDataMapping.getMaxValidId()+"]) " + "for pos ["+fullDataMapping.getPos()+"] in level ["+levelId+"]. " + diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java index cb8538c51..6a8f4b201 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/FullDataSourceProviderV2.java @@ -130,7 +130,7 @@ public class FullDataSourceProviderV2 DebugRenderer.register(this, Config.Client.Advanced.Debugging.DebugWireframe.showFullDataUpdateStatus); - String levelId = level.getLevelWrapper().getLevelIdString(); + String levelId = level.getLevelWrapper().getDhIdentifier(); // start migrating any legacy data sources present in the background this.migrationThreadPool = ThreadUtil.makeRateLimitedThreadPool(1, MIGRATION_THREAD_NAME_PREFIX + "["+levelId+"]", Config.Common.MultiThreading.runTimeRatioForUpdatePropagatorThreads.get(), Thread.MIN_PRIORITY, (Semaphore) null); @@ -346,7 +346,7 @@ public class FullDataSourceProviderV2 private void convertLegacyDataSources() { - String levelId = this.level.getLevelWrapper().getLevelIdString(); + String levelId = this.level.getLevelWrapper().getDhIdentifier(); LOGGER.info("Attempting to migrate data sources for: ["+levelId+"]-["+this.saveDir+"]..."); @@ -525,7 +525,7 @@ public class FullDataSourceProviderV2 } this.migrationStartMessageQueued = true; - String levelId = this.level.getLevelWrapper().getLevelIdString(); + String levelId = this.level.getLevelWrapper().getDhIdentifier(); ClientApi.INSTANCE.showChatMessageNextFrame( "Old Distant Horizons data is being migrated for ["+levelId+"]. \n" + "While migrating LODs may load slowly \n" + @@ -536,7 +536,7 @@ public class FullDataSourceProviderV2 private void showMigrationEndMessage(boolean success) { - String levelId = this.level.getLevelWrapper().getLevelIdString(); + String levelId = this.level.getLevelWrapper().getDhIdentifier(); if (success) { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java index 37b6ac504..4aeacfb65 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/GeneratedFullDataSourceProvider.java @@ -145,7 +145,7 @@ public class GeneratedFullDataSourceProvider extends FullDataSourceProviderV2 im { boolean oldQueueExists = this.worldGenQueueRef.compareAndSet(null, newWorldGenQueue); LodUtil.assertTrue(oldQueueExists, "previous world gen queue is still here!"); - LOGGER.info("Set world gen queue for level [" + this.level.getLevelWrapper().getLevelIdString() + "]."); + LOGGER.info("Set world gen queue for level [" + this.level.getLevelWrapper().getDhIdentifier() + "]."); } @Override diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java index fb4db25ce..d9e7c6bbe 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/structure/ClientOnlySaveStructure.java @@ -80,14 +80,14 @@ public class ClientOnlySaveStructure implements ISaveStructure if (newLevelWrapper instanceof IServerKeyedClientLevel) { IServerKeyedClientLevel keyedClientLevel = (IServerKeyedClientLevel) newLevelWrapper; - LOGGER.info("Loading level [" + newLevelWrapper.getLevelIdString() + "] with key: [" + keyedClientLevel.getServerLevelKey() + "]."); + LOGGER.info("Loading level [" + newLevelWrapper.getDhIdentifier() + "] with key: [" + keyedClientLevel.getServerLevelKey() + "]."); // This world was identified by the server directly, so we can know for sure which folder to use. saveFolder = getSaveFolderByLevelId(keyedClientLevel.getServerLevelKey()); } else { // get the default folder - saveFolder = getSaveFolderByLevelId(levelWrapper.getLevelIdString()); + saveFolder = getSaveFolderByLevelId(levelWrapper.getDhIdentifier()); } // Allow API users to override the save folder diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java index d08e8070c..059697189 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/AbstractDhServerLevel.java @@ -114,7 +114,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I continue; } - NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getLevelIdString()+"] Fulfilled request group ["+entry.getKey()+"]"); + NETWORK_LOGGER.debug("[" + this.serverLevelWrapper.getDhIdentifier() + "] Fulfilled request group [" + entry.getKey() + "]"); // Make this group unavailable for adding into this.requestGroupByPos.remove(entry.getKey()); @@ -228,7 +228,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I FullDataSourceRequestMessage requestMessage = requestGroup.requestMessages.remove(msg.futureId); if (requestGroup.requestMessages.isEmpty()) { - NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getLevelIdString()+"] Cancelled request group ["+DhSectionPos.toString(requestMessage.sectionPos)+"]."); + NETWORK_LOGGER.debug("[" + this.serverLevelWrapper.getDhIdentifier() + "] Cancelled request group [" + DhSectionPos.toString(requestMessage.sectionPos) + "]."); this.requestGroupByPos.remove(requestMessage.sectionPos); this.serverside.fullDataFileHandler.removeRetrievalRequestIf(pos -> pos == requestMessage.sectionPos); } @@ -307,7 +307,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I { DataSourceRequestGroup newGroup = new DataSourceRequestGroup(); this.tryFulfillDataSourceRequestGroup(newGroup, pos); - NETWORK_LOGGER.debug("["+this.serverLevelWrapper.getLevelIdString()+"] Created request group for pos ["+DhSectionPos.toString(pos)+"]."); + NETWORK_LOGGER.debug("[" + this.serverLevelWrapper.getDhIdentifier() + "] Created request group for pos [" + DhSectionPos.toString(pos) + "]."); return newGroup; }); @@ -353,8 +353,8 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I new InvalidLevelException( "Generation not allowed. " + "Requested dimension: ["+((ILevelRelatedMessage) message).getLevelName()+"], " + - "player dimension: ["+message.getSession().serverPlayer.getLevel().getLevelIdString()+"], " + - "handler dimension: ["+this.getLevelWrapper().getLevelIdString()+"]" + "player dimension: [" + message.getSession().serverPlayer.getLevel().getDhIdentifier() + "], " + + "handler dimension: [" + this.getLevelWrapper().getDhIdentifier() + "]" ) ); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java index 4fd2ee944..4d67fa2dc 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientLevel.java @@ -294,7 +294,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel @Override public void addDebugMenuStringsToList(List messageList) { - String dimName = this.levelWrapper.getDimensionName(); + String dimName = this.levelWrapper.getDhIdentifier(); boolean rendering = this.clientside.isRendering(); messageList.add("["+dimName+"] rendering: "+(rendering ? "yes" : "no")); @@ -338,7 +338,7 @@ public class DhClientLevel extends AbstractDhLevel implements IDhClientLevel //================// @Override - public String toString() { return "DhClientLevel{"+this.getClientLevelWrapper().getLevelIdString()+"}"; } + public String toString() { return "DhClientLevel{" + this.getClientLevelWrapper().getDhIdentifier() + "}"; } @Override public void close() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java index 357a68521..0ffe9a7de 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhClientServerLevel.java @@ -132,7 +132,7 @@ public class DhClientServerLevel extends AbstractDhServerLevel implements IDhCli public void addDebugMenuStringsToList(List messageList) { // header - String dimName = this.serverLevelWrapper.getDimensionName(); + String dimName = this.serverLevelWrapper.getDhIdentifier(); boolean rendering = this.clientside.isRendering(); messageList.add("["+dimName+"] rendering: "+(rendering ? "yes" : "no")); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java index 0dc6640b4..ecd3fcfb0 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/DhServerLevel.java @@ -78,7 +78,7 @@ public class DhServerLevel extends AbstractDhServerLevel @Override public void addDebugMenuStringsToList(List messageList) { - messageList.add("[" + this.serverLevelWrapper.getDimensionName() + "]"); + messageList.add("[" + this.serverLevelWrapper.getDhIdentifier() + "]"); super.addDebugMenuStringsToList(messageList); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java index 2731a0b94..70779b70d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/AbstractFullDataNetworkRequestQueue.java @@ -19,7 +19,6 @@ import com.seibel.distanthorizons.core.render.renderer.DebugRenderer; import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import com.seibel.distanthorizons.core.util.LodUtil; -import com.seibel.distanthorizons.core.util.TimerUtil; import com.seibel.distanthorizons.core.util.objects.DataCorruptedException; import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; @@ -291,7 +290,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende public void addDebugMenuStringsToList(List messageList) { - messageList.add(this.getQueueName() + " [" + this.level.getClientLevelWrapper().getLevelIdString() + "]"); + messageList.add(this.getQueueName() + " [" + this.level.getClientLevelWrapper().getDhIdentifier() + "]"); messageList.add("Requests: " + this.finishedRequests + " / " + (this.getWaitingTaskCount() + this.finishedRequests.get()) + " (failed: " + this.failedRequests + ", rate limit: " + this.getRequestRateLimit() + ")"); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java index 43d5bd37d..5176bb845 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/ILevelRelatedMessage.java @@ -17,7 +17,7 @@ public interface ILevelRelatedMessage return this.getLevelName().equals(((IServerLevelWrapper) levelWrapper).getKeyedLevelDimensionName()); } - return this.getLevelName().equals(levelWrapper.getDimensionName()); + return this.getLevelName().equals(levelWrapper.getDhIdentifier()); } } \ No newline at end of file diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java index 683b1df66..ca19b181a 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/fullData/FullDataSourceRequestMessage.java @@ -48,7 +48,7 @@ public class FullDataSourceRequestMessage extends AbstractTrackableMessage imple public FullDataSourceRequestMessage() {} public FullDataSourceRequestMessage(ILevelWrapper levelWrapper, long sectionPos, @Nullable Long clientTimestamp) { - this.levelName = levelWrapper.getDimensionName(); + this.levelName = levelWrapper.getDhIdentifier(); this.sectionPos = sectionPos; this.clientTimestamp = clientTimestamp; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java index 2d7eac30d..dd0b123ea 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/LodQuadTree.java @@ -147,7 +147,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen } catch (Exception e) { - LOGGER.error("Quad Tree tick exception for level: [" + this.level.getLevelWrapper().getLevelIdString() + "], error: [" + e.getMessage() + "].", e); + LOGGER.error("Quad Tree tick exception for level: [" + this.level.getLevelWrapper().getDhIdentifier() + "], error: [" + e.getMessage() + "].", e); } finally { diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java index 0b02b85c6..36677667d 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhServerWorld.java @@ -10,7 +10,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper; import org.jetbrains.annotations.NotNull; -import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.concurrent.ConcurrentHashMap; @@ -123,7 +122,7 @@ public abstract class AbstractDhServerWorld Date: Sat, 9 Nov 2024 20:58:45 -0600 Subject: [PATCH 24/26] Fix height fog --- .../api/enums/rendering/EDhApiFogFalloff.java | 17 +- ...ode.java => EDhApiHeightFogDirection.java} | 24 +- .../rendering/EDhApiHeightFogMixMode.java | 49 +- .../config/client/IDhApiHeightFogConfig.java | 11 +- .../config/client/DhApiHeightFogConfig.java | 6 +- .../distanthorizons/core/config/Config.java | 37 +- .../core/render/fog/LodFogConfig.java | 420 ------------------ .../render/glObject/shader/ShaderProgram.java | 119 +++-- .../core/render/renderer/LodRenderer.java | 20 +- .../render/renderer/shaders/FogShader.java | 149 +++++-- .../assets/distanthorizons/lang/en_us.json | 28 +- core/src/main/resources/shaders/fog/fog.frag | 293 +++++++++--- 12 files changed, 513 insertions(+), 660 deletions(-) rename api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/{EDhApiHeightFogMode.java => EDhApiHeightFogDirection.java} (68%) delete mode 100644 core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java diff --git a/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiFogFalloff.java b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiFogFalloff.java index 3437cd00a..8fbe37a2a 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiFogFalloff.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiFogFalloff.java @@ -25,7 +25,7 @@ package com.seibel.distanthorizons.api.enums.rendering; * EXPONENTIAL_SQUARED
* * @author Leetom - * @version 2022-6-30 + * @version 2024-11-09 * @since API 2.0.0 */ public enum EDhApiFogFalloff @@ -35,8 +35,17 @@ public enum EDhApiFogFalloff // when removing items up the API major version - LINEAR, - EXPONENTIAL, - EXPONENTIAL_SQUARED, + LINEAR(0), + EXPONENTIAL(1), + EXPONENTIAL_SQUARED(2); + + + /** + * Stable version of {@link EDhApiFogFalloff#ordinal()} + * @since API 4.0.0 + */ + public final int value; + + EDhApiFogFalloff(int value) { this.value = value; } } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogMode.java b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogDirection.java similarity index 68% rename from api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogMode.java rename to api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogDirection.java index 81a847630..624b391f6 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogMode.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogDirection.java @@ -31,28 +31,28 @@ package com.seibel.distanthorizons.api.enums.rendering; * @version 2024-4-6 * @since API 2.0.0 */ -public enum EDhApiHeightFogMode +public enum EDhApiHeightFogDirection { // Reminder: // when adding items up the API minor version // when removing items up the API major version - ABOVE_CAMERA(true, true, false), - BELOW_CAMERA(true, false, true), - ABOVE_AND_BELOW_CAMERA(true, true, true), - ABOVE_SET_HEIGHT(false, true, false), - BELOW_SET_HEIGHT(false, false, true), - ABOVE_AND_BELOW_SET_HEIGHT(false, true, true); + ABOVE_CAMERA (true, true, false), + BELOW_CAMERA (true, false, true), + ABOVE_AND_BELOW_CAMERA (true, true, true), + ABOVE_SET_HEIGHT (false, true, false), + BELOW_SET_HEIGHT (false, false, true), + ABOVE_AND_BELOW_SET_HEIGHT (false, true, true); public final boolean basedOnCamera; - public final boolean above; - public final boolean below; + public final boolean fogAppliesUp; + public final boolean fogAppliesDown; - EDhApiHeightFogMode(boolean basedOnCamera, boolean above, boolean below) + EDhApiHeightFogDirection(boolean basedOnCamera, boolean fogAppliesUp, boolean fogAppliesDown) { this.basedOnCamera = basedOnCamera; - this.above = above; - this.below = below; + this.fogAppliesUp = fogAppliesUp; + this.fogAppliesDown = fogAppliesDown; } } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogMixMode.java b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogMixMode.java index 56226f2da..c2e5e7f38 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogMixMode.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/enums/rendering/EDhApiHeightFogMixMode.java @@ -20,10 +20,11 @@ package com.seibel.distanthorizons.api.enums.rendering; /** - * BASIC
- * IGNORE_HEIGHT
- * ADDITION
+ * SPHERICAL
+ * CYLINDRICAL
+ *
* MAX
+ * ADDITION
* MULTIPLY
* INVERSE_MULTIPLY
* LIMITED_ADDITION
@@ -37,14 +38,36 @@ package com.seibel.distanthorizons.api.enums.rendering; */ public enum EDhApiHeightFogMixMode { - BASIC, - IGNORE_HEIGHT, - ADDITION, - MAX, - MULTIPLY, - INVERSE_MULTIPLY, - LIMITED_ADDITION, - MULTIPLY_ADDITION, - INVERSE_MULTIPLY_ADDITION, - AVERAGE, + /** + * Basic just means the fog will be based on the fragment depth + * not on any special height calculation IE spherical fog.

+ * + * Not to be confused with {@link EDhApiHeightFogMixMode#CYLINDRICAL} + * which causes fog to only apply based on horizontal distance. + */ + SPHERICAL(0), + /** + * Fog is applied based on horizontal distance from the camera, + * IE cylindrical fog. + */ + CYLINDRICAL(1), + + MAX(2), + ADDITION(3), + MULTIPLY(4), + INVERSE_MULTIPLY(5), + LIMITED_ADDITION(6), + MULTIPLY_ADDITION(7), + INVERSE_MULTIPLY_ADDITION(8), + AVERAGE(9); + + + /** + * Stable version of {@link EDhApiFogFalloff#ordinal()} + * @since API 4.0.0 + */ + public final int value; + + EDhApiHeightFogMixMode(int value) { this.value = value; } + } diff --git a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiHeightFogConfig.java b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiHeightFogConfig.java index 754cc01a9..f348f94ba 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiHeightFogConfig.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/interfaces/config/client/IDhApiHeightFogConfig.java @@ -21,7 +21,7 @@ package com.seibel.distanthorizons.api.interfaces.config.client; import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogFalloff; import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode; -import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMode; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogDirection; import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigGroup; import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue; @@ -42,11 +42,14 @@ public interface IDhApiHeightFogConfig extends IDhApiConfigGroup /** Defines how the height fog mixes. */ IDhApiConfigValue heightFogMixMode(); - /** Defines how the height fog is drawn relative to the camera or world. */ - IDhApiConfigValue heightFogMode(); + /** + * Defines which direction height fog is drawn relative to the world. + * @since API 4.0.0 + */ + IDhApiConfigValue heightFogDirection(); /** - * Defines the height fog's base height if {@link IDhApiHeightFogConfig#heightFogMode()} + * Defines the height fog's base height if {@link IDhApiHeightFogConfig#heightFogDirection()} * is set to use a specific height. */ IDhApiConfigValue heightFogBaseHeight(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiHeightFogConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiHeightFogConfig.java index b65925172..05f3b3e43 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiHeightFogConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/api/external/methods/config/client/DhApiHeightFogConfig.java @@ -21,7 +21,7 @@ package com.seibel.distanthorizons.core.api.external.methods.config.client; import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogFalloff; import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode; -import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMode; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogDirection; import com.seibel.distanthorizons.api.interfaces.config.IDhApiConfigValue; import com.seibel.distanthorizons.api.interfaces.config.client.IDhApiHeightFogConfig; import com.seibel.distanthorizons.api.objects.config.DhApiConfigValue; @@ -40,8 +40,8 @@ public class DhApiHeightFogConfig implements IDhApiHeightFogConfig { return new DhApiConfigValue(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMixMode); } @Override - public IDhApiConfigValue heightFogMode() - { return new DhApiConfigValue(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMode); } + public IDhApiConfigValue heightFogDirection() + { return new DhApiConfigValue(Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDirection); } @Override public IDhApiConfigValue heightFogBaseHeight() diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 1dd476e92..73910ed05 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java @@ -39,7 +39,6 @@ import com.seibel.distanthorizons.coreapi.ModInfo; import org.apache.logging.log4j.Logger; import org.lwjgl.util.tinyfd.TinyFileDialogs; -import javax.swing.*; import java.awt.*; import java.io.File; import java.util.*; @@ -549,40 +548,40 @@ public class Config public static class HeightFog { public static ConfigEntry heightFogMixMode = new ConfigEntry.Builder() - .set(EDhApiHeightFogMixMode.BASIC) + .set(EDhApiHeightFogMixMode.SPHERICAL) .comment("" + "How should height effect the fog thickness? \n" + "Note: height fog is combined with the other fog settings. \n" + "\n" - + EDhApiHeightFogMixMode.BASIC + ": No special height fog effect. Fog is calculated based on camera distance \n" - + EDhApiHeightFogMixMode.IGNORE_HEIGHT + ": Ignore height completely. Fog is only calculated with horizontal distance \n" - + EDhApiHeightFogMixMode.ADDITION + ": heightFog + farFog \n" + + EDhApiHeightFogMixMode.SPHERICAL + ": Fog is calculated based on camera distance. \n" + + EDhApiHeightFogMixMode.CYLINDRICAL + ": Ignore height, fog is calculated based on horizontal distance. \n" + + "\n" + EDhApiHeightFogMixMode.MAX + ": max(heightFog, farFog) \n" + + EDhApiHeightFogMixMode.ADDITION + ": heightFog + farFog \n" + EDhApiHeightFogMixMode.MULTIPLY + ": heightFog * farFog \n" + EDhApiHeightFogMixMode.INVERSE_MULTIPLY + ": 1 - (1-heightFog) * (1-farFog) \n" + EDhApiHeightFogMixMode.LIMITED_ADDITION + ": farFog + max(farFog, heightFog) \n" + EDhApiHeightFogMixMode.MULTIPLY_ADDITION + ": farFog + farFog * heightFog \n" + EDhApiHeightFogMixMode.INVERSE_MULTIPLY_ADDITION + ": farFog + 1 - (1-heightFog) * (1-farFog) \n" + EDhApiHeightFogMixMode.AVERAGE + ": farFog*0.5 + heightFog*0.5 \n" - + "\n" - + "Note: height fog settings are ignored if '" + EDhApiHeightFogMixMode.BASIC + "' or '" + EDhApiHeightFogMixMode.IGNORE_HEIGHT + "' are selected.") + + "\n") .build(); - public static ConfigEntry heightFogMode = new ConfigEntry.Builder() - .set(EDhApiHeightFogMode.ABOVE_AND_BELOW_CAMERA) + public static ConfigEntry heightFogDirection = new ConfigEntry.Builder() + .set(EDhApiHeightFogDirection.BELOW_SET_HEIGHT) .comment("" + "Where should the height fog start? \n" + "\n" - + EDhApiHeightFogMode.ABOVE_CAMERA + ": Height fog starts at the camera and goes towards the sky \n" - + EDhApiHeightFogMode.BELOW_CAMERA + ": Height fog starts at the camera and goes towards the void \n" - + EDhApiHeightFogMode.ABOVE_AND_BELOW_CAMERA + ": Height fog starts from the camera to goes towards both the sky and void \n" - + EDhApiHeightFogMode.ABOVE_SET_HEIGHT + ": Height fog starts from a set height and goes towards the sky \n" - + EDhApiHeightFogMode.BELOW_SET_HEIGHT + ": Height fog starts from a set height and goes towards the void \n" - + EDhApiHeightFogMode.ABOVE_AND_BELOW_SET_HEIGHT + ": Height fog starts from a set height and goes towards both the sky and void") + + EDhApiHeightFogDirection.ABOVE_CAMERA + ": Height fog starts at the camera and goes towards the sky \n" + + EDhApiHeightFogDirection.BELOW_CAMERA + ": Height fog starts at the camera and goes towards the void \n" + + EDhApiHeightFogDirection.ABOVE_AND_BELOW_CAMERA + ": Height fog starts from the camera to goes towards both the sky and void \n" + + EDhApiHeightFogDirection.ABOVE_SET_HEIGHT + ": Height fog starts from a set height and goes towards the sky \n" + + EDhApiHeightFogDirection.BELOW_SET_HEIGHT + ": Height fog starts from a set height and goes towards the void \n" + + EDhApiHeightFogDirection.ABOVE_AND_BELOW_SET_HEIGHT + ": Height fog starts from a set height and goes towards both the sky and void") .build(); public static ConfigEntry heightFogBaseHeight = new ConfigEntry.Builder() - .setMinDefaultMax(-4096.0, 70.0, 4096.0) + .setMinDefaultMax(-4096.0, 80.0, 4096.0) .comment("If the height fog is calculated around a set height, what is that height position?") .build(); @@ -596,7 +595,7 @@ public class Config .build(); public static ConfigEntry heightFogEnd = new ConfigEntry.Builder() - .setMinDefaultMax(FOG_RANGE_MIN, 1.0, FOG_RANGE_MAX) + .setMinDefaultMax(FOG_RANGE_MIN, 0.6, FOG_RANGE_MAX) .comment("" + "Should the end of the height fog be offset? \n" + "\n" @@ -605,7 +604,7 @@ public class Config .build(); public static ConfigEntry heightFogMin = new ConfigEntry.Builder() - .setMinDefaultMax(-5.0, 0.0, FOG_RANGE_MAX) + .setMinDefaultMax(0.0, 0.0, FOG_RANGE_MAX) .comment("" + "What is the minimum fog thickness? \n" + "\n" @@ -633,7 +632,7 @@ public class Config .build(); public static ConfigEntry heightFogDensity = new ConfigEntry.Builder() - .setMinDefaultMax(0.01, 2.5, 50.0) + .setMinDefaultMax(0.01, 20.0, 50.0) .comment("What is the height fog's density?") .build(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java deleted file mode 100644 index 3ea6f8455..000000000 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/fog/LodFogConfig.java +++ /dev/null @@ -1,420 +0,0 @@ -/* - * This file is part of the Distant Horizons mod - * licensed under the GNU LGPL v3 License. - * - * Copyright (C) 2020-2023 James Seibel - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, version 3. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this program. If not, see . - */ - -package com.seibel.distanthorizons.core.render.fog; - -import com.seibel.distanthorizons.api.enums.rendering.*; -import com.seibel.distanthorizons.core.config.Config; -import com.seibel.distanthorizons.core.dependencyInjection.ModAccessorInjector; -import com.seibel.distanthorizons.core.render.glObject.GLProxy; -import com.seibel.distanthorizons.core.render.glObject.shader.Shader; -import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IOptifineAccessor; - -import java.io.FileOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.Objects; - -/** - * This holds fog related settings and - * creates the fog related shader code. - * - * @author Leetom - * @author James Seibel - * @version 2022-11-24 - */ -// TODO: Move lots out of here, there should be a listener hooked onto the config to update the shader -public class LodFogConfig -{ - private static final IOptifineAccessor OPTIFINE = ModAccessorInjector.INSTANCE.get(IOptifineAccessor.class); - - public static final boolean DEBUG_DUMP_GENERATED_CODE = false; - - public final FogSettings farFogSetting; - public final FogSettings heightFogSetting; - public final EDhApiHeightFogMixMode heightFogMixMode; - public final EDhApiHeightFogMode heightFogMode; - public final float heightFogHeight; - - // TODO: Move these out of here - public final int earthCurveRatio; - - // Noise Values - public final boolean noiseEnable; - public final int noiseSteps; - public final float noiseIntensity; - public final int noiseDropoff; - - - public static LodFogConfig generateFogConfig() { return new LodFogConfig(); } - - /** sets all fog options from the config */ - private LodFogConfig() - { - // TODO: Move these out of here - this.earthCurveRatio = Config.Client.Advanced.Graphics.Experimental.earthCurveRatio.get(); - - this.noiseEnable = Config.Client.Advanced.Graphics.NoiseTexture.enableNoiseTexture.get(); - this.noiseSteps = Config.Client.Advanced.Graphics.NoiseTexture.noiseSteps.get(); - this.noiseIntensity = Config.Client.Advanced.Graphics.NoiseTexture.noiseIntensity.get().floatValue(); - this.noiseDropoff = Config.Client.Advanced.Graphics.NoiseTexture.noiseDropoff.get(); - - - if (Config.Client.Advanced.Graphics.Fog.enableDhFog.get()) - { - // fog should be drawn - - this.farFogSetting = new FogSettings( - Config.Client.Advanced.Graphics.Fog.farFogStart.get(), - Config.Client.Advanced.Graphics.Fog.farFogEnd.get(), - Config.Client.Advanced.Graphics.Fog.farFogMin.get(), - Config.Client.Advanced.Graphics.Fog.farFogMax.get(), - Config.Client.Advanced.Graphics.Fog.farFogDensity.get(), - Config.Client.Advanced.Graphics.Fog.farFogFalloff.get() - ); - - this.heightFogMixMode = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMixMode.get(); - if (this.heightFogMixMode == EDhApiHeightFogMixMode.IGNORE_HEIGHT - || this.heightFogMixMode == EDhApiHeightFogMixMode.BASIC) - { - // basic fog mixing - - this.heightFogSetting = null; - this.heightFogMode = null; - this.heightFogHeight = 0.f; - } - else - { - // advanced fog mixing - - this.heightFogSetting = new FogSettings( - Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDensity.get(), - Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogEnd.get(), - Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMin.get(), - Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMax.get(), - Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDensity.get(), - Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogFalloff.get() - ); - - this.heightFogMode = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMode.get(); - - if (this.heightFogMode.basedOnCamera) - { - this.heightFogHeight = 0.f; - } - else - { - this.heightFogHeight = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogBaseHeight.get().floatValue(); - } - } - } - else - { - // fog disabled - - this.farFogSetting = null; - this.heightFogMixMode = null; - this.heightFogMode = null; - this.heightFogSetting = null; - this.heightFogHeight = 0.f; - } - } - - public StringBuilder loadAndProcessFragShader(String path, boolean absoluteFilePath) - { - StringBuilder stringBuilder = this.makeRuntimeDefine(); - this.generateRuntimeShaderCode(Shader.loadFile(path, absoluteFilePath, stringBuilder)); - - if (DEBUG_DUMP_GENERATED_CODE) - { - try (FileOutputStream file = new FileOutputStream("debugGenerated.frag", false)) - { - file.write(stringBuilder.toString().getBytes(StandardCharsets.UTF_8)); - GLProxy.GL_LOGGER.info("Debug dumped generated code to debugGenerated.frag for {}", path); - } - catch (IOException e) - { - GLProxy.GL_LOGGER.warn("Failed to debug dump generated code to file for {}", path); - } - } - - return stringBuilder; - } - - /** Generates the necessary constants for a fragment shader */ - private void generateRuntimeShaderCode(StringBuilder str) - { - str.append("// =======RUNTIME GENERATED CODE SECTION======== //\n"); - - // Generate method: float getNearFogThickness(float dist); - str.append("" + - "float getNearFogThickness(float dist) \n" + - "{ \n" + - " return linearFog(dist, uNearFogStart, uNearFogLength, 0.0, 1.0); \n" + - "} \n"); - - - if (this.farFogSetting == null) - { - str.append("\n" + - "float getFarFogThickness(float dist) { return 0.0; } \n" + - "float getHeightFogThickness(float dist) { return 0.0; } \n" + - "float calculateFarFogDepth(float horizontal, float dist, float uNearFogStart) { return 0.0; } \n" + - "float calculateHeightFogDepth(float vertical, float realY) { return 0.0; } \n" + - "float mixFogThickness(float near, float far, float height) \n" + - "{ \n" + - " return 0.0; \n" + - "} \n\n"); - } - else - { - // Generate method: float getFarFogThickness(float dist); - str.append("" + - "float getFarFogThickness(float dist) \n" + - "{ \n" + - getFarFogMethod(this.farFogSetting.fogType) + "\n" + - "} \n"); - - - // Generate method: float getHeightFogThickness(float dist); - str.append("" + - "float getHeightFogThickness(float dist) \n" + - "{ \n" + - (this.heightFogSetting != null ? getHeightFogMethod(this.heightFogSetting.fogType) : " return 0.0;") + "\n" + - "} \n"); - - - // Generate method: float calculateHeightFogDepth(float vertical, float realY); - str.append("" + - "float calculateHeightFogDepth(float vertical, float realY) \n" + - "{ \n" + - (this.heightFogSetting != null ? getHeightDepthMethod(this.heightFogMode, this.heightFogHeight) : " return 0.0;") + "\n" + - "} \n"); - - - // Generate method: calculateFarFogDepth(float horizontal, float dist, float uNearFogStart); - str.append("" + - "float calculateFarFogDepth(float horizontal, float dist, float uNearFogStart) \n" + - "{ \n" + - " return " + (this.heightFogMixMode == EDhApiHeightFogMixMode.BASIC ? - "(dist - uNearFogStart)/(1.0 - uNearFogStart);" : - "(horizontal - uNearFogStart)/(1.0 - uNearFogStart);") + - "} \n"); - - // Generate method: float mixFogThickness(float near, float far, float height); - str.append("" + - "float mixFogThickness(float near, float far, float height) \n" + - "{ \n" + - getMixFogLine(this.heightFogMixMode) + "\n" + - "} \n"); - } - } - - - - //=================// - // shader creation // - // helper methods // - //=================// - - private StringBuilder makeRuntimeDefine() - { - StringBuilder str = new StringBuilder(); - str.append("// =======RUNTIME GENERATED DEFINE SECTION======== //\n"); - str.append("#version 150 core\n"); - - FogSettings activeFarFogSetting = this.farFogSetting != null ? this.farFogSetting : FogSettings.EMPTY; - FogSettings activeHeightFogSetting = this.heightFogSetting != null ? this.heightFogSetting : FogSettings.EMPTY; - - str.append("\n" + - "#define farFogStart " + activeFarFogSetting.start + "\n" + - "#define farFogLength " + (activeFarFogSetting.end - activeFarFogSetting.start) + "\n" + - "#define farFogMin " + activeFarFogSetting.min + "\n" + - "#define farFogRange " + (activeFarFogSetting.max - activeFarFogSetting.min) + "\n" + - "#define farFogDensity " + activeFarFogSetting.density + "\n" + - "\n" + - "#define heightFogStart " + activeHeightFogSetting.start + "\n" + - "#define heightFogLength " + (activeHeightFogSetting.end - activeHeightFogSetting.start) + "\n" + - "#define heightFogMin " + activeHeightFogSetting.min + "\n" + - "#define heightFogRange " + (activeHeightFogSetting.max - activeHeightFogSetting.min) + "\n" + - "#define heightFogDensity " + activeHeightFogSetting.density + "\n" + - "\n"); - - str.append("// =======RUNTIME END======== //\n"); - return str; - } - - private static String getFarFogMethod(EDhApiFogFalloff fogType) - { - switch (fogType) - { - case LINEAR: - return "return linearFog(dist, farFogStart, farFogLength, farFogMin, farFogRange);\n"; - case EXPONENTIAL: - return "return exponentialFog(dist, farFogStart, farFogLength, farFogMin, farFogRange, farFogDensity);\n"; - case EXPONENTIAL_SQUARED: - return "return exponentialSquaredFog(dist, farFogStart, farFogLength, farFogMin, farFogRange, farFogDensity);\n"; - - default: - throw new IllegalArgumentException("FogType [" + fogType + "] not implemented for [getFarFogMethod]."); - } - } - - private static String getHeightDepthMethod(EDhApiHeightFogMode heightMode, float heightFogHeight) - { - String str = ""; - if (!heightMode.basedOnCamera) - { - str = " vertical = realY - (" + heightFogHeight + ");\n"; - } - - if (heightMode.below && heightMode.above) - { - str += " return abs(vertical);\n"; - } - else if (heightMode.below) - { - str += " return -vertical;\n"; - } - else if (heightMode.above) - { - str += " return vertical;\n"; - } - else - { - str += " return 0;\n"; - } - return str; - } - - /** - * Returns the method call for the given fog type.
- * Example:
- * " return linearFog(dist, heightFogStart, heightFogLength, heightFogMin, heightFogRange);" - */ - private static String getHeightFogMethod(EDhApiFogFalloff fogType) - { - switch (fogType) - { - case LINEAR: - return " return linearFog(dist, heightFogStart, heightFogLength, heightFogMin, heightFogRange);\n"; - case EXPONENTIAL: - return " return exponentialFog(dist, heightFogStart, heightFogLength, heightFogMin, heightFogRange, heightFogDensity);\n"; - case EXPONENTIAL_SQUARED: - return " return exponentialSquaredFog(dist, heightFogStart, heightFogLength, heightFogMin, heightFogRange, heightFogDensity);\n"; - - default: - throw new IllegalArgumentException("FogType [" + fogType + "] not implemented for [getHeightFogMethod]."); - } - } - - /** - * creates a line in the format
- * " return max(1.0-near, far);" - */ - private static String getMixFogLine(EDhApiHeightFogMixMode heightFogMode) - { - String str = " return "; - - switch (heightFogMode) - { - case BASIC: - case IGNORE_HEIGHT: - str += "near * far;\n"; - break; - - case ADDITION: - str += "near * (far + height);\n"; - break; - - case MAX: - str += "near * max(far, height);\n"; - break; - - case INVERSE_MULTIPLY: - str += "near * (1.0 - (1.0-far)*(1.0-height));\n"; - break; - - case MULTIPLY: - str += "near * far * height;\n"; - break; - - case LIMITED_ADDITION: - str += "near * (far + max(far, height));\n"; - break; - - case MULTIPLY_ADDITION: - str += "near * (far + far*height);\n"; - break; - - case INVERSE_MULTIPLY_ADDITION: - str += "near * (far + 1.0 - (1.0-far)*(1.0-height));\n"; - break; - - case AVERAGE: - str += "near * (far*0.5 + height*0.5);\n"; - break; - - default: - throw new IllegalArgumentException("FogType [" + heightFogMode + "] not implemented for [getMixFogMethod]."); - } - - return str; - } - - - - - - - //========================// - // default object methods // - //========================// - - @Override - public boolean equals(Object other) - { - if (this == other) - { - return true; - } - else if (other == null || this.getClass() != other.getClass()) - { - return false; - } - else - { - LodFogConfig that = (LodFogConfig) other; - return Float.compare(that.heightFogHeight, this.heightFogHeight) == 0 && - Objects.equals(this.farFogSetting, that.farFogSetting) && - Objects.equals(this.heightFogSetting, that.heightFogSetting) && this.heightFogMixMode == that.heightFogMixMode && - this.heightFogMode == that.heightFogMode - // TODO: Move these out of here - && this.earthCurveRatio == that.earthCurveRatio - && this.noiseEnable == that.noiseEnable && this.noiseSteps == that.noiseSteps && this.noiseIntensity == that.noiseIntensity && this.noiseDropoff == that.noiseDropoff; - } - } - - @Override - public int hashCode() - { - return Objects.hash(this.farFogSetting, this.heightFogSetting, this.heightFogMixMode, this.heightFogMode, this.heightFogHeight, this.earthCurveRatio, this.noiseEnable, this.noiseSteps, this.noiseIntensity, this.noiseDropoff); - } - -} diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java index fc821a461..94c1284a1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/glObject/shader/ShaderProgram.java @@ -49,6 +49,8 @@ public class ShaderProgram /** Stores the handle of the program. */ public final int id; + + // TODO: A better way to set the fragData output name /** * Creates a shader program. @@ -73,20 +75,20 @@ public class ShaderProgram } - public ShaderProgram(List> vert, List> frag, String[] attributes) + public ShaderProgram(List> vertSupplierList, List> fragSupplierList, String[] attributes) { - id = GL32.glCreateProgram(); + this.id = GL32.glCreateProgram(); - for (Supplier v : vert) + for (Supplier vertSupplier : vertSupplierList) { - Shader vertShader = new Shader(GL32.GL_VERTEX_SHADER, v.get()); + Shader vertShader = new Shader(GL32.GL_VERTEX_SHADER, vertSupplier.get()); GL32.glAttachShader(this.id, vertShader.id); vertShader.free(); // important! } - for (Supplier f : frag) + for (Supplier fragSupplier : fragSupplierList) { - Shader fragShader = new Shader(GL32.GL_FRAGMENT_SHADER, f.get()); + Shader fragShader = new Shader(GL32.GL_FRAGMENT_SHADER, fragSupplier.get()); GL32.glAttachShader(this.id, fragShader.id); fragShader.free(); // important! } @@ -101,28 +103,22 @@ public class ShaderProgram if (status != GL32.GL_TRUE) { String message = "Shader Link Error. Details: " + GL32.glGetProgramInfoLog(this.id); - free(); // important! + this.free(); // important! throw new RuntimeException(message); } - GL32.glUseProgram(id); // This HAVE to be a direct call to prevent calling the overloaded version + GL32.glUseProgram(this.id); // This HAVE to be a direct call to prevent calling the overloaded version } - /** This will bind ShaderProgram */ - public void bind() - { - GL32.glUseProgram(id); - } - /** This will unbind ShaderProgram */ - public void unbind() - { - GL32.glUseProgram(0); - } - // REMEMBER to always free the resource! - public void free() - { - GL32.glDeleteProgram(id); - } + + + public void bind() { GL32.glUseProgram(this.id); } + public void unbind() { GL32.glUseProgram(0); } + + public void free() { GL32.glDeleteProgram(this.id); } + + + /** * WARNING: Slow native call! Cache it if possible! @@ -139,12 +135,12 @@ public class ShaderProgram if (i == -1) throw new RuntimeException("Attribute name not found: " + name); return i; } - // Same as above but without throwing errors. - // Return -1 if attribute doesn't exist or has been optimized out + /** + * Same as above but without throwing errors.
+ * Returns -1 if the attribute doesn't exist or has been optimized out. + */ public int tryGetAttributeLocation(CharSequence name) - { - return GL32.glGetAttribLocation(id, name); - } + { return GL32.glGetAttribLocation(this.id, name); } /** * WARNING: Slow native call! Cache it if possible! @@ -168,41 +164,34 @@ public class ShaderProgram // Same as above but without throwing errors. // Return -1 if uniform doesn't exist or has been optimized out public int tryGetUniformLocation(CharSequence name) - { - return GL32.glGetUniformLocation(id, name); - } + { return GL32.glGetUniformLocation(this.id, name); } - /** Requires ShaderProgram binded. */ - public void setUniform(int location, boolean value) - { - // This use -1 for false as that equals all one set - GL32.glUniform1i(location, value ? 1 : 0); - } + /** Requires a bound ShaderProgram. */ + public void setUniform(int location, boolean value) { GL32.glUniform1i(location, value ? 1 : 0); } + /** @see ShaderProgram#setUniform(int, boolean) */ + public void trySetUniform(int location, boolean value) { if (location != -1) { this.setUniform(location, value); } } - /** Requires ShaderProgram binded. */ - public void setUniform(int location, int value) - { - GL32.glUniform1i(location, value); - } + /** Requires a bound ShaderProgram. */ + public void setUniform(int location, int value) { GL32.glUniform1i(location, value); } + /** @see ShaderProgram#setUniform(int, int) */ + public void trySetUniform(int location, int value) { if (location != -1) { this.setUniform(location, value); } } - /** Requires ShaderProgram binded. */ - public void setUniform(int location, float value) - { - GL32.glUniform1f(location, value); - } + /** Requires a bound ShaderProgram. */ + public void setUniform(int location, float value) { GL32.glUniform1f(location, value); } + /** @see ShaderProgram#setUniform(int, float) */ + public void trySetUniform(int location, float value) { if (location != -1) { this.setUniform(location, value); } } - /** Requires ShaderProgram binded. */ - public void setUniform(int location, Vec3f value) - { - GL32.glUniform3f(location, value.x, value.y, value.z); - } - /** Requires ShaderProgram binded. */ - public void setUniform(int location, DhApiVec3i value) - { - GL32.glUniform3i(location, value.x, value.y, value.z); - } + /** Requires a bound ShaderProgram. */ + public void setUniform(int location, Vec3f value) { GL32.glUniform3f(location, value.x, value.y, value.z); } + /** @see ShaderProgram#setUniform(int, Vec3f) */ + public void trySetUniform(int location, Vec3f value) { if (location != -1) { this.setUniform(location, value); } } - /** Requires ShaderProgram binded. */ + /** Requires a bound ShaderProgram. */ + public void setUniform(int location, DhApiVec3i value) { GL32.glUniform3i(location, value.x, value.y, value.z); } + /** @see ShaderProgram#setUniform(int, Mat4f) */ + public void trySetUniform(int location, DhApiVec3i value) { if (location != -1) { this.setUniform(location, value); } } + + /** Requires a bound ShaderProgram. */ public void setUniform(int location, Mat4f value) { try (MemoryStack stack = MemoryStack.stackPush()) @@ -212,14 +201,22 @@ public class ShaderProgram GL32.glUniformMatrix4fv(location, false, buffer); } } + /** @see ShaderProgram#setUniform(int, Mat4f) */ + public void trySetUniform(int location, Mat4f value) { if (location != -1) { this.setUniform(location, value); } } /** - * Converts the color's RGBA values into values between 0 and 1. - * Requires ShaderProgram binded. + * Converts the color's RGBA values into values between 0 and 1.
+ * Requires a bound ShaderProgram. */ public void setUniform(int location, Color value) { - GL32.glUniform4f(location, value.getRed() / 256.0f, value.getGreen() / 256.0f, value.getBlue() / 256.0f, value.getAlpha() / 256.0f); + GL32.glUniform4f(location, + value.getRed() / 256.0f, + value.getGreen() / 256.0f, + value.getBlue() / 256.0f, + value.getAlpha() / 256.0f); } + /** @see ShaderProgram#setUniform(int, Color) */ + public void trySetUniform(int location, Color value) { if (location != -1) { this.setUniform(location, value); } } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java index cf91f4d5e..692fb8842 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/LodRenderer.java @@ -44,7 +44,6 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRen import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IProfilerWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.ILightMapWrapper; import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogColorMode; -import com.seibel.distanthorizons.core.render.fog.LodFogConfig; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.AbstractOptifineAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.modAccessor.IIrisAccessor; import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper; @@ -104,8 +103,7 @@ public class LodRenderer // The shader program - IDhApiShaderProgram lodRenderProgram = null; - LodFogConfig fogConfig; + private IDhApiShaderProgram lodRenderProgram = null; public QuadElementBuffer quadIBO = null; public boolean isSetupComplete = false; @@ -634,22 +632,6 @@ public class LodRenderer } else { - LodFogConfig newFogConfig = LodFogConfig.generateFogConfig(); // TODO use a config listener instead - if (this.fogConfig == null) - { - this.fogConfig = newFogConfig; - } - - if (!this.fogConfig.equals(newFogConfig)) - { - this.fogConfig = newFogConfig; - - this.lodRenderProgram.free(); - this.lodRenderProgram = new DhTerrainShaderProgram(); - - FogShader.INSTANCE.free(); - FogShader.INSTANCE = new FogShader(newFogConfig); - } this.lodRenderProgram.bind(); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java index f35eafc94..51666a335 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/render/renderer/shaders/FogShader.java @@ -20,11 +20,11 @@ package com.seibel.distanthorizons.core.render.renderer.shaders; import com.seibel.distanthorizons.api.enums.rendering.EDhApiFogColorMode; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogDirection; +import com.seibel.distanthorizons.api.enums.rendering.EDhApiHeightFogMixMode; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; -import com.seibel.distanthorizons.core.render.fog.LodFogConfig; import com.seibel.distanthorizons.core.render.glObject.GLState; -import com.seibel.distanthorizons.core.render.glObject.shader.Shader; import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram; import com.seibel.distanthorizons.core.render.renderer.LodRenderer; import com.seibel.distanthorizons.core.render.renderer.ScreenQuad; @@ -38,7 +38,7 @@ import java.awt.*; public class FogShader extends AbstractShaderRenderer { - public static FogShader INSTANCE = new FogShader(LodFogConfig.generateFogConfig()); + public static final FogShader INSTANCE = new FogShader(); private static final IMinecraftClientWrapper MC = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); private static final IVersionConstants VERSION_CONSTANTS = SingletonInjector.INSTANCE.get(IVersionConstants.class); @@ -46,21 +46,46 @@ public class FogShader extends AbstractShaderRenderer public int frameBuffer; - private final LodFogConfig fogConfig; private Mat4f inverseMvmProjMatrix; - // Uniforms + //==========// + // Uniforms // + //==========// + + public int uDepthMap; + /** Inverted Model View Projection matrix */ + public int uInvMvmProj; + + // fog uniforms public int uFogColor; public int uFogScale; public int uFogVerticalScale; - public int uNearFogStart; - public int uNearFogLength; public int uFullFogMode; - /** Inverted Model View Projection matrix */ - public int uInvMvmProj; - public int uDepthMap; + // far fog + public int uFarFogStart; + public int uFarFogLength; + public int uFarFogMin; + public int uFarFogRange; + public int uFarFogDensity; + + // height fog + public int uHeightFogStart; + public int uHeightFogLength; + public int uHeightFogMin; + public int uHeightFogRange; + public int uHeightFogDensity; + + public int uHeightFogEnabled; + public int uHeightFogFalloffType; + public int uHeightBasedOnCamera; + public int uHeightFogBaseHeight; + public int uHeightFogAppliesUp; + public int uHeightFogAppliesDown; + public int uUseSphericalFog; + public int uHeightFogMixingMode; + public int uCameraBlockYPos; @@ -68,15 +93,13 @@ public class FogShader extends AbstractShaderRenderer // constructor // //=============// - public FogShader(LodFogConfig fogConfig) { this.fogConfig = fogConfig; } - + public FogShader() { } + @Override public void onInit() { this.shader = new ShaderProgram( - // TODO rename normal.vert to something like "postProcess.vert" - () -> Shader.loadFile("shaders/normal.vert", false, new StringBuilder()).toString(), - () -> this.fogConfig.loadAndProcessFragShader("shaders/fog/fog.frag", false).toString(), + "shaders/normal.vert", "shaders/fog/fog.frag", "fragColor", new String[]{"vPosition"} ); @@ -87,15 +110,35 @@ public class FogShader extends AbstractShaderRenderer this.uInvMvmProj = this.shader.getUniformLocation("uInvMvmProj"); // Fog uniforms - this.uFogScale = this.shader.tryGetUniformLocation("uFogScale"); - this.uFogVerticalScale = this.shader.tryGetUniformLocation("uFogVerticalScale"); - this.uFogColor = this.shader.tryGetUniformLocation("uFogColor"); - this.uFullFogMode = this.shader.tryGetUniformLocation("uFullFogMode"); + this.uFogScale = this.shader.getUniformLocation("uFogScale"); + this.uFogVerticalScale = this.shader.getUniformLocation("uFogVerticalScale"); + this.uFogColor = this.shader.getUniformLocation("uFogColor"); + this.uFullFogMode = this.shader.getUniformLocation("uFullFogMode"); - // near fog - this.uNearFogStart = this.shader.tryGetUniformLocation("uNearFogStart"); - this.uNearFogLength = this.shader.tryGetUniformLocation("uNearFogLength"); + // fog config + this.uFarFogStart = this.shader.getUniformLocation("uFarFogStart"); + this.uFarFogLength = this.shader.getUniformLocation("uFarFogLength"); + this.uFarFogMin = this.shader.getUniformLocation("uFarFogMin"); + this.uFarFogRange = this.shader.getUniformLocation("uFarFogRange"); + this.uFarFogDensity = this.shader.getUniformLocation("uFarFogDensity"); + // height fog + this.uHeightFogStart = this.shader.getUniformLocation("uHeightFogStart"); + this.uHeightFogLength = this.shader.getUniformLocation("uHeightFogLength"); + this.uHeightFogMin = this.shader.getUniformLocation("uHeightFogMin"); + this.uHeightFogRange = this.shader.getUniformLocation("uHeightFogRange"); + this.uHeightFogDensity = this.shader.getUniformLocation("uHeightFogDensity"); + + this.uHeightFogEnabled = this.shader.getUniformLocation("uHeightFogEnabled"); + this.uHeightFogFalloffType = this.shader.getUniformLocation("uHeightFogFalloffType"); + this.uHeightBasedOnCamera = this.shader.getUniformLocation("uHeightBasedOnCamera"); + this.uHeightFogBaseHeight = this.shader.getUniformLocation("uHeightFogBaseHeight"); + this.uHeightFogAppliesUp = this.shader.getUniformLocation("uHeightFogAppliesUp"); + this.uHeightFogAppliesDown = this.shader.getUniformLocation("uHeightFogAppliesDown"); + this.uUseSphericalFog = this.shader.getUniformLocation("uUseSphericalFog"); + this.uHeightFogMixingMode = this.shader.getUniformLocation("uHeightFogMixingMode"); + this.uCameraBlockYPos = this.shader.getUniformLocation("uCameraBlockYPos"); + } @@ -107,22 +150,66 @@ public class FogShader extends AbstractShaderRenderer @Override protected void onApplyUniforms(float partialTicks) { + int lodDrawDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH; + + + if (this.inverseMvmProjMatrix != null) { this.shader.setUniform(this.uInvMvmProj, this.inverseMvmProjMatrix); } - int lodDrawDistance = Config.Client.Advanced.Graphics.Quality.lodChunkRenderDistanceRadius.get() * LodUtil.CHUNK_WIDTH; - // Fog - if (this.uFullFogMode != -1) this.shader.setUniform(this.uFullFogMode, MC_RENDER.isFogStateSpecial() ? 1 : 0); - if (this.uFogColor != -1) this.shader.setUniform(this.uFogColor, MC_RENDER.isFogStateSpecial() ? this.getSpecialFogColor(partialTicks) : this.getFogColor(partialTicks)); + // Fog uniforms + this.shader.setUniform(this.uFogColor, MC_RENDER.isFogStateSpecial() ? this.getSpecialFogColor(partialTicks) : this.getFogColor(partialTicks)); + this.shader.setUniform(this.uFogScale, 1.f / lodDrawDistance); + this.shader.setUniform(this.uFogVerticalScale, 1.f / MC.getWrappedClientLevel().getMaxHeight()); + this.shader.setUniform(this.uFullFogMode, MC_RENDER.isFogStateSpecial() ? 1 : 0); + + + // fog config + float farFogStart = Config.Client.Advanced.Graphics.Fog.farFogStart.get().floatValue(); + float farFogEnd = Config.Client.Advanced.Graphics.Fog.farFogEnd.get().floatValue(); + float farFogMin = Config.Client.Advanced.Graphics.Fog.farFogMin.get().floatValue(); + float farFogMax = Config.Client.Advanced.Graphics.Fog.farFogMax.get().floatValue(); + float farFogDensity = Config.Client.Advanced.Graphics.Fog.farFogDensity.get().floatValue(); + + this.shader.setUniform(this.uFarFogStart, farFogStart); + this.shader.setUniform(this.uFarFogLength, farFogEnd - farFogStart); + this.shader.setUniform(this.uFarFogMin, farFogMin); + this.shader.setUniform(this.uFarFogRange, farFogMax - farFogMin); + this.shader.setUniform(this.uFarFogDensity, farFogDensity); + + + // height config + EDhApiHeightFogMixMode heightFogMixingMode = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMixMode.get(); + boolean heightFogEnabled = heightFogMixingMode != EDhApiHeightFogMixMode.SPHERICAL && heightFogMixingMode != EDhApiHeightFogMixMode.CYLINDRICAL; + boolean useSphericalFog = heightFogMixingMode == EDhApiHeightFogMixMode.SPHERICAL; + EDhApiHeightFogDirection heightFogCameraDirection = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDirection.get(); + + float heightFogStart = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogStart.get().floatValue(); + float heightFogEnd = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogEnd.get().floatValue(); + float heightFogMin = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMin.get().floatValue(); + float heightFogMax = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogMax.get().floatValue(); + float heightFogDensity = Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogDensity.get().floatValue(); + + this.shader.setUniform(this.uHeightFogStart, heightFogStart); + this.shader.setUniform(this.uHeightFogLength, heightFogEnd - heightFogStart); + this.shader.setUniform(this.uHeightFogMin, heightFogMin); + this.shader.setUniform(this.uHeightFogRange, heightFogMax - heightFogMin); + this.shader.setUniform(this.uHeightFogDensity, heightFogDensity); + + + this.shader.setUniform(this.uHeightFogEnabled, heightFogEnabled); + this.shader.setUniform(this.uHeightFogFalloffType, Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogFalloff.get().value); + this.shader.setUniform(this.uHeightFogBaseHeight, Config.Client.Advanced.Graphics.Fog.HeightFog.heightFogBaseHeight.get().floatValue()); + this.shader.setUniform(this.uHeightBasedOnCamera, heightFogCameraDirection.basedOnCamera); + this.shader.setUniform(this.uHeightFogAppliesUp, heightFogCameraDirection.fogAppliesUp); + this.shader.setUniform(this.uHeightFogAppliesDown, heightFogCameraDirection.fogAppliesDown); + this.shader.setUniform(this.uUseSphericalFog, useSphericalFog); + this.shader.setUniform(this.uHeightFogMixingMode, heightFogMixingMode.value); + this.shader.setUniform(this.uCameraBlockYPos, (float)MC_RENDER.getCameraExactPosition().y); - float nearFogStart = (VERSION_CONSTANTS.isVanillaRenderedChunkSquare() ? (float) Math.sqrt(2.0) : 1.0f) / lodDrawDistance; - if (this.uNearFogStart != -1) this.shader.setUniform(this.uNearFogStart, nearFogStart); - if (this.uNearFogLength != -1) this.shader.setUniform(this.uNearFogLength, 0.0f); - if (this.uFogScale != -1) this.shader.setUniform(this.uFogScale, 1.f / lodDrawDistance); - if (this.uFogVerticalScale != -1) this.shader.setUniform(this.uFogVerticalScale, 1.f / MC.getWrappedClientLevel().getMaxHeight()); } private Color getFogColor(float partialTicks) { diff --git a/core/src/main/resources/assets/distanthorizons/lang/en_us.json b/core/src/main/resources/assets/distanthorizons/lang/en_us.json index 80675ad7b..a2940b8c9 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -269,10 +269,10 @@ "distanthorizons.config.client.advanced.graphics.fog.heightFog.heightFogMixMode": "Height Fog Mix Mode", "distanthorizons.config.client.advanced.graphics.fog.heightFog.heightFogMixMode.@tooltip": - "How the height should effect the fog thickness combined with the normal function? \n\nBASIC: No special height fog effect. Fog is calculated based on camera distance \nIGNORE_HEIGHT: Ignore height completely. Fog is calculated based on horizontal distance \nADDITION: heightFog + farFog \nMAX: max(heightFog, farFog) \nMULTIPLY: heightFog * farFog \nINVERSE_MULTIPLY: 1 - (1-heightFog) * (1-farFog) \nLIMITED_ADDITION: farFog + max(farFog, heightFog) \nMULTIPLY_ADDITION: farFog + farFog * heightFog \nINVERSE_MULTIPLY_ADDITION: farFog + 1 - (1-heightFog) * (1-farFog) \nAVERAGE: farFog*0.5 + heightFog*0.5 \n\nNote that for 'BASIC' mode and 'IGNORE_HEIGHT' mode, fog settings for height fog has no effect.\n", - "distanthorizons.config.client.advanced.graphics.fog.heightFog.heightFogMode": - "Height Fog Mode", - "distanthorizons.config.client.advanced.graphics.fog.heightFog.heightFogMode.@tooltip": + "Defines how height effects fog. \nWhen in doubt start with SPHERICAL, ADDITION, or MAX. \n\nSPHERICAL: Fog is calculated based on camera distance. \nCYLINDRICAL: Ignore height, fog is calculated based on horizontal distance. \nMAX: max(heightFog, farFog) \nADDITION: heightFog + farFog \nMULTIPLY: heightFog * farFog \nINVERSE_MULTIPLY: 1 - (1-heightFog) * (1-farFog) \nLIMITED_ADDITION: farFog + max(farFog, heightFog) \nMULTIPLY_ADDITION: farFog + farFog * heightFog \nINVERSE_MULTIPLY_ADDITION: farFog + 1 - (1-heightFog) * (1-farFog) \nAVERAGE: farFog*0.5 + heightFog*0.5", + "distanthorizons.config.client.advanced.graphics.fog.heightFog.heightFogDirection": + "Height Fog Direction", + "distanthorizons.config.client.advanced.graphics.fog.heightFog.heightFogDirection.@tooltip": "Where should the height fog be located? \n\nABOVE_CAMERA: Height fog starts from camera to the sky \nBELOW_CAMERA: Height fog starts from camera to the void \nABOVE_AND_BELOW_CAMERA: Height fog starts from camera to both the sky and the void \nABOVE_SET_HEIGHT: Height fog starts from a set height to the sky \nBELOW_SET_HEIGHT: Height fog starts from a set height to the void \nABOVE_AND_BELOW_SET_HEIGHT: Height fog starts from a set height to both the sky and the void \n", "distanthorizons.config.client.advanced.graphics.fog.heightFog.heightFogBaseHeight": "Height Fog Base Height", @@ -805,10 +805,10 @@ "distanthorizons.config.enum.EDhApiFogFalloff.EXPONENTIAL_SQUARED": "Exponential squared", - "distanthorizons.config.enum.EDhApiHeightFogMixMode.BASIC": - "Basic", - "distanthorizons.config.enum.EDhApiHeightFogMixMode.IGNORE_HEIGHT": - "Ignore Height", + "distanthorizons.config.enum.EDhApiHeightFogMixMode.SPHERICAL": + "Spherical", + "distanthorizons.config.enum.EDhApiHeightFogMixMode.CYLINDRICAL": + "Cylindrical", "distanthorizons.config.enum.EDhApiHeightFogMixMode.ADDITION": "Addition", "distanthorizons.config.enum.EDhApiHeightFogMixMode.MAX": @@ -826,17 +826,17 @@ "distanthorizons.config.enum.EDhApiHeightFogMixMode.AVERAGE": "Average", - "distanthorizons.config.enum.EDhApiHeightFogMode.ABOVE_CAMERA": + "distanthorizons.config.enum.EDhApiHeightFogDirection.ABOVE_CAMERA": "Above Camera", - "distanthorizons.config.enum.EDhApiHeightFogMode.BELOW_CAMERA": + "distanthorizons.config.enum.EDhApiHeightFogDirection.BELOW_CAMERA": "Below Camera", - "distanthorizons.config.enum.EDhApiHeightFogMode.ABOVE_AND_BELOW_CAMERA": + "distanthorizons.config.enum.EDhApiHeightFogDirection.ABOVE_AND_BELOW_CAMERA": "Above And Below Camera", - "distanthorizons.config.enum.EDhApiHeightFogMode.ABOVE_SET_HEIGHT": + "distanthorizons.config.enum.EDhApiHeightFogDirection.ABOVE_SET_HEIGHT": "Above Set Height", - "distanthorizons.config.enum.EDhApiHeightFogMode.BELOW_SET_HEIGHT": + "distanthorizons.config.enum.EDhApiHeightFogDirection.BELOW_SET_HEIGHT": "Below Set Height", - "distanthorizons.config.enum.EDhApiHeightFogMode.ABOVE_AND_BELOW_SET_HEIGHT": + "distanthorizons.config.enum.EDhApiHeightFogDirection.ABOVE_AND_BELOW_SET_HEIGHT": "Above And Below Set Height", "distanthorizons.config.enum.EDhApiVanillaOverdraw.NEVER": diff --git a/core/src/main/resources/shaders/fog/fog.frag b/core/src/main/resources/shaders/fog/fog.frag index eed4c6b6b..a4495bd82 100644 --- a/core/src/main/resources/shaders/fog/fog.frag +++ b/core/src/main/resources/shaders/fog/fog.frag @@ -1,99 +1,120 @@ +#version 150 core in vec2 TexCoord; out vec4 fragColor; + + uniform sampler2D uDepthMap; // inverted model view matrix and projection matrix uniform mat4 uInvMvmProj; +// fog uniforms +uniform vec4 uFogColor; uniform float uFogScale; uniform float uFogVerticalScale; -uniform vec4 uFogColor; uniform int uFullFogMode; +uniform int uFogFalloffType; -uniform float uNearFogStart; -uniform float uNearFogLength; +// fog config +uniform float uFarFogStart; +uniform float uFarFogLength; +uniform float uFarFogMin; +uniform float uFarFogRange; +uniform float uFarFogDensity; + +// height fog config +uniform float uHeightFogStart; +uniform float uHeightFogLength; +uniform float uHeightFogMin; +uniform float uHeightFogRange; +uniform float uHeightFogDensity; -/* ========MARCO DEFINED BY RUNTIME CODE GEN========= +uniform bool uHeightFogEnabled; +uniform int uHeightFogFalloffType; +uniform bool uHeightBasedOnCamera; +uniform float uHeightFogBaseHeight; +uniform bool uHeightFogAppliesUp; +uniform bool uHeightFogAppliesDown; +uniform bool uUseSphericalFog; +uniform int uHeightFogMixingMode; +uniform float uCameraBlockYPos; -float farFogStart; -float farFogLength; -float farFogMin; -float farFogRange; -float farFogDensity; - -float heightFogStart; -float heightFogLength; -float heightFogMin; -float heightFogRange; -float heightFogDensity; -*/ - -// method definitions -// ==== The below 5 methods will be run-time generated. ==== -float getNearFogThickness(float dist); -float getFarFogThickness(float dist); -float getHeightFogThickness(float dist); -float calculateFarFogDepth(float horizontal, float dist, float nearFogStart); -float calculateHeightFogDepth(float vertical, float realY); -float mixFogThickness(float near, float far, float height); -// ========================================================= const vec3 MAGIC = vec3(0.06711056, 0.00583715, 52.9829189); -float InterleavedGradientNoise(const in vec2 pixel) { - float x = dot(pixel, MAGIC.xy); - return fract(MAGIC.z * fract(x)); -} -vec3 calcViewPosition(float fragmentDepth) { - vec4 ndc = vec4(TexCoord.xy, fragmentDepth, 1.0); - ndc.xyz = ndc.xyz * 2.0 - 1.0; - vec4 eyeCoord = uInvMvmProj * ndc; - return eyeCoord.xyz / eyeCoord.w; -} +//====================// +// method definitions // +//====================// + +float InterleavedGradientNoise(const in vec2 pixel); +vec3 calcViewPosition(float fragmentDepth); + +float getFarFogThickness(float dist); +float getHeightFogThickness(float dist); +float calculateHeightFogDepth(float worldYPos); +float mixFogThickness(float far, float height); + + + +//======// +// main // +//======// /** * Fragment shader for fog. - * This should be passed last so it applies above other affects like AO - * - * version: 2023-6-21 + * This should be run last so it applies above other affects like Ambient Occlusioning */ -void main() +void main() { - float vertexYPos = 100.0f; float fragmentDepth = texture(uDepthMap, TexCoord).r; fragColor = vec4(uFogColor.rgb, 0.0); // a fragment depth of "1" means the fragment wasn't drawn to, // we only want to apply Fog to LODs, not to the sky outside the LODs - if (fragmentDepth < 1.0) { - if (uFullFogMode == 0) { + if (fragmentDepth < 1.0) + { + int fogMode = uFullFogMode; + if (fogMode == 0) + { // render fog based on distance from the camera vec3 vertexWorldPos = calcViewPosition(fragmentDepth); - float horizontalDist = length(vertexWorldPos.xz) * uFogScale; - float heightDist = calculateHeightFogDepth(vertexWorldPos.y, vertexYPos) * uFogVerticalScale; - float farDist = calculateFarFogDepth(horizontalDist, length(vertexWorldPos.xyz) * uFogScale, uNearFogStart); + float horizontalWorldDistance = length(vertexWorldPos.xz) * uFogScale; + float worldDistance = length(vertexWorldPos.xyz) * uFogScale; + float activeDistance = uUseSphericalFog ? worldDistance : horizontalWorldDistance; - float nearFogThickness = getNearFogThickness(horizontalDist); - float farFogThickness = getFarFogThickness(farDist); - float heightFogThickness = getHeightFogThickness(heightDist); - float mixedFogThickness = mixFogThickness(nearFogThickness, farFogThickness, heightFogThickness); + + // far fog + float farFogThickness = getFarFogThickness(activeDistance); + + // height fog + float heightFogDepth = calculateHeightFogDepth(vertexWorldPos.y); + float heightFogThickness = getHeightFogThickness(heightFogDepth); + + // combined fog + float mixedFogThickness = mixFogThickness(farFogThickness, heightFogThickness); fragColor.a = clamp(mixedFogThickness, 0.0, 1.0); - float dither = InterleavedGradientNoise(gl_FragCoord.xy) - 0.5; - fragColor.a += dither / 255.0; + // test + //fragColor.a = heightFogThickness; + + // dither fog (to smooth out aliasing) + //float dither = InterleavedGradientNoise(gl_FragCoord.xy) - 0.5; + //fragColor.a += dither / 255.0; } - else if (uFullFogMode == 1) { + else if (fogMode == 1) + { // render everything with the fog color fragColor.a = 1.0; } - else { + else + { // test code. // this can be fired by manually changing the fullFogMode to a (normally) @@ -108,22 +129,174 @@ void main() } } -// Are these still needed? -float linearFog(float x, float fogStart, float fogLength, float fogMin, float fogRange) { - x = clamp((x-fogStart)/fogLength, 0.0, 1.0); - return fogMin + fogRange * x; + +// +// methods // +// + +float InterleavedGradientNoise(const in vec2 pixel) +{ + float x = dot(pixel, MAGIC.xy); + return fract(MAGIC.z * fract(x)); +} + +vec3 calcViewPosition(float fragmentDepth) +{ + vec4 ndc = vec4(TexCoord.xy, fragmentDepth, 1.0); + ndc.xyz = ndc.xyz * 2.0 - 1.0; + + vec4 eyeCoord = uInvMvmProj * ndc; + return eyeCoord.xyz / eyeCoord.w; +} + + + +float linearFog(float worldDist, float fogStart, float fogLength, float fogMin, float fogRange) +{ + worldDist = (worldDist - fogStart) / fogLength; + worldDist = clamp(worldDist, 0.0, 1.0); + return fogMin + fogRange * worldDist; } float exponentialFog(float x, float fogStart, float fogLength, - float fogMin, float fogRange, float fogDensity) +float fogMin, float fogRange, float fogDensity) { x = max((x-fogStart)/fogLength, 0.0) * fogDensity; return fogMin + fogRange - fogRange/exp(x); } float exponentialSquaredFog(float x, float fogStart, float fogLength, - float fogMin, float fogRange, float fogDensity) +float fogMin, float fogRange, float fogDensity) { x = max((x-fogStart)/fogLength, 0.0) * fogDensity; return fogMin + fogRange - fogRange/exp(x*x); } + + + +// +// generated methods // +// + +float getFarFogThickness(float dist) +{ + if (uFogFalloffType == 0) // LINEAR + { + return linearFog(dist, uFarFogStart, uFarFogLength, uFarFogMin, uFarFogRange); + } + else if (uFogFalloffType == 1) // EXPONENTIAL + { + return exponentialFog(dist, uFarFogStart, uFarFogLength, uFarFogMin, uFarFogRange, uFarFogDensity); + } + else // EXPONENTIAL_SQUARED + { + return exponentialSquaredFog(dist, uFarFogStart, uFarFogLength, uFarFogMin, uFarFogRange, uFarFogDensity); + } +} + +float getHeightFogThickness(float dist) +{ + if (!uHeightFogEnabled) + { + return 0.0; + } + + if (uHeightFogFalloffType == 0) // LINEAR + { + return linearFog(dist, uHeightFogStart, uHeightFogLength, uHeightFogMin, uHeightFogRange); + } + else if (uHeightFogFalloffType == 1) // EXPONENTIAL + { + return exponentialFog(dist, uHeightFogStart, uHeightFogLength, uHeightFogMin, uHeightFogRange, uHeightFogDensity); + } + else // EXPONENTIAL_SQUARED + { + return exponentialSquaredFog(dist, uHeightFogStart, uHeightFogLength, uHeightFogMin, uHeightFogRange, uHeightFogDensity); + } +} + +/** 1 = full fog, 0 = no fog */ +float calculateHeightFogDepth(float worldYPos) +{ + // worldYPos -65 - 384 + + + //worldYPos = worldYPos * -1; // negative, fog below height; positive, fog above height + //return worldYPos * uFogVerticalScale; // "* uFogVerticalScale" is done to convert world position to a percent of the world height; + + if (!uHeightFogEnabled) + { + // ignore the height + return 0.0; + } + + + if (!uHeightBasedOnCamera) + { + worldYPos -= (uHeightFogBaseHeight - uCameraBlockYPos); + } + + + if (uHeightFogAppliesDown && uHeightFogAppliesUp) + { + // TODO this aint right + return abs(worldYPos) * uFogVerticalScale; + } + else if (uHeightFogAppliesDown) + { + // apploy fog below given height + return -worldYPos * uFogVerticalScale; + } + else if (uHeightFogAppliesUp) + { + // apply fog above given height + return worldYPos * uFogVerticalScale; + } + else + { + // shouldn't happen, + return 0; + } + +} + +float mixFogThickness(float far, float height) +{ + switch (uHeightFogMixingMode) + { + case 0: // BASIC + case 1: // IGNORE_HEIGHT + return far; + + case 2: // MAX + return max(far, height); + + case 3: // ADDITION + return (far + height); + + case 4: // MULTIPLY + return far * height; + + case 5: // INVERSE_MULTIPLY + return (1.0 - (1.0-far)*(1.0-height)); + + case 6: // LIMITED_ADDITION + return (far + max(far, height)); + + case 7: // MULTIPLY_ADDITION + return (far + far*height); + + case 8: // INVERSE_MULTIPLY_ADDITION + return (far + 1.0 - (1.0-far)*(1.0-height)); + + case 9: // AVERAGE + return (far*0.5 + height*0.5); + } + + // shouldn't happen, but default to BASIC / IGNORE_HEIGHT + // if an invalid option is selected + return far; +} + + + From 8a612666511f0b093117c57ec5cb394a0e555db5 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 9 Nov 2024 21:17:09 -0600 Subject: [PATCH 25/26] fix level.getHashedSeed() and re-add default getDhIdentifier() --- .../core/wrapperInterfaces/world/ILevelWrapper.java | 5 +++-- .../worldGeneratorInjection/objects/LevelWrapperTest.java | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java index 70be98fc9..d10b8e71b 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/ILevelWrapper.java @@ -37,12 +37,13 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable @Override String getDimensionName(); - int getHashedSeed(); + long getHashedSeed(); /** * A string intended to uniquely identify this level. */ - @Override String getDhIdentifier(); + @Override + default String getDhIdentifier() { return this.getDimensionName() + "_" + this.getHashedSeed(); } @Override boolean hasCeiling(); diff --git a/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java b/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java index 334670bdc..df68445eb 100644 --- a/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java +++ b/core/src/test/java/testItems/worldGeneratorInjection/objects/LevelWrapperTest.java @@ -39,9 +39,10 @@ public class LevelWrapperTest implements IDhApiLevelWrapper @Override public IDhApiDimensionTypeWrapper getDimensionType() { return null; } - @Override public String getDimensionName() { return null; } + @Override + public String getDhIdentifier() { return null; } @Override public EDhApiLevelType getLevelType() { return null; } From ea55750c4b132dd2038f9c1b75670e128ee1be6f Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 11 Nov 2024 07:45:18 -0600 Subject: [PATCH 26/26] Hopefully fix some file path complaints for test files --- .../{clouds - original.png => clouds-original.png} | Bin .../textures/{test grid.png => test-grid.png} | Bin 2 files changed, 0 insertions(+), 0 deletions(-) rename core/src/main/resources/assets/distanthorizons/textures/{clouds - original.png => clouds-original.png} (100%) rename core/src/main/resources/assets/distanthorizons/textures/{test grid.png => test-grid.png} (100%) diff --git a/core/src/main/resources/assets/distanthorizons/textures/clouds - original.png b/core/src/main/resources/assets/distanthorizons/textures/clouds-original.png similarity index 100% rename from core/src/main/resources/assets/distanthorizons/textures/clouds - original.png rename to core/src/main/resources/assets/distanthorizons/textures/clouds-original.png diff --git a/core/src/main/resources/assets/distanthorizons/textures/test grid.png b/core/src/main/resources/assets/distanthorizons/textures/test-grid.png similarity index 100% rename from core/src/main/resources/assets/distanthorizons/textures/test grid.png rename to core/src/main/resources/assets/distanthorizons/textures/test-grid.png