From eeae8dbdc56837cfe7f7be67f0fbe17f585cdad6 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Tue, 12 Nov 2024 12:25:29 +0500 Subject: [PATCH 01/24] WIP generation distance limiting --- .../distanthorizons/core/config/Config.java | 72 ++++++++++++++----- .../generation/RemoteWorldRetrievalQueue.java | 2 + .../core/level/AbstractDhServerLevel.java | 15 +++- .../AbstractFullDataNetworkRequestQueue.java | 41 ++++++----- .../client/SyncOnLoadRequestQueue.java | 2 + .../multiplayer/config/SessionConfig.java | 27 +++++-- ...n.java => RequestOutOfRangeException.java} | 7 +- .../messages/requests/ExceptionMessage.java | 4 +- .../core/pos/DhSectionPos.java | 10 ++- 9 files changed, 127 insertions(+), 53 deletions(-) rename core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/{InvalidLevelException.java => RequestOutOfRangeException.java} (80%) 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 848b0b856..709b185e3 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 @@ -33,7 +33,6 @@ import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.wrapperInterfaces.IWrapperFactory; -import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftSharedWrapper; import com.seibel.distanthorizons.coreapi.ModInfo; import org.apache.logging.log4j.Logger; @@ -43,7 +42,6 @@ import java.awt.*; import java.io.File; import java.util.*; import java.util.List; -import java.util.concurrent.ThreadLocalRandom; /** * This handles any configuration the user has access to.

@@ -1512,23 +1510,9 @@ public class Config } - public static class Server + public static class Server { - public static ConfigEntry realTimeUpdateDistanceRadiusInChunks = new ConfigEntry.Builder() - .setServersideShortName("renderDistanceRadius") - .setMinDefaultMax(32, 256, 4096) - .comment("" + - "Defines the distance players will receive real-time updates for if enabled. \n" + - "\n" + - "Note: \n" + - "This setting does not prevent players from generating farther out. \n" + - "If you want to limit performance impact, change rate limits \n" + - "and thread count/runtime ratio settings instead. \n" + - "It also does not affect the visuals on clients. \n" + - "") - .setPerformance(EConfigEntryPerformance.HIGH) - .build(); - + // Level keys public static ConfigEntry sendLevelKeys = new ConfigEntry.Builder() .setServersideShortName("sendLevelKeys") .setAppearance(EConfigEntryAppearance.ONLY_IN_FILE) @@ -1549,6 +1533,8 @@ public class Config + "") .build(); + + // Generation public static ConfigEntry generationRequestRateLimit = new ConfigEntry.Builder() .setServersideShortName("generationRequestRateLimit") .setMinDefaultMax(1, 20, 100) @@ -1558,6 +1544,23 @@ public class Config + "") .build(); + public static ConfigEntry maxGenerationRequestDistance = new ConfigEntry.Builder() + .setServersideShortName("maxGenerationRequestDistance") + .setMinDefaultMax(32, 1024, 4096) + .comment("" + + "Defines the distance players will receive real-time updates for if enabled. \n" + + "\n" + + "Note: \n" + + "This setting does not prevent players from generating farther out. \n" + + "If you want to limit performance impact, change rate limits \n" + + "and thread count/runtime ratio settings instead. \n" + + "It also does not affect the visuals on clients. \n" + + "") + .setPerformance(EConfigEntryPerformance.HIGH) + .build(); + + + // Real-time updates public static ConfigEntry enableRealTimeUpdates = new ConfigEntry.Builder() .setServersideShortName("enableRealTimeUpdates") .set(true) @@ -1566,7 +1569,23 @@ public class Config + "") .build(); + public static ConfigEntry realTimeUpdateDistanceRadiusInChunks = new ConfigEntry.Builder() + .setServersideShortName("realTimeUpdateDistanceRadius") + .setMinDefaultMax(32, 256, 4096) + .comment("" + + "Defines the distance players will receive real-time updates for if enabled. \n" + + "\n" + + "Note: \n" + + "This setting does not prevent players from generating farther out. \n" + + "If you want to limit performance impact, change rate limits \n" + + "and thread count/runtime ratio settings instead. \n" + + "It also does not affect the visuals on clients. \n" + + "") + .setPerformance(EConfigEntryPerformance.HIGH) + .build(); + + // Sync on load public static ConfigEntry synchronizeOnLoad = new ConfigEntry.Builder() .setServersideShortName("synchronizeOnLoad") .set(true) @@ -1584,6 +1603,23 @@ public class Config + "") .build(); + public static ConfigEntry maxSyncOnLoadRequestDistance = new ConfigEntry.Builder() + .setServersideShortName("maxSyncOnLoadRequestDistance") + .setMinDefaultMax(32, 1024, 4096) + .comment("" + + "Defines the distance players will receive real-time updates for if enabled. \n" + + "\n" + + "Note: \n" + + "This setting does not prevent players from generating farther out. \n" + + "If you want to limit performance impact, change rate limits \n" + + "and thread count/runtime ratio settings instead. \n" + + "It also does not affect the visuals on clients. \n" + + "") + .setPerformance(EConfigEntryPerformance.HIGH) + .build(); + + + // Common public static ConfigEntry maxDataTransferSpeed = new ConfigEntry.Builder() .setServersideShortName("maxDataTransferSpeed") .setMinDefaultMax(0, 500, 1000000 /* 1 GB/s */) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/generation/RemoteWorldRetrievalQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/generation/RemoteWorldRetrievalQueue.java index 56b23490f..18a5c4ad9 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/generation/RemoteWorldRetrievalQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/generation/RemoteWorldRetrievalQueue.java @@ -64,6 +64,8 @@ public class RemoteWorldRetrievalQueue extends AbstractFullDataNetworkRequestQue @Override protected int getRequestRateLimit() { return this.networkState.sessionConfig.getGenerationRequestRateLimit(); } + @Override + protected int getMaxRequestDistance() { return this.networkState.sessionConfig.getMaxGenerationRequestDistance(); } @Override protected String getQueueName() { return "World Remote Generation Queue"; } 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 059697189..47cfc5e98 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 @@ -9,7 +9,7 @@ import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerState; import com.seibel.distanthorizons.core.multiplayer.server.ServerPlayerStateManager; -import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; +import com.seibel.distanthorizons.core.network.exceptions.RequestOutOfRangeException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage; import com.seibel.distanthorizons.core.network.messages.AbstractTrackableMessage; @@ -194,6 +194,13 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I return; } + Vec3d playerPosition = serverPlayerState.getServerPlayer().getPosition(); + int distanceFromPlayer = DhSectionPos.getChebyshevSignedBlockDistance(message.sectionPos, new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; + if (distanceFromPlayer > Config.Server.maxGenerationRequestDistance.get()) + { + message.sendResponse(new RequestOutOfRangeException("Distance too large: " + distanceFromPlayer + " > " + Config.Server.maxGenerationRequestDistance.get())); + return; + } ServerPlayerState.RateLimiterSet rateLimiterSet = serverPlayerState.getRateLimiterSet(this); @@ -241,6 +248,8 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I } private void queueLodSyncForRequestMessage(ServerPlayerState serverPlayerState, FullDataSourceRequestMessage message, ServerPlayerState.RateLimiterSet rateLimiterSet) { + + if (!serverPlayerState.sessionConfig.getSynchronizeOnLoad()) { message.sendResponse(new RequestRejectedException("Operation is disabled in config.")); @@ -350,7 +359,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I if (message instanceof AbstractTrackableMessage) { ((AbstractTrackableMessage) message).sendResponse( - new InvalidLevelException( + new RequestRejectedException( "Generation not allowed. " + "Requested dimension: ["+((ILevelRelatedMessage) message).getLevelName()+"], " + "player dimension: [" + message.getSession().serverPlayer.getLevel().getDhIdentifier() + "], " + @@ -446,7 +455,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I } Vec3d playerPosition = serverPlayerState.getServerPlayer().getPosition(); - int distanceFromPlayer = DhSectionPos.getChebyshevBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; + int distanceFromPlayer = DhSectionPos.getChebyshevSignedBlockDistance(data.getPos(), new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; if (distanceFromPlayer >= serverPlayerState.getServerPlayer().getViewDistance() && distanceFromPlayer <= serverPlayerState.sessionConfig.getMaxUpdateDistanceRadius()) { 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 70779b70d..7812129a9 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 @@ -7,8 +7,8 @@ import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSour import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.level.DhClientLevel; import com.seibel.distanthorizons.core.logging.ConfigBasedSpamLogger; -import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; +import com.seibel.distanthorizons.core.network.exceptions.RequestOutOfRangeException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; import com.seibel.distanthorizons.core.network.session.SessionClosedException; import com.seibel.distanthorizons.core.network.messages.fullData.FullDataSourceRequestMessage; @@ -71,6 +71,8 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende private final SupplierBasedRateLimiter rateLimiter = new SupplierBasedRateLimiter<>(this::getRequestRateLimit); + private DhBlockPos2D lastTargetPos = new DhBlockPos2D(0, 0); + //=============// @@ -95,6 +97,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende //==================// protected abstract int getRequestRateLimit(); + protected abstract int getMaxRequestDistance(); protected abstract String getQueueName(); @@ -138,6 +141,8 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende return false; } + this.lastTargetPos = targetPos; + // queue requests until the queue is full while (this.getInProgressTaskCount() < this.getWaitingTaskCount() && this.getInProgressTaskCount() < this.getRequestRateLimit() @@ -158,7 +163,8 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende { Map.Entry mapEntry = this.waitingTasksBySectionPos.entrySet().stream() .filter(task -> task.getValue().networkDataSourceFuture == null) - .min(Comparator.comparingInt(x -> posDistanceSquared(targetPos, x.getKey()))) + .filter(task -> DhSectionPos.getChebyshevSignedBlockDistance(task.getKey(), targetPos) <= this.getMaxRequestDistance()) + .min(Comparator.comparingInt(task -> DhSectionPos.getChebyshevSignedBlockDistance(task.getKey(), targetPos))) .orElse(null); if (mapEntry == null) @@ -220,16 +226,15 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende LodUtil.assertTrue(this.changedOnly, "Received empty data source response for not changes-only request"); } } - catch (InvalidLevelException | RequestRejectedException ignored) - { - // We're too late / some cases might trigger a bunch of expected rejections - return entry.future.complete(false); - } catch (SessionClosedException | CancellationException ignored) { - // Triggered when level is unloaded return entry.future.cancel(false); } + catch (RequestRejectedException e) + { + LOGGER.info("Request rejected by the server: " + e.getMessage()); + return entry.future.complete(false); + } catch (RateLimitedException e) { LOGGER.warn("Rate limited by server, re-queueing task [" + DhSectionPos.toString(sectionPos) + "]: " + e.getMessage()); @@ -240,6 +245,13 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende entry.networkDataSourceFuture = null; return null; } + catch (RequestOutOfRangeException e) + { + LOGGER.warn("Out of range, re-queueing task [" + DhSectionPos.toString(sectionPos) + "]: " + e.getMessage()); + + entry.networkDataSourceFuture = null; + return null; + } catch (Throwable e) { entry.retryAttempts--; @@ -352,22 +364,15 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende for (Map.Entry mapEntry : this.waitingTasksBySectionPos.entrySet()) { renderer.renderBox(new DebugRenderer.Box(mapEntry.getKey(), -32f, 64f, 0.05f, - mapEntry.getValue().networkDataSourceFuture != null ? Color.red : Color.gray + mapEntry.getValue().networkDataSourceFuture != null ? Color.red + : DhSectionPos.getChebyshevSignedBlockDistance(mapEntry.getKey(), this.lastTargetPos) <= this.getMaxRequestDistance() ? Color.gray + : Color.lightGray )); } } - //================// - // helper methods // - //================// - - protected static int posDistanceSquared(DhBlockPos2D targetPos, long pos) - { return (int) DhSectionPos.getCenterBlockPos(pos).distSquared(targetPos); } - - - //================// // helper classes // //================// diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoadRequestQueue.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoadRequestQueue.java index 5ae421c75..522f04d54 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoadRequestQueue.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/SyncOnLoadRequestQueue.java @@ -31,6 +31,8 @@ public class SyncOnLoadRequestQueue extends AbstractFullDataNetworkRequestQueue @Override protected int getRequestRateLimit() { return this.networkState.sessionConfig.getSyncOnLoginRateLimit(); } + @Override + protected int getMaxRequestDistance() { return this.networkState.sessionConfig.getMaxSyncOnLoadDistance(); } @Override protected String getQueueName() { return "Sync On Login Queue"; } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/SessionConfig.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/SessionConfig.java index 790bffccc..6ace4c042 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/SessionConfig.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/config/SessionConfig.java @@ -31,14 +31,15 @@ public class SessionConfig implements INetworkObject { // Note: config values are transmitted in the insertion order - registerConfigEntry(Config.Server.realTimeUpdateDistanceRadiusInChunks, Math::min); - - registerConfigEntry(Config.Common.WorldGenerator.enableDistantGeneration, (x, y) -> x && y); + registerConfigEntry(Config.Common.WorldGenerator.enableDistantGeneration, Boolean::logicalAnd); + registerConfigEntry(Config.Server.maxGenerationRequestDistance, Math::min); registerConfigEntry(Config.Server.generationRequestRateLimit, Math::min); - registerConfigEntry(Config.Server.enableRealTimeUpdates, (x, y) -> x && y); + registerConfigEntry(Config.Server.enableRealTimeUpdates, Boolean::logicalAnd); + registerConfigEntry(Config.Server.realTimeUpdateDistanceRadiusInChunks, Math::min); - registerConfigEntry(Config.Server.synchronizeOnLoad, (x, y) -> x && y); + registerConfigEntry(Config.Server.synchronizeOnLoad, Boolean::logicalAnd); + registerConfigEntry(Config.Server.maxSyncOnLoadRequestDistance, Math::min); registerConfigEntry(Config.Server.syncOnLoadRateLimit, Math::min); registerConfigEntry(Config.Server.maxDataTransferSpeed, (x, y) -> { @@ -62,12 +63,17 @@ public class SessionConfig implements INetworkObject // public values // //===============// - public int getMaxUpdateDistanceRadius() { return this.getValue(Config.Server.realTimeUpdateDistanceRadiusInChunks); } public boolean isDistantGenerationEnabled() { return this.getValue(Config.Common.WorldGenerator.enableDistantGeneration); } + public int getMaxGenerationRequestDistance() { return this.getValue(Config.Server.maxGenerationRequestDistance); } public int getGenerationRequestRateLimit() { return this.getValue(Config.Server.generationRequestRateLimit); } + public boolean isRealTimeUpdatesEnabled() { return this.getValue(Config.Server.enableRealTimeUpdates); } + public int getMaxUpdateDistanceRadius() { return this.getValue(Config.Server.realTimeUpdateDistanceRadiusInChunks); } + public boolean getSynchronizeOnLoad() { return this.getValue(Config.Server.synchronizeOnLoad); } + public int getMaxSyncOnLoadDistance() { return this.getValue(Config.Server.maxSyncOnLoadRequestDistance); } public int getSyncOnLoginRateLimit() { return this.getValue(Config.Server.syncOnLoadRateLimit); } + public int getMaxDataTransferSpeed() { return this.getValue(Config.Server.maxDataTransferSpeed); } @@ -78,7 +84,14 @@ public class SessionConfig implements INetworkObject private static void registerConfigEntry(ConfigEntry configEntry, BiFunction valueConstrainer) { - CONFIG_ENTRIES.put(Objects.requireNonNull(configEntry.getServersideShortName()), new Entry(configEntry, valueConstrainer)); + CONFIG_ENTRIES.compute(Objects.requireNonNull(configEntry.getServersideShortName()), (key, existingEntry) -> { + if (existingEntry != null) + { + throw new IllegalArgumentException("Attempted to register config entry with duplicate serversideShortName: " + key); + } + + return new Entry(configEntry, valueConstrainer); + }); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/InvalidLevelException.java b/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/RequestOutOfRangeException.java similarity index 80% rename from core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/InvalidLevelException.java rename to core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/RequestOutOfRangeException.java index 9a7d20a80..fdd32e611 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/InvalidLevelException.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/exceptions/RequestOutOfRangeException.java @@ -19,8 +19,9 @@ package com.seibel.distanthorizons.core.network.exceptions; -/** Fired if a user attempts to run an operation in a level they aren't currently in. */ -public class InvalidLevelException extends Exception +/** Fired if the client attempts to request an LOD out of allowed range. */ +public class RequestOutOfRangeException extends Exception { - public InvalidLevelException(String message) { super(message); } + public RequestOutOfRangeException(String message) { super(message); } + } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/requests/ExceptionMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/requests/ExceptionMessage.java index c6b4bae46..c0679fc40 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/requests/ExceptionMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/requests/ExceptionMessage.java @@ -20,8 +20,8 @@ package com.seibel.distanthorizons.core.network.messages.requests; import com.google.common.base.MoreObjects; -import com.seibel.distanthorizons.core.network.exceptions.InvalidLevelException; import com.seibel.distanthorizons.core.network.exceptions.RateLimitedException; +import com.seibel.distanthorizons.core.network.exceptions.RequestOutOfRangeException; import com.seibel.distanthorizons.core.network.exceptions.RequestRejectedException; import com.seibel.distanthorizons.core.network.messages.AbstractTrackableMessage; import io.netty.buffer.ByteBuf; @@ -36,7 +36,7 @@ public class ExceptionMessage extends AbstractTrackableMessage {{ // All exceptions here must include constructor: (String) this.add(RateLimitedException.class); - this.add(InvalidLevelException.class); + this.add(RequestOutOfRangeException.class); this.add(RequestRejectedException.class); }}; diff --git a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java index 3fb1252af..64bf74423 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/pos/DhSectionPos.java @@ -261,12 +261,18 @@ public class DhSectionPos + Math.abs(getCenterBlockPosZ(pos) - blockPos.z); } - public static int getChebyshevBlockDistance(long pos, DhBlockPos2D blockPos) + /** + * Returns the signed distance from a given block to a given section.
+ * Essentially acts like a distance from the block to the nearest edge of the section, + * except inside the section it's negative.
+ * Useful for detail level insensitive distance comparisons. + */ + public static int getChebyshevSignedBlockDistance(long pos, DhBlockPos2D blockPos) { return Math.max( Math.abs(getCenterBlockPosX(pos) - blockPos.x), Math.abs(getCenterBlockPosZ(pos) - blockPos.z) - ); + ) - getBlockWidth(pos) / 2; } From 7f761e415f275bf14cdf3d0c2ae5b1e8d1196798 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Tue, 12 Nov 2024 07:27:41 -0600 Subject: [PATCH 02/24] Fix potential null pointers if other mods mess with the MVM or Proj matricies --- .../api/objects/math/DhApiMat4f.java | 32 +++++++++---------- .../core/api/internal/ClientApi.java | 5 ++- .../distanthorizons/core/util/RenderUtil.java | 23 ++++++++----- 3 files changed, 35 insertions(+), 25 deletions(-) diff --git a/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiMat4f.java b/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiMat4f.java index 2c05ae708..feb7594ac 100644 --- a/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiMat4f.java +++ b/api/src/main/java/com/seibel/distanthorizons/api/objects/math/DhApiMat4f.java @@ -88,25 +88,25 @@ public class DhApiMat4f implements IDhApiCopyable /** Expects the values of the input array to be in row major order (AKA rows then columns) */ public DhApiMat4f(float[] values) { - m00 = values[0]; - m01 = values[1]; - m02 = values[2]; - m03 = values[3]; + this.m00 = values[0]; + this.m01 = values[1]; + this.m02 = values[2]; + this.m03 = values[3]; - m10 = values[4]; - m11 = values[5]; - m12 = values[6]; - m13 = values[7]; + this.m10 = values[4]; + this.m11 = values[5]; + this.m12 = values[6]; + this.m13 = values[7]; - m20 = values[8]; - m21 = values[9]; - m22 = values[10]; - m23 = values[11]; + this.m20 = values[8]; + this.m21 = values[9]; + this.m22 = values[10]; + this.m23 = values[11]; - m30 = values[12]; - m31 = values[13]; - m32 = values[14]; - m33 = values[15]; + this.m30 = values[12]; + this.m31 = values[13]; + this.m32 = values[14]; + this.m33 = values[15]; } 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 8e5cde40a..c3d2d3369 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 @@ -25,6 +25,7 @@ import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass; import com.seibel.distanthorizons.api.methods.events.abstractEvents.*; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; +import com.seibel.distanthorizons.core.logging.f3.F3Screen; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.render.DhApiRenderProxy; import com.seibel.distanthorizons.core.render.renderer.FadeRenderer; @@ -430,7 +431,9 @@ public class ClientApi try { - if (!RenderUtil.shouldLodsRender(levelWrapper)) + // TODO write this message to the F3 menu so people can see when a different mod screws with the lightmap + String reasonLodsCannotRender = RenderUtil.shouldLodsRender(levelWrapper, renderEventParam); + if (reasonLodsCannotRender != null) { return; } 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 37dc6957f..2fdbe6d1b 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 @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.util; +import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; import com.seibel.distanthorizons.core.api.internal.SharedApi; import com.seibel.distanthorizons.core.config.Config; import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; @@ -164,37 +165,43 @@ public class RenderUtil return -1.0f; } - /** @return false if LODs shouldn't be rendered for any reason */ - public static boolean shouldLodsRender(ILevelWrapper levelWrapper) + /** @return a message if LODs shouldn't be rendered, null if the LODs can render */ + public static String shouldLodsRender(ILevelWrapper levelWrapper, DhApiRenderParam renderEventParam) { if (!MC.playerExists()) { - return false; + return "No Player Exists"; } if (levelWrapper == null) { - return false; + return "No Level Given"; } IDhClientWorld clientWorld = SharedApi.getIDhClientWorld(); if (clientWorld == null) { - return false; + return "No Client World Loaded"; } IDhClientLevel level = clientWorld.getClientLevel(levelWrapper); if (level == null) { - return false; //Level is not ready yet. + return "No Client Level Loaded"; //Level is not ready yet. } if (MC_RENDER.getLightmapWrapper(levelWrapper) == null) { - return false; + return "No Lightmap loaded"; } - return true; + if (renderEventParam.dhModelViewMatrix == null + || renderEventParam.mcModelViewMatrix == null) + { + return "No MVM or Proj Matrix Given"; + } + + return null; } } From 937b36bfa2f544032d767b63d8766b19bd0e5094 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Wed, 13 Nov 2024 07:00:31 -0600 Subject: [PATCH 03/24] Catch a few FullDataSourceV2 Repo closed exceptions --- .../core/level/AbstractDhServerLevel.java | 2 +- .../core/sql/repo/FullDataSourceV2Repo.java | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) 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 059697189..20e1291f7 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 @@ -258,7 +258,7 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I // the server timestamp will be null if no LOD data exists for this position Long serverTimestamp = this.serverside.fullDataFileHandler.getTimestampForPos(message.sectionPos); if (serverTimestamp == null - || serverTimestamp <= clientTimestamp) + || serverTimestamp <= clientTimestamp) { // either no data exists to sync, or the client is already up to date rateLimiterSet.syncOnLoginRateLimiter.release(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/FullDataSourceV2Repo.java b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/FullDataSourceV2Repo.java index 6558775ee..9280f2ec3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/FullDataSourceV2Repo.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/sql/repo/FullDataSourceV2Repo.java @@ -23,6 +23,7 @@ import com.seibel.distanthorizons.api.enums.config.EDhApiDataCompressionMode; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.sql.DbConnectionClosedException; import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO; import com.seibel.distanthorizons.core.util.objects.dataStreams.DhDataInputStream; import it.unimi.dsi.fastutil.longs.LongArrayList; @@ -34,6 +35,7 @@ import java.io.File; import java.io.IOException; import java.sql.PreparedStatement; import java.sql.SQLException; +import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.stream.Collectors; @@ -311,6 +313,10 @@ public class FullDataSourceV2Repo extends AbstractDhRepo> row = this.queryDictionary(preparedStatement); return !row.isEmpty() ? (Long) row.get(0).get("LastModifiedUnixDateTime") : null; } + catch (DbConnectionClosedException e) + { + return null; + } catch (SQLException e) { throw new RuntimeException(e); @@ -342,6 +348,10 @@ public class FullDataSourceV2Repo extends AbstractDhRepo (long) row.get("LastModifiedUnixDateTime")) ); } + catch (DbConnectionClosedException e) + { + return new HashMap<>(); + } catch (SQLException e) { throw new RuntimeException(e); From 127ec81adeba783cb5a90c56add2422b65ad39b3 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Wed, 13 Nov 2024 18:26:20 -0600 Subject: [PATCH 04/24] Add AbstractConfigType.typeIsFloatingPointNumber() --- .../listeners/ConfigChangeListener.java | 4 +- .../core/config/types/AbstractConfigType.java | 64 ++++++++----------- 2 files changed, 30 insertions(+), 38 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/listeners/ConfigChangeListener.java b/core/src/main/java/com/seibel/distanthorizons/core/config/listeners/ConfigChangeListener.java index d05fd938d..d969f8995 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/listeners/ConfigChangeListener.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/listeners/ConfigChangeListener.java @@ -54,9 +54,9 @@ public class ConfigChangeListener implements IConfigListener, Closeable public void onConfigValueSet() { T newValue = this.configEntry.get(); - if (newValue != previousValue) + if (newValue != this.previousValue) { - previousValue = newValue; + this.previousValue = newValue; this.onValueChangeFunc.accept(newValue); } } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/types/AbstractConfigType.java b/core/src/main/java/com/seibel/distanthorizons/core/config/types/AbstractConfigType.java index d1b903f11..0542cf647 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/config/types/AbstractConfigType.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/config/types/AbstractConfigType.java @@ -33,6 +33,7 @@ public abstract class AbstractConfigType public String category = ""; // This should only be set once in the init public String name; // This should only be set once in the init protected final T defaultValue; + protected final boolean isFloatingPointNumber; protected T value; public ConfigBase configBase; @@ -40,54 +41,45 @@ public abstract class AbstractConfigType protected EConfigEntryAppearance appearance; - protected AbstractConfigType(EConfigEntryAppearance appearance, T value) + + + //=============// + // constructor // + //=============// + + protected AbstractConfigType(EConfigEntryAppearance appearance, T defaultValue) { - this.defaultValue = value; - this.value = value; + this.defaultValue = defaultValue; + this.value = defaultValue; this.appearance = appearance; + + Class defaultValueClass = defaultValue.getClass(); + this.isFloatingPointNumber = (defaultValueClass == Double.class || defaultValueClass == Float.class); } + + //=========// + // methods // + //=========// + /** Gets the value */ - public T get() - { - return this.value; - } + public T get() { return this.value; } /** Sets the value */ - public void set(T newValue) - { - this.value = newValue; - } + public void set(T newValue) { this.value = newValue; } - public EConfigEntryAppearance getAppearance() - { - return appearance; - } - public void setAppearance(EConfigEntryAppearance newAppearance) - { - this.appearance = newAppearance; - } + public EConfigEntryAppearance getAppearance() { return this.appearance; } + public void setAppearance(EConfigEntryAppearance newAppearance) { this.appearance = newAppearance; } - public String getCategory() - { - return this.category; - } - public String getName() - { - return this.name; - } - public String getNameWCategory() - { - return (this.category.isEmpty() ? "" : this.category + ".") + this.name; - } + public String getCategory() { return this.category; } + public String getName() { return this.name; } + public String getNameWCategory() { return (this.category.isEmpty() ? "" : this.category + ".") + this.name; } - // Gets the class of T - public Class getType() - { - return this.defaultValue.getClass(); - } + /** Gets the class of T */ + public Class getType() { return this.defaultValue.getClass(); } + public boolean typeIsFloatingPointNumber() { return this.isFloatingPointNumber; } protected static abstract class Builder { From 5448b8890d1d079f56867ec899b392bf4e9866ca Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:23:35 +0500 Subject: [PATCH 05/24] Replace truncating the hashed seed with encoding it into base32 --- .../wrapperInterfaces/world/ILevelWrapper.java | 16 ++++++++++++++-- .../world/IServerLevelWrapper.java | 4 ++-- 2 files changed, 16 insertions(+), 4 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 d10b8e71b..652d7be55 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 @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.world; +import com.google.common.primitives.Longs; import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; import com.seibel.distanthorizons.core.level.IDhLevel; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos; @@ -26,10 +27,12 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; +import org.apache.commons.codec.binary.Base32; /** Can be either a Server world or a Client world. */ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable { + Base32 base32 = new Base32(true); @Override IDimensionTypeWrapper getDimensionType(); @@ -38,12 +41,21 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable String getDimensionName(); long getHashedSeed(); + /** + * Returns the result of {@link #getHashedSeed()}, encoded into a short string.
+ * Prefer using this method over stringifying the number directly. + */ + default String getHashedSeedEncoded() + { + String encoded = base32.encodeAsString(Longs.toByteArray(this.getHashedSeed())); + return encoded.substring(0, 13).toLowerCase(); // Remaining 3 chars are padding + } /** * A string intended to uniquely identify this level. */ - @Override - default String getDhIdentifier() { return this.getDimensionName() + "_" + this.getHashedSeed(); } + @Override + String getDhIdentifier(); @Override boolean hasCeiling(); diff --git a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java index c4e419d12..cfc6096c3 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/wrapperInterfaces/world/IServerLevelWrapper.java @@ -45,12 +45,12 @@ public interface IServerLevelWrapper extends ILevelWrapper .replaceAll(" ", "_"); levelKeyPrefix += (!levelKeyPrefix.isEmpty() ? "_" : "") + cleanWorldFolderName - + "_" + this.getHashedSeed(); + + "_" + this.getHashedSeedEncoded(); } if (levelKeyPrefix.isEmpty()) { - levelKeyPrefix = String.valueOf(this.getHashedSeed()); + levelKeyPrefix = this.getHashedSeedEncoded(); } String mainPart = "@" + dimensionName; From f92a1ccc27cb6ffaca70431a27120a20148dbafa Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Thu, 14 Nov 2024 15:52:41 +0500 Subject: [PATCH 06/24] Add a comment to #pluginMessageReceived methods --- .../distanthorizons/core/api/internal/ClientApi.java | 7 ++++++- .../distanthorizons/core/api/internal/ServerApi.java | 6 ++++++ 2 files changed, 12 insertions(+), 1 deletion(-) 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 c3d2d3369..739dc00da 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 @@ -25,7 +25,7 @@ import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass; import com.seibel.distanthorizons.api.methods.events.abstractEvents.*; import com.seibel.distanthorizons.api.methods.events.sharedParameterObjects.DhApiRenderParam; import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure; -import com.seibel.distanthorizons.core.logging.f3.F3Screen; +import com.seibel.distanthorizons.core.network.messages.MessageRegistry; import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.render.DhApiRenderProxy; import com.seibel.distanthorizons.core.render.renderer.FadeRenderer; @@ -356,6 +356,11 @@ public class ClientApi // networking // //============// + /** + * Forwards a decoded message into the registered handlers. + * + * @see MessageRegistry + */ public void pluginMessageReceived(@NotNull AbstractNetworkMessage message) { NetworkSession networkSession = this.pluginChannelApi.networkSession; 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 d852c1504..e09399d51 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,6 +22,7 @@ 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.network.messages.MessageRegistry; import com.seibel.distanthorizons.core.world.*; import com.seibel.distanthorizons.core.wrapperInterfaces.misc.IServerPlayerWrapper; import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector; @@ -186,6 +187,11 @@ public class ServerApi } } + /** + * Forwards a decoded message into the registered handlers. + * + * @see MessageRegistry + */ public void pluginMessageReceived(IServerPlayerWrapper player, @NotNull AbstractNetworkMessage message) { if (DhApiWorldProxy.INSTANCE.worldLoaded() && DhApiWorldProxy.INSTANCE.getReadOnly()) From f5fc0004d5bea13a9cc29b951e433a1b6115e1bb Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 15 Nov 2024 09:32:51 +0500 Subject: [PATCH 07/24] Replace Apache's base32 with guava --- .../core/wrapperInterfaces/world/ILevelWrapper.java | 6 ++---- 1 file changed, 2 insertions(+), 4 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 652d7be55..5b8922c66 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 @@ -19,6 +19,7 @@ package com.seibel.distanthorizons.core.wrapperInterfaces.world; +import com.google.common.io.BaseEncoding; import com.google.common.primitives.Longs; import com.seibel.distanthorizons.api.interfaces.world.IDhApiLevelWrapper; import com.seibel.distanthorizons.core.level.IDhLevel; @@ -27,13 +28,10 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos; import com.seibel.distanthorizons.core.wrapperInterfaces.block.IBlockStateWrapper; import com.seibel.distanthorizons.core.wrapperInterfaces.chunk.IChunkWrapper; import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindable; -import org.apache.commons.codec.binary.Base32; /** Can be either a Server world or a Client world. */ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable { - Base32 base32 = new Base32(true); - @Override IDimensionTypeWrapper getDimensionType(); @@ -47,7 +45,7 @@ public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable */ default String getHashedSeedEncoded() { - String encoded = base32.encodeAsString(Longs.toByteArray(this.getHashedSeed())); + String encoded = BaseEncoding.base32Hex().encode(Longs.toByteArray(this.getHashedSeed())); return encoded.substring(0, 13).toLowerCase(); // Remaining 3 chars are padding } From 6595f5d90efccac02f5a3c635bc45f11cfb0d3d3 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 16 Nov 2024 21:57:58 -0600 Subject: [PATCH 08/24] Fix fog shader compiling on some software renderers --- core/src/main/resources/shaders/fog/fog.frag | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/resources/shaders/fog/fog.frag b/core/src/main/resources/shaders/fog/fog.frag index a4495bd82..d4982674d 100644 --- a/core/src/main/resources/shaders/fog/fog.frag +++ b/core/src/main/resources/shaders/fog/fog.frag @@ -255,7 +255,7 @@ float calculateHeightFogDepth(float worldYPos) else { // shouldn't happen, - return 0; + return 0.0; } } From 37dd0c4d55e94b638307f9b51c76c541acb768aa Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sat, 16 Nov 2024 22:07:31 -0600 Subject: [PATCH 09/24] up version number 2.3.0-a -> 2.3.0-b --- .../main/java/com/seibel/distanthorizons/coreapi/ModInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index 74779b072..c3652eed6 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -38,7 +38,7 @@ public final class ModInfo public static final String NAME = "DistantHorizons"; /** Human-readable version of NAME */ public static final String READABLE_NAME = "Distant Horizons"; - public static final String VERSION = "2.3.0-a-dev"; + public static final String VERSION = "2.3.0-b-dev"; /** Returns true if the current build is an unstable developer build, false otherwise. */ public static boolean IS_DEV_BUILD = VERSION.toLowerCase().contains("dev"); From bcc44ab5e3230ddff78c658e1c567779335ebc6c Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 17 Nov 2024 06:22:48 -0600 Subject: [PATCH 10/24] Add player's DhSectionPos to the F3 menu --- .../distanthorizons/core/logging/f3/F3Screen.java | 10 ++++++++++ 1 file changed, 10 insertions(+) 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 8c1011974..ed5cbecd4 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 @@ -20,12 +20,16 @@ package com.seibel.distanthorizons.core.logging.f3; import com.seibel.distanthorizons.core.api.internal.SharedApi; +import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector; import com.seibel.distanthorizons.core.jar.ModJarInfo; import com.seibel.distanthorizons.core.level.IDhLevel; +import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.render.RenderBufferHandler; import com.seibel.distanthorizons.core.render.renderer.generic.GenericObjectRenderer; import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil; import com.seibel.distanthorizons.core.world.AbstractDhWorld; +import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper; +import com.seibel.distanthorizons.coreapi.DependencyInjection.DependencyInjector; import com.seibel.distanthorizons.coreapi.ModInfo; import com.seibel.distanthorizons.coreapi.util.StringUtil; import org.apache.logging.log4j.LogManager; @@ -38,6 +42,7 @@ import java.util.concurrent.ThreadPoolExecutor; public class F3Screen { private static final Logger LOGGER = LogManager.getLogger(); + private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class); public static final NumberFormat NUMBER_FORMAT = NumberFormat.getIntegerInstance(); @@ -88,6 +93,11 @@ public class F3Screen { messageList.add("Build: " + StringUtil.shortenString(ModJarInfo.Git_Commit, 8) + " (" + ModJarInfo.Git_Branch + ")"); } + if (MC_CLIENT != null) + { + // player pos + messageList.add("LOD Pos: " + DhSectionPos.toString(DhSectionPos.encodeContaining(DhSectionPos.SECTION_BLOCK_DETAIL_LEVEL, MC_CLIENT.getPlayerChunkPos())) ); + } messageList.add(""); // thread pools messageList.add(getThreadPoolStatString("World Gen", worldGenPool));//"World Gen Tasks: 40/5304, (in progress: 7)"); From 9f966f0643f2612e7e9c398ca31d157ef00c25f6 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Sun, 17 Nov 2024 08:03:04 -0600 Subject: [PATCH 11/24] Remove manifold string plugin --- .../com/seibel/distanthorizons/core/level/WorldGenModule.java | 2 +- .../core/multiplayer/client/ClientNetworkState.java | 2 +- .../com/seibel/distanthorizons/core/world/AbstractDhWorld.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java b/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java index 847b186e5..3036013c1 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/level/WorldGenModule.java @@ -191,7 +191,7 @@ public class WorldGenModule implements Closeable String waitingCountStr = F3Screen.NUMBER_FORMAT.format(worldGenState.worldGenerationQueue.getWaitingTaskCount()); String inProgressCountStr = F3Screen.NUMBER_FORMAT.format(worldGenState.worldGenerationQueue.getInProgressTaskCount()); String totalCountEstimateStr = F3Screen.NUMBER_FORMAT.format(worldGenState.worldGenerationQueue.getEstimatedTotalTaskCount()); - messageList.add("World Gen Tasks: ${waitingCountStr}/${totalCountEstimateStr} (in progress: ${inProgressCountStr})"); + messageList.add("World Gen Tasks: "+waitingCountStr+"/"+totalCountEstimateStr+" (in progress "+inProgressCountStr+")"); worldGenState.worldGenerationQueue.addDebugMenuStringsToList(messageList); } diff --git a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java index 3246fa015..f3abf60cd 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/multiplayer/client/ClientNetworkState.java @@ -82,7 +82,7 @@ public class ClientNetworkState implements Closeable } this.serverTimeOffset = message.serverTime - System.currentTimeMillis(); - LOGGER.info("Server time offset: [${this.serverTimeOffset}] ms"); + LOGGER.info("Server time offset: ["+this.serverTimeOffset+"] ms"); }); this.networkSession.registerHandler(CloseInternalEvent.class, message -> diff --git a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhWorld.java b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhWorld.java index 17615a238..233d101a2 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhWorld.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/world/AbstractDhWorld.java @@ -69,7 +69,7 @@ public abstract class AbstractDhWorld implements IDhWorld, Closeable readOnlyStr += " - ReadOnly"; } - String message = "${environment} World with ${levelCountStr} levels${readOnlyStr}"; + String message = environment+" World with "+levelCountStr+" levels"+readOnlyStr; messageList.add(message); } From deaccf53f98f804edd77f46dfaa6aff8cc6976e9 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 18 Nov 2024 07:40:16 -0600 Subject: [PATCH 12/24] Fix sometimes not loading high-detail LODs when on a server --- .../distanthorizons/core/render/LodQuadTree.java | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) 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 dd0b123ea..f12d215de 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 @@ -345,9 +345,15 @@ public class LodQuadTree extends QuadTree implements IDebugRen // prepare this section for rendering if (!renderSection.gpuUploadInProgress() && renderSection.renderBuffer == null - // this check is specifically for N-sized world generators where the higher quality - // data source may not exist yet - && renderSection.getFullDataSourceExists()) + && + ( + // this check is specifically for N-sized world generators where the higher quality + // data source may not exist yet, this is done to prevent holes while waiting for said generator + renderSection.getFullDataSourceExists() + // if we can't request generation we don't want to check for full data existing + // since that will prevent server LODs from loading high-detail LODs where quadrants haven't been generated. + || !this.fullDataSourceProvider.canQueueRetrieval()) + ) { nodesNeedingLoading.add(renderSection); } @@ -424,7 +430,7 @@ public class LodQuadTree extends QuadTree implements IDebugRen if (renderSection != null) { // this data source may now exist - renderSection.updateFullDataSourceExists(); + renderSection.updateFullDataSourceExists(); if (renderSection.canRender()) { From eb749d6bb04bdbd96b121af05bbd68780089afb0 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 18 Nov 2024 07:46:24 -0600 Subject: [PATCH 13/24] Fix a rare error where chunk lighting is set to -1 --- .../distanthorizons/core/generation/DhLightingEngine.java | 6 ++++++ 1 file changed, 6 insertions(+) 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 8a1584a73..f2448593e 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 @@ -246,6 +246,10 @@ public class DhLightingEngine // block light if (updateBlockLight) { + // done to prevent a rare issue where the light values are incorrectly set to -1 + // TODO why could that happen? + centerChunk.clearDhBlockLighting(); + this.propagateChunkLightPosList(blockLightWorldPosQueue, adjacentChunkHolder, (neighbourChunk, relBlockPos) -> neighbourChunk.getDhBlockLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ()), (neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhBlockLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ(), newLightValue), @@ -255,6 +259,8 @@ public class DhLightingEngine // sky light if (updateSkyLight) { + centerChunk.clearDhSkyLighting(); + this.propagateChunkLightPosList(skyLightWorldPosQueue, adjacentChunkHolder, (neighbourChunk, relBlockPos) -> neighbourChunk.getDhSkyLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ()), (neighbourChunk, relBlockPos, newLightValue) -> neighbourChunk.setDhSkyLight(relBlockPos.getX(), relBlockPos.getY(), relBlockPos.getZ(), newLightValue), From f7926456a3f8f32cb406bc6f9f0c918998c44d4b Mon Sep 17 00:00:00 2001 From: helpimnotdrowning <5190181-helpimnotdrowning@users.noreply.gitlab.com> Date: Tue, 19 Nov 2024 16:15:23 -0600 Subject: [PATCH 14/24] Fix distant-horizons#870: Correctly template regex --- .../core/network/messages/base/LevelInitMessage.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/LevelInitMessage.java b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/LevelInitMessage.java index 7f8d13a96..9ae276bed 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/LevelInitMessage.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/network/messages/base/LevelInitMessage.java @@ -12,7 +12,8 @@ public class LevelInitMessage extends AbstractNetworkMessage // prefix@namespace:path // 1-150 characters in total, all parts except namespace can be omitted - public static final String VALIDATION_REGEX = "^(?=.{1,$MAX_LENGTH}$)([$PART_ALLOWED_CHARS_REGEX]+@)?[$PART_ALLOWED_CHARS_REGEX]+(:[$PART_ALLOWED_CHARS_REGEX]+)?$"; + public static final String VALIDATION_REGEX = String.format("^(?=.{1,%s}$)([%s]+@)?[%s]+(:[%s]+)?$", ++ MAX_LENGTH, PART_ALLOWED_CHARS_REGEX, PART_ALLOWED_CHARS_REGEX, PART_ALLOWED_CHARS_REGEX); public String levelKey; From 6ff2c9f14c1fbf2ba643e9ea18fb80c89cb0d56c Mon Sep 17 00:00:00 2001 From: James Seibel Date: Thu, 21 Nov 2024 07:17:20 -0600 Subject: [PATCH 15/24] improve columnRenderbuffer assertion message --- .../render/bufferBuilding/ColumnRenderBufferBuilder.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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 17f37ad5e..5839a3cfe 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 @@ -275,7 +275,10 @@ public class ColumnRenderBufferBuilder // the old logic handled additional cases, but they never appeared to fire, // so just these two cases should be fine - LodUtil.assertTrue(adjDetailLevel == thisDetailLevel || adjDetailLevel > thisDetailLevel); + if (adjDetailLevel == thisDetailLevel || adjDetailLevel > thisDetailLevel) + { + LodUtil.assertNotReach("Mismatch between adjacent detail level ["+adjDetailLevel+"] and this render source's detail level ["+thisDetailLevel+"]. Detail levels should be adj >= this."); + } adjColumnViews[lodDirection.ordinal() - 2] = adjRenderSource.getVerticalDataPointView(xAdj, zAdj); } From 23c160e948d307eb0a5791399dbf15e3398e8236 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Thu, 21 Nov 2024 07:41:31 -0600 Subject: [PATCH 16/24] Accidentally flipped an assertion last commit --- .../render/bufferBuilding/ColumnRenderBufferBuilder.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 5839a3cfe..4f5898b60 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 @@ -275,7 +275,8 @@ public class ColumnRenderBufferBuilder // the old logic handled additional cases, but they never appeared to fire, // so just these two cases should be fine - if (adjDetailLevel == thisDetailLevel || adjDetailLevel > thisDetailLevel) + boolean expectedDetailLevels = (adjDetailLevel == thisDetailLevel) || (adjDetailLevel > thisDetailLevel); + if (!expectedDetailLevels) { LodUtil.assertNotReach("Mismatch between adjacent detail level ["+adjDetailLevel+"] and this render source's detail level ["+thisDetailLevel+"]. Detail levels should be adj >= this."); } From b59965671ccd3f529278f3851ae73fbd6dd6d8e0 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 22 Nov 2024 00:16:30 +0500 Subject: [PATCH 17/24] Make generation limit work --- .../RemoteFullDataSourceProvider.java | 30 +++++++++++++++++-- .../core/level/AbstractDhServerLevel.java | 15 ++++++---- .../AbstractFullDataNetworkRequestQueue.java | 17 +++++++++-- .../core/render/LodRenderSection.java | 1 - 4 files changed, 52 insertions(+), 11 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java index 0ca214482..cb84c8a3e 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java @@ -22,10 +22,11 @@ package com.seibel.distanthorizons.core.file.fullDatafile; import com.seibel.distanthorizons.core.dataObjects.fullData.sources.FullDataSourceV2; import com.seibel.distanthorizons.core.file.structure.ISaveStructure; import com.seibel.distanthorizons.core.generation.RemoteWorldRetrievalQueue; -import com.seibel.distanthorizons.core.level.IDhLevel; +import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.level.WorldGenModule; import com.seibel.distanthorizons.core.multiplayer.client.SyncOnLoadRequestQueue; import com.seibel.distanthorizons.core.pos.DhSectionPos; +import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; import org.jetbrains.annotations.Nullable; @@ -51,7 +52,7 @@ public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvide //=============// public RemoteFullDataSourceProvider( - IDhLevel level, ISaveStructure saveStructure, @Nullable File saveDirOverride, + IDhClientLevel level, ISaveStructure saveStructure, @Nullable File saveDirOverride, @Nullable SyncOnLoadRequestQueue syncOnLoadRequestQueue) { super(level, saveStructure, saveDirOverride); @@ -59,6 +60,24 @@ public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvide } + @Override + public boolean queuePositionForRetrieval(Long genPos) + { + if (this.syncOnLoadRequestQueue == null) + { + return super.queuePositionForRetrieval(genPos); + } + + int maxGenerationRequestDistance = this.syncOnLoadRequestQueue.networkState.sessionConfig.getMaxGenerationRequestDistance(); + DhBlockPos2D targetPos = this.level.getTargetPosForGeneration(); + if (targetPos == null || DhSectionPos.getChebyshevSignedBlockDistance(genPos, targetPos) / 16 > maxGenerationRequestDistance) + { + return false; + } + + return super.queuePositionForRetrieval(genPos); + } + //==================// // override methods // @@ -98,6 +117,13 @@ public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvide DhSectionPos.forEachChildAtDetailLevel(pos, DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL, childPos -> { + int maxSyncOnLoadDistance = this.syncOnLoadRequestQueue.networkState.sessionConfig.getMaxSyncOnLoadDistance(); + DhBlockPos2D targetPos = this.level.getTargetPosForGeneration(); + if (targetPos == null || DhSectionPos.getChebyshevSignedBlockDistance(childPos, targetPos) / 16 > maxSyncOnLoadDistance) + { + return; + } + if (!this.visitedPositions.add(childPos)) { return; 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 cda82959a..efcc0022f 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 @@ -196,20 +196,25 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I Vec3d playerPosition = serverPlayerState.getServerPlayer().getPosition(); int distanceFromPlayer = DhSectionPos.getChebyshevSignedBlockDistance(message.sectionPos, new DhBlockPos2D((int) playerPosition.x, (int) playerPosition.z)) / 16; - if (distanceFromPlayer > Config.Server.maxGenerationRequestDistance.get()) - { - message.sendResponse(new RequestOutOfRangeException("Distance too large: " + distanceFromPlayer + " > " + Config.Server.maxGenerationRequestDistance.get())); - return; - } ServerPlayerState.RateLimiterSet rateLimiterSet = serverPlayerState.getRateLimiterSet(this); if (message.clientTimestamp == null) { + if (distanceFromPlayer > Config.Server.maxGenerationRequestDistance.get()) + { + message.sendResponse(new RequestOutOfRangeException("Distance too large: " + distanceFromPlayer + " > " + Config.Server.maxGenerationRequestDistance.get())); + return; + } this.queueWorldGenForRequestMessage(serverPlayerState, message, rateLimiterSet); } else { + if (distanceFromPlayer > Config.Server.maxSyncOnLoadRequestDistance.get()) + { + message.sendResponse(new RequestOutOfRangeException("Distance too large: " + distanceFromPlayer + " > " + Config.Server.maxSyncOnLoadRequestDistance.get())); + return; + } this.queueLodSyncForRequestMessage(serverPlayerState, message, rateLimiterSet); } }); 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 7812129a9..525f8db17 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 @@ -118,6 +118,11 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende { this.waitingTasksBySectionPos.remove(sectionPos); + if (throwable instanceof CancellationException) + { + return; + } + this.finishedRequests.incrementAndGet(); if (!success || throwable != null) { @@ -163,7 +168,6 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende { Map.Entry mapEntry = this.waitingTasksBySectionPos.entrySet().stream() .filter(task -> task.getValue().networkDataSourceFuture == null) - .filter(task -> DhSectionPos.getChebyshevSignedBlockDistance(task.getKey(), targetPos) <= this.getMaxRequestDistance()) .min(Comparator.comparingInt(task -> DhSectionPos.getChebyshevSignedBlockDistance(task.getKey(), targetPos))) .orElse(null); @@ -176,6 +180,13 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende long sectionPos = mapEntry.getKey(); RequestQueueEntry entry = mapEntry.getValue(); + if (DhSectionPos.getChebyshevSignedBlockDistance(sectionPos, targetPos) > this.getMaxRequestDistance() * 16) + { + entry.future.cancel(false); + this.pendingTasksSemaphore.release(); + return; + } + Long offsetEntryTimestamp = entry.updateTimestamp != null ? entry.updateTimestamp + this.networkState.getServerTimeOffset() : null; @@ -365,8 +376,8 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende { renderer.renderBox(new DebugRenderer.Box(mapEntry.getKey(), -32f, 64f, 0.05f, mapEntry.getValue().networkDataSourceFuture != null ? Color.red - : DhSectionPos.getChebyshevSignedBlockDistance(mapEntry.getKey(), this.lastTargetPos) <= this.getMaxRequestDistance() ? Color.gray - : Color.lightGray + : DhSectionPos.getChebyshevSignedBlockDistance(mapEntry.getKey(), this.lastTargetPos) <= this.getMaxRequestDistance() * 16 ? Color.gray + : Color.darkGray )); } } 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 7e4bb84fc..9783529b5 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 @@ -490,7 +490,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable { // shouldn't normally happen, but just in case this.missingGenerationPos.add(pos); - break; } } } From b9092149744d609b289eb369c719ed67430a3c15 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Thu, 21 Nov 2024 17:21:14 -0600 Subject: [PATCH 18/24] Fix caught null pointer --- .../seibel/distanthorizons/core/render/LodRenderSection.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) 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 7e4bb84fc..4ff96f6e0 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 @@ -272,7 +272,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable } this.bufferUploadFuture = ColumnRenderBufferBuilder.uploadBuffersAsync(this.level, this.pos, lodQuadBuilder); - return this.bufferUploadFuture.thenCompose((buffer) -> + return this.bufferUploadFuture.thenAccept((buffer) -> { // needed to clean up the old data ColumnRenderBuffer previousBuffer = this.renderBuffer; @@ -286,8 +286,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable { previousBuffer.close(); } - - return null; }); } From 7455893ef8bea2e15dc6c516113ed53094ac67ea Mon Sep 17 00:00:00 2001 From: James Seibel Date: Thu, 21 Nov 2024 19:00:27 -0600 Subject: [PATCH 19/24] Fix race condition in LodRenderSection loading --- .../core/render/LodRenderSection.java | 55 ++++++++++++++----- 1 file changed, 40 insertions(+), 15 deletions(-) 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 4ff96f6e0..daa9b4fa1 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 @@ -334,9 +334,8 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable // use the already loading future if one is present ReferencedRenderSourceFutureWrapper oldFuture = this.renderSourceLoadingRefFuture; - if (oldFuture != null) + if (oldFuture != null && oldFuture.tryIncrementRefCount()) { - oldFuture.incrementRefCount(); return oldFuture; } @@ -642,23 +641,49 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable public ReferencedRenderSourceFutureWrapper(CompletableFuture future) { this.future = future; } - public void incrementRefCount() { this.refCount.incrementAndGet(); } + + + /** @return true if this {@link ReferencedRenderSourceFutureWrapper} can be acquired, false if it has already been freed */ + public boolean tryIncrementRefCount() + { + // synchronized to prevent incrementing/decrementing at the same time + synchronized (this.refCount) + { + int refCount = this.refCount.get(); + if (refCount <= 0) + { + // there are no references to this data source and it has been returned to the pool + // a new reference will be needed + return false; + } + else + { + // at least one other + this.refCount.getAndIncrement(); + return true; + } + } + } public void decrementRefCount() { - int refCount = this.refCount.decrementAndGet(); - if (refCount <= 0) + // synchronized to prevent incrementing/decrementing at the same time + synchronized (this.refCount) { - // this render section should only be released once - if (refCount < 0) + int refCount = this.refCount.decrementAndGet(); + if (refCount <= 0) { - LodUtil.assertNotReach("ReferencedRenderSourceFutureWrapper was released more than once! Ref Count ["+refCount+"]."); - } - - // return data source to the pool - ColumnRenderSource source = this.future.getNow(null); - if (source != null) - { - ColumnRenderSource.DATA_SOURCE_POOL.returnPooledDataSource(source); + // this should only be released once + if (refCount < 0) + { + LodUtil.assertNotReach("ReferencedRenderSourceFutureWrapper was released more than once! Ref Count [" + refCount + "]."); + } + + // return data source to the pool + ColumnRenderSource source = this.future.getNow(null); + if (source != null) + { + ColumnRenderSource.DATA_SOURCE_POOL.returnPooledDataSource(source); + } } } } From 0f2e007eff236e9b020bd80545e4b82e2ec360f7 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 22 Nov 2024 13:29:06 +0500 Subject: [PATCH 20/24] Fix config descriptions & clean up --- .../distanthorizons/core/config/Config.java | 29 +++------- .../core/level/AbstractDhServerLevel.java | 2 - .../AbstractFullDataNetworkRequestQueue.java | 6 +-- .../assets/distanthorizons/lang/en_us.json | 53 +++++++++++++------ 4 files changed, 44 insertions(+), 46 deletions(-) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java b/core/src/main/java/com/seibel/distanthorizons/core/config/Config.java index 709b185e3..bd8f2191b 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 @@ -1546,15 +1546,9 @@ public class Config public static ConfigEntry maxGenerationRequestDistance = new ConfigEntry.Builder() .setServersideShortName("maxGenerationRequestDistance") - .setMinDefaultMax(32, 1024, 4096) + .setMinDefaultMax(256, 4096, 4096) .comment("" + - "Defines the distance players will receive real-time updates for if enabled. \n" + - "\n" + - "Note: \n" + - "This setting does not prevent players from generating farther out. \n" + - "If you want to limit performance impact, change rate limits \n" + - "and thread count/runtime ratio settings instead. \n" + - "It also does not affect the visuals on clients. \n" + + "Defines the distance allowed to generate around the player." + "") .setPerformance(EConfigEntryPerformance.HIGH) .build(); @@ -1573,13 +1567,7 @@ public class Config .setServersideShortName("realTimeUpdateDistanceRadius") .setMinDefaultMax(32, 256, 4096) .comment("" + - "Defines the distance players will receive real-time updates for if enabled. \n" + - "\n" + - "Note: \n" + - "This setting does not prevent players from generating farther out. \n" + - "If you want to limit performance impact, change rate limits \n" + - "and thread count/runtime ratio settings instead. \n" + - "It also does not affect the visuals on clients. \n" + + "Defines the distance the player will receive updates around." + "") .setPerformance(EConfigEntryPerformance.HIGH) .build(); @@ -1605,15 +1593,10 @@ public class Config public static ConfigEntry maxSyncOnLoadRequestDistance = new ConfigEntry.Builder() .setServersideShortName("maxSyncOnLoadRequestDistance") - .setMinDefaultMax(32, 1024, 4096) + .setMinDefaultMax(256, 4096, 4096) .comment("" + - "Defines the distance players will receive real-time updates for if enabled. \n" + - "\n" + - "Note: \n" + - "This setting does not prevent players from generating farther out. \n" + - "If you want to limit performance impact, change rate limits \n" + - "and thread count/runtime ratio settings instead. \n" + - "It also does not affect the visuals on clients. \n" + + "Defines the distance allowed to be synchronized around the player. \n" + + "Should be the same or larger than maxGenerationRequestDistance in most cases." + "") .setPerformance(EConfigEntryPerformance.HIGH) .build(); 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 efcc0022f..df537730b 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 @@ -253,8 +253,6 @@ public abstract class AbstractDhServerLevel extends AbstractDhLevel implements I } private void queueLodSyncForRequestMessage(ServerPlayerState serverPlayerState, FullDataSourceRequestMessage message, ServerPlayerState.RateLimiterSet rateLimiterSet) { - - if (!serverPlayerState.sessionConfig.getSynchronizeOnLoad()) { message.sendResponse(new RequestRejectedException("Operation is disabled in config.")); 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 525f8db17..5c1d2b6c0 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 @@ -71,8 +71,6 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende private final SupplierBasedRateLimiter rateLimiter = new SupplierBasedRateLimiter<>(this::getRequestRateLimit); - private DhBlockPos2D lastTargetPos = new DhBlockPos2D(0, 0); - //=============// @@ -146,8 +144,6 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende return false; } - this.lastTargetPos = targetPos; - // queue requests until the queue is full while (this.getInProgressTaskCount() < this.getWaitingTaskCount() && this.getInProgressTaskCount() < this.getRequestRateLimit() @@ -376,7 +372,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende { renderer.renderBox(new DebugRenderer.Box(mapEntry.getKey(), -32f, 64f, 0.05f, mapEntry.getValue().networkDataSourceFuture != null ? Color.red - : DhSectionPos.getChebyshevSignedBlockDistance(mapEntry.getKey(), this.lastTargetPos) <= this.getMaxRequestDistance() * 16 ? Color.gray + : DhSectionPos.getChebyshevSignedBlockDistance(mapEntry.getKey(), Objects.requireNonNull(this.level.getTargetPosForGeneration())) <= this.getMaxRequestDistance() * 16 ? Color.gray : Color.darkGray )); } 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 a2940b8c9..e8c777f16 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -669,34 +669,55 @@ "Server", - "distanthorizons.config.server.realTimeUpdateDistanceRadiusInChunks": - "Realtime update Radius In Chunks", - "distanthorizons.config.server.realTimeUpdateDistanceRadiusInChunks.@tooltip": - "Defines the how far away players will receive real-time updates for if enabled.", + "distanthorizons.config.server.sendLevelKeys": + "Send Level Keys", + "distanthorizons.config.server.sendLevelKeys.@tooltip": + "Makes the server send level keys for each world.\nDisable this if you use alternative ways to send level keys.", + "distanthorizons.config.server.levelKeyPrefix": - "Level Key Prefix", + "Level Key Prefix", "distanthorizons.config.server.levelKeyPrefix.@tooltip": - "Prefix of the level keys sent to the clients.", + "Prefix of the level keys sent to the clients.\nIf the mod is running behind a proxy, each backend should use a unique value.\nIf this value is empty, level key will be based on the server's seed hash.", + "distanthorizons.config.server.generationRequestRateLimit": - "Rate Limit for Generation Requests", + "Rate Limit for Generation Requests", "distanthorizons.config.server.generationRequestRateLimit.@tooltip": - "How many LOD generation requests per second should a client send? \nAlso limits the amount of player's requests allowed to stay in the server's queue.", + "How many LOD generation requests per second should a client send?\nAlso limits the number of client requests allowed to stay in the server's queue.", + + "distanthorizons.config.server.maxGenerationRequestDistance": + "Max Generation Request Distance", + "distanthorizons.config.server.maxGenerationRequestDistance.@tooltip": + "Defines the distance allowed to generate around the player.", + "distanthorizons.config.server.enableRealTimeUpdates": - "Enable Real-time Updates", + "Enable Real-time Updates", "distanthorizons.config.server.enableRealTimeUpdates.@tooltip": - "If true, clients will receive real-time LOD updates for chunks outside the client's render distance.", + "If true, clients will receive real-time LOD updates for chunks outside the client's render distance.", + + "distanthorizons.config.server.realTimeUpdateDistanceRadiusInChunks": + "Real-time Update Radius in Chunks", + "distanthorizons.config.server.realTimeUpdateDistanceRadiusInChunks.@tooltip": + "Defines the distance the player will receive updates around.", + "distanthorizons.config.server.synchronizeOnLoad": - "Synchronize LODs on Load", + "Synchronize LODs on Load", "distanthorizons.config.server.synchronizeOnLoad.@tooltip": - "If true, clients will receive updated LODs on join if any changes occurred since last join.", + "If true, clients will receive updated LODs when joining or loading new LODs.", + "distanthorizons.config.server.syncOnLoadRateLimit": - "Rate Limit for Sync on Load", + "Rate Limit for Sync on Load", "distanthorizons.config.server.syncOnLoadRateLimit.@tooltip": - "How many LOD sync requests per second should a client send? \nAlso limits the amount of player's requests allowed to stay in the server's queue.", + "How many LOD sync requests per second should a client send?\nAlso limits the number of client's requests allowed to stay in the server's queue.", + + "distanthorizons.config.server.maxSyncOnLoadRequestDistance": + "Max Sync on Load Request Distance", + "distanthorizons.config.server.maxSyncOnLoadRequestDistance.@tooltip": + "Defines the distance allowed to be synchronized around the player.\nShould be the same or larger than maxGenerationRequestDistance in most cases.", + "distanthorizons.config.server.maxDataTransferSpeed": - "Maximum Data Transfer Speed, KB/s", + "Maximum Data Transfer Speed, KB/s", "distanthorizons.config.server.maxDataTransferSpeed.@tooltip": - "Maximum speed for uploading LODs to the clients, in KB/s.\nValue of 0 disables the limit.", + "Maximum speed for uploading LODs to the clients, in KB/s.\nValue of 0 disables the limit.", From 00fd3c236a4f3c582765798ae28d9e80fa631892 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 22 Nov 2024 14:44:25 +0500 Subject: [PATCH 21/24] Fix config description indentation --- .../assets/distanthorizons/lang/en_us.json | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) 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 e8c777f16..178c9d66c 100644 --- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json +++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json @@ -670,54 +670,54 @@ "distanthorizons.config.server.sendLevelKeys": - "Send Level Keys", + "Send Level Keys", "distanthorizons.config.server.sendLevelKeys.@tooltip": - "Makes the server send level keys for each world.\nDisable this if you use alternative ways to send level keys.", + "Makes the server send level keys for each world.\nDisable this if you use alternative ways to send level keys.", "distanthorizons.config.server.levelKeyPrefix": - "Level Key Prefix", + "Level Key Prefix", "distanthorizons.config.server.levelKeyPrefix.@tooltip": - "Prefix of the level keys sent to the clients.\nIf the mod is running behind a proxy, each backend should use a unique value.\nIf this value is empty, level key will be based on the server's seed hash.", + "Prefix of the level keys sent to the clients.\nIf the mod is running behind a proxy, each backend should use a unique value.\nIf this value is empty, level key will be based on the server's seed hash.", "distanthorizons.config.server.generationRequestRateLimit": - "Rate Limit for Generation Requests", + "Rate Limit for Generation Requests", "distanthorizons.config.server.generationRequestRateLimit.@tooltip": - "How many LOD generation requests per second should a client send?\nAlso limits the number of client requests allowed to stay in the server's queue.", + "How many LOD generation requests per second should a client send?\nAlso limits the number of client requests allowed to stay in the server's queue.", "distanthorizons.config.server.maxGenerationRequestDistance": - "Max Generation Request Distance", + "Max Generation Request Distance", "distanthorizons.config.server.maxGenerationRequestDistance.@tooltip": - "Defines the distance allowed to generate around the player.", + "Defines the distance allowed to generate around the player.", "distanthorizons.config.server.enableRealTimeUpdates": - "Enable Real-time Updates", + "Enable Real-time Updates", "distanthorizons.config.server.enableRealTimeUpdates.@tooltip": - "If true, clients will receive real-time LOD updates for chunks outside the client's render distance.", + "If true, clients will receive real-time LOD updates for chunks outside the client's render distance.", "distanthorizons.config.server.realTimeUpdateDistanceRadiusInChunks": - "Real-time Update Radius in Chunks", + "Real-time Update Radius in Chunks", "distanthorizons.config.server.realTimeUpdateDistanceRadiusInChunks.@tooltip": - "Defines the distance the player will receive updates around.", + "Defines the distance the player will receive updates around.", "distanthorizons.config.server.synchronizeOnLoad": - "Synchronize LODs on Load", + "Synchronize LODs on Load", "distanthorizons.config.server.synchronizeOnLoad.@tooltip": - "If true, clients will receive updated LODs when joining or loading new LODs.", + "If true, clients will receive updated LODs when joining or loading new LODs.", "distanthorizons.config.server.syncOnLoadRateLimit": - "Rate Limit for Sync on Load", + "Rate Limit for Sync on Load", "distanthorizons.config.server.syncOnLoadRateLimit.@tooltip": - "How many LOD sync requests per second should a client send?\nAlso limits the number of client's requests allowed to stay in the server's queue.", + "How many LOD sync requests per second should a client send?\nAlso limits the number of client's requests allowed to stay in the server's queue.", "distanthorizons.config.server.maxSyncOnLoadRequestDistance": - "Max Sync on Load Request Distance", + "Max Sync on Load Request Distance", "distanthorizons.config.server.maxSyncOnLoadRequestDistance.@tooltip": - "Defines the distance allowed to be synchronized around the player.\nShould be the same or larger than maxGenerationRequestDistance in most cases.", + "Defines the distance allowed to be synchronized around the player.\nShould be the same or larger than maxGenerationRequestDistance in most cases.", "distanthorizons.config.server.maxDataTransferSpeed": - "Maximum Data Transfer Speed, KB/s", + "Maximum Data Transfer Speed, KB/s", "distanthorizons.config.server.maxDataTransferSpeed.@tooltip": - "Maximum speed for uploading LODs to the clients, in KB/s.\nValue of 0 disables the limit.", + "Maximum speed for uploading LODs to the clients, in KB/s.\nValue of 0 disables the limit.", From 97943107f7342868df7c843a1f47b07b1932e37b Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 22 Nov 2024 14:49:39 +0500 Subject: [PATCH 22/24] Up protocol version --- .../main/java/com/seibel/distanthorizons/coreapi/ModInfo.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java index c3652eed6..a61287f87 100644 --- a/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java +++ b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java @@ -31,7 +31,7 @@ public final class ModInfo public static final String DEDICATED_SERVER_INITIAL_PATH = "dedicated_server_initial"; /** Incremented every time any packets are added, changed or removed, with a few exceptions. */ - public static final int PROTOCOL_VERSION = 6; + public static final int PROTOCOL_VERSION = 7; public static final String WRAPPER_PACKET_PATH = "message"; /** The internal mod name */ From aa5c4aa9c45c6bdb21181fc82223c56feca48ee4 Mon Sep 17 00:00:00 2001 From: s809 <43530948+s809@users.noreply.github.com> Date: Fri, 22 Nov 2024 14:54:37 +0500 Subject: [PATCH 23/24] Lower log level of out of range warning --- .../multiplayer/client/AbstractFullDataNetworkRequestQueue.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 5c1d2b6c0..330f79cb6 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 @@ -254,7 +254,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende } catch (RequestOutOfRangeException e) { - LOGGER.warn("Out of range, re-queueing task [" + DhSectionPos.toString(sectionPos) + "]: " + e.getMessage()); + LOGGER.debug("Out of range, re-queueing task [" + DhSectionPos.toString(sectionPos) + "]: " + e.getMessage()); entry.networkDataSourceFuture = null; return null; From af932c5bc9800a26fb86e0f88fb8fba1580d2988 Mon Sep 17 00:00:00 2001 From: James Seibel Date: Mon, 2 Dec 2024 07:51:07 -0600 Subject: [PATCH 24/24] Add visited position removal timer in RemoteFullDataSourceProvider This is done to hopefully prevent memory leaks --- .../RemoteFullDataSourceProvider.java | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java index cb84c8a3e..f35cff1b5 100644 --- a/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java +++ b/core/src/main/java/com/seibel/distanthorizons/core/file/fullDatafile/RemoteFullDataSourceProvider.java @@ -24,15 +24,20 @@ import com.seibel.distanthorizons.core.file.structure.ISaveStructure; import com.seibel.distanthorizons.core.generation.RemoteWorldRetrievalQueue; import com.seibel.distanthorizons.core.level.IDhClientLevel; import com.seibel.distanthorizons.core.level.WorldGenModule; +import com.seibel.distanthorizons.core.logging.DhLoggerBuilder; import com.seibel.distanthorizons.core.multiplayer.client.SyncOnLoadRequestQueue; import com.seibel.distanthorizons.core.pos.DhSectionPos; import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos2D; +import com.seibel.distanthorizons.core.util.TimerUtil; import com.seibel.distanthorizons.coreapi.util.BitShiftUtil; +import org.apache.logging.log4j.Logger; import org.jetbrains.annotations.Nullable; import java.io.File; import java.util.Map; import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; import java.util.concurrent.ConcurrentHashMap; /** @@ -41,6 +46,11 @@ import java.util.concurrent.ConcurrentHashMap; */ public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvider { + private static final Logger LOGGER = DhLoggerBuilder.getLogger(); + private static final Timer DELAY_UPDATE_TIMER = TimerUtil.CreateTimer("Remote DataSource Visited Pos Removal Timer"); + /** auto remove visited positions from the set after a given amount of time to prevent the set from growing infinitely */ + private static final int VISITED_POSITION_REMOVAL_TIME_IN_MS = 20 * 60 * 1_000; // 20 minutes + @Nullable private final SyncOnLoadRequestQueue syncOnLoadRequestQueue; private final Set visitedPositions = ConcurrentHashMap.newKeySet(); @@ -128,6 +138,7 @@ public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvide { return; } + this.queueVisitedPositionForRemoval(childPos); // check if the server has newer versions of these LODs Long subTimestamp = timestamps.get(childPos); @@ -139,6 +150,23 @@ public class RemoteFullDataSourceProvider extends GeneratedFullDataSourceProvide return super.get(pos); } + /** this is done to prevent infinite set growth */ + private void queueVisitedPositionForRemoval(long pos) + { + TimerTask timerTask = new TimerTask() + { + @Override + public void run() + { + RemoteFullDataSourceProvider.this.visitedPositions.remove(pos); + } + }; + try + { + DELAY_UPDATE_TIMER.schedule(timerTask, VISITED_POSITION_REMOVAL_TIME_IN_MS); + } + catch (IllegalStateException ignore) { /* shouldn't happen, but there have been issues like this in the past */ } + }