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; } }