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/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java b/api/src/main/java/com/seibel/distanthorizons/coreapi/ModInfo.java
index 74779b072..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,14 +31,14 @@ 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 */
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");
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..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,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.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;
@@ -355,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;
@@ -430,7 +436,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/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())
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 4d02dd495..03e55bdfe 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.
@@ -1514,23 +1512,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)
@@ -1551,6 +1535,8 @@ public class Config
+ "")
.build();
+
+ // Generation
public static ConfigEntry generationRequestRateLimit = new ConfigEntry.Builder()
.setServersideShortName("generationRequestRateLimit")
.setMinDefaultMax(1, 20, 100)
@@ -1560,6 +1546,17 @@ public class Config
+ "")
.build();
+ public static ConfigEntry maxGenerationRequestDistance = new ConfigEntry.Builder()
+ .setServersideShortName("maxGenerationRequestDistance")
+ .setMinDefaultMax(256, 4096, 4096)
+ .comment("" +
+ "Defines the distance allowed to generate around the player." +
+ "")
+ .setPerformance(EConfigEntryPerformance.HIGH)
+ .build();
+
+
+ // Real-time updates
public static ConfigEntry enableRealTimeUpdates = new ConfigEntry.Builder()
.setServersideShortName("enableRealTimeUpdates")
.set(true)
@@ -1568,7 +1565,17 @@ public class Config
+ "")
.build();
+ public static ConfigEntry realTimeUpdateDistanceRadiusInChunks = new ConfigEntry.Builder()
+ .setServersideShortName("realTimeUpdateDistanceRadius")
+ .setMinDefaultMax(32, 256, 4096)
+ .comment("" +
+ "Defines the distance the player will receive updates around." +
+ "")
+ .setPerformance(EConfigEntryPerformance.HIGH)
+ .build();
+
+ // Sync on load
public static ConfigEntry synchronizeOnLoad = new ConfigEntry.Builder()
.setServersideShortName("synchronizeOnLoad")
.set(true)
@@ -1586,6 +1593,18 @@ public class Config
+ "")
.build();
+ public static ConfigEntry maxSyncOnLoadRequestDistance = new ConfigEntry.Builder()
+ .setServersideShortName("maxSyncOnLoadRequestDistance")
+ .setMinDefaultMax(256, 4096, 4096)
+ .comment("" +
+ "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();
+
+
+ // 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/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
{
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..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,11 @@ 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);
+ 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.");
+ }
adjColumnViews[lodDirection.ordinal() - 2] = adjRenderSource.getVerticalDataPointView(xAdj, zAdj);
}
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),
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 c56ea51c7..bef67c9a6 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
@@ -81,6 +81,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 8886d6232..3cab0e086 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,8 @@ import com.seibel.distanthorizons.core.logging.f3.F3Screen;
import com.seibel.distanthorizons.core.multiplayer.server.FullDataSourceRequestHandler;
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;
import com.seibel.distanthorizons.core.network.messages.ILevelRelatedMessage;
@@ -138,16 +139,29 @@ 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;
+
ServerPlayerState.RateLimiterSet rateLimiterSet = serverPlayerState.getRateLimiterSet(this);
LOGGER.info("received message ["+DhSectionPos.toString(message.sectionPos)+"]");
if (message.clientTimestamp == null)
{
+ if (distanceFromPlayer > Config.Server.maxGenerationRequestDistance.get())
+ {
+ message.sendResponse(new RequestOutOfRangeException("Distance too large: " + distanceFromPlayer + " > " + Config.Server.maxGenerationRequestDistance.get()));
+ return;
+ }
this.requestHandler.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.requestHandler.queueLodSyncForRequestMessage(serverPlayerState, message, rateLimiterSet);
}
});
@@ -184,7 +198,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() + "], " +
@@ -257,7 +271,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/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/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)");
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 688657a92..b2ae7d5f7 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.exceptions.SectionRequiresSplittingException;
import com.seibel.distanthorizons.core.network.session.SessionClosedException;
@@ -97,6 +97,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
//==================//
protected abstract int getRequestRateLimit();
+ protected abstract int getMaxRequestDistance();
protected abstract String getQueueName();
@@ -182,7 +183,7 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
Map.Entry mapEntry = this.waitingTasksBySectionPos
.entrySet().stream()
.filter(task -> task.getValue().networkDataSourceFuture == null)
- .min(Comparator.comparingInt(x -> posDistance(targetPos, x.getKey())))
+ .min(Comparator.comparingInt(x -> posDistanceSquared(targetPos, x.getKey())))
.orElse(null);
if (mapEntry == null)
@@ -194,6 +195,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;
@@ -249,16 +257,15 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
{
return entry.future.complete(RequestResult.REQUIRES_SPLITTING);
}
- catch (InvalidLevelException | RequestRejectedException ignored)
- {
- // We're too late / some cases might trigger a bunch of expected rejections
- return entry.future.complete(RequestResult.FAILED);
- }
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(RequestResult.FAILED);
+ }
catch (RateLimitedException e)
{
LOGGER.warn("Rate limited by server, re-queueing task [" + DhSectionPos.toString(sectionPos) + "]: " + e.getMessage());
@@ -269,6 +276,13 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
entry.networkDataSourceFuture = null;
return null;
}
+ catch (RequestOutOfRangeException e)
+ {
+ LOGGER.debug("Out of range, re-queueing task [" + DhSectionPos.toString(sectionPos) + "]: " + e.getMessage());
+
+ entry.networkDataSourceFuture = null;
+ return null;
+ }
catch (Throwable e)
{
entry.retryAttempts--;
@@ -381,7 +395,9 @@ 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(), Objects.requireNonNull(this.level.getTargetPosForGeneration())) <= this.getMaxRequestDistance() * 16 ? Color.gray
+ : Color.darkGray
));
}
}
@@ -392,10 +408,8 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
// helper methods //
//================//
- protected static int posDistance(DhBlockPos2D targetPos, long pos)
- {
- return DhSectionPos.signedDistance(pos, targetPos);
- }
+ protected static int posDistanceSquared(DhBlockPos2D targetPos, long pos)
+ { return (int) DhSectionPos.getCenterBlockPos(pos).distSquared(targetPos); }
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/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/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;
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 187d2cfa9..2d3a2c049 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.exceptions.SectionRequiresSplittingException;
import com.seibel.distanthorizons.core.network.messages.AbstractTrackableMessage;
@@ -37,7 +37,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);
this.add(SectionRequiresSplittingException.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 446ab110b..033bb9ddb 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;
}
public static int signedDistance(long pos, DhBlockPos2D blockPos)
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())
{
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 48242a572..f58eb02c1 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;
});
}
@@ -336,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;
}
@@ -490,7 +487,6 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
{
// shouldn't normally happen, but just in case
this.missingGenerationPos.add(pos);
- break;
}
}
}
@@ -644,23 +640,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);
+ }
}
}
}
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);
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;
}
}
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);
}
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..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,8 @@
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;
import com.seibel.distanthorizons.core.pos.blockPos.DhBlockPos;
@@ -30,7 +32,6 @@ import com.seibel.distanthorizons.coreapi.interfaces.dependencyInjection.IBindab
/** Can be either a Server world or a Client world. */
public interface ILevelWrapper extends IDhApiLevelWrapper, IBindable
{
-
@Override
IDimensionTypeWrapper getDimensionType();
@@ -38,12 +39,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 = BaseEncoding.base32Hex().encode(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;
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..178c9d66c 100644
--- a/core/src/main/resources/assets/distanthorizons/lang/en_us.json
+++ b/core/src/main/resources/assets/distanthorizons/lang/en_us.json
@@ -669,30 +669,51 @@
"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",
"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",
"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",
"distanthorizons.config.server.enableRealTimeUpdates.@tooltip":
"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",
"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",
"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",
"distanthorizons.config.server.maxDataTransferSpeed.@tooltip":
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;
}
}