Merge branch 'main' of https://gitlab.com/jeseibel/distant-horizons-core into NSizedMultiplayerTest
This commit is contained in:
@@ -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];
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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. <br><br>
|
||||
@@ -1514,23 +1512,9 @@ public class Config
|
||||
|
||||
}
|
||||
|
||||
public static class Server
|
||||
public static class Server
|
||||
{
|
||||
public static ConfigEntry<Integer> realTimeUpdateDistanceRadiusInChunks = new ConfigEntry.Builder<Integer>()
|
||||
.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<Boolean> sendLevelKeys = new ConfigEntry.Builder<Boolean>()
|
||||
.setServersideShortName("sendLevelKeys")
|
||||
.setAppearance(EConfigEntryAppearance.ONLY_IN_FILE)
|
||||
@@ -1551,6 +1535,8 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
|
||||
// Generation
|
||||
public static ConfigEntry<Integer> generationRequestRateLimit = new ConfigEntry.Builder<Integer>()
|
||||
.setServersideShortName("generationRequestRateLimit")
|
||||
.setMinDefaultMax(1, 20, 100)
|
||||
@@ -1560,6 +1546,17 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Integer> maxGenerationRequestDistance = new ConfigEntry.Builder<Integer>()
|
||||
.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<Boolean> enableRealTimeUpdates = new ConfigEntry.Builder<Boolean>()
|
||||
.setServersideShortName("enableRealTimeUpdates")
|
||||
.set(true)
|
||||
@@ -1568,7 +1565,17 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Integer> realTimeUpdateDistanceRadiusInChunks = new ConfigEntry.Builder<Integer>()
|
||||
.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<Boolean> synchronizeOnLoad = new ConfigEntry.Builder<Boolean>()
|
||||
.setServersideShortName("synchronizeOnLoad")
|
||||
.set(true)
|
||||
@@ -1586,6 +1593,18 @@ public class Config
|
||||
+ "")
|
||||
.build();
|
||||
|
||||
public static ConfigEntry<Integer> maxSyncOnLoadRequestDistance = new ConfigEntry.Builder<Integer>()
|
||||
.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<Integer> maxDataTransferSpeed = new ConfigEntry.Builder<Integer>()
|
||||
.setServersideShortName("maxDataTransferSpeed")
|
||||
.setMinDefaultMax(0, 500, 1000000 /* 1 GB/s */)
|
||||
|
||||
+2
-2
@@ -54,9 +54,9 @@ public class ConfigChangeListener<T> 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);
|
||||
}
|
||||
}
|
||||
|
||||
+28
-36
@@ -33,6 +33,7 @@ public abstract class AbstractConfigType<T, S>
|
||||
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<T, S>
|
||||
|
||||
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<T, S>
|
||||
{
|
||||
|
||||
+5
-1
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
|
||||
+2
@@ -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"; }
|
||||
|
||||
+17
-3
@@ -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())
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)");
|
||||
|
||||
+27
-13
@@ -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<Long, RequestQueueEntry> 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<Long, RequestQueueEntry> 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); }
|
||||
|
||||
|
||||
|
||||
|
||||
+1
-1
@@ -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 ->
|
||||
|
||||
+2
@@ -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"; }
|
||||
|
||||
+20
-7
@@ -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 <T> void registerConfigEntry(ConfigEntry<T> configEntry, BiFunction<T, T, T> 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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
+4
-3
@@ -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); }
|
||||
|
||||
}
|
||||
+2
-1
@@ -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;
|
||||
|
||||
+2
-2
@@ -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);
|
||||
}};
|
||||
|
||||
@@ -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. <br>
|
||||
* Essentially acts like a distance from the block to the nearest edge of the section,
|
||||
* except inside the section it's negative. <br>
|
||||
* 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)
|
||||
|
||||
@@ -345,9 +345,15 @@ public class LodQuadTree extends QuadTree<LodRenderSection> 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<LodRenderSection> implements IDebugRen
|
||||
if (renderSection != null)
|
||||
{
|
||||
// this data source may now exist
|
||||
renderSection.updateFullDataSourceExists();
|
||||
renderSection.updateFullDataSourceExists();
|
||||
|
||||
if (renderSection.canRender())
|
||||
{
|
||||
|
||||
@@ -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<ColumnRenderSource> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Long, FullDataSourceV2D
|
||||
List<Map<String, Object>> 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, FullDataSourceV2D
|
||||
row -> (long) row.get("LastModifiedUnixDateTime"))
|
||||
);
|
||||
}
|
||||
catch (DbConnectionClosedException e)
|
||||
{
|
||||
return new HashMap<>();
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
throw new RuntimeException(e);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+13
-3
@@ -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. <br>
|
||||
* 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();
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
@@ -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":
|
||||
|
||||
@@ -255,7 +255,7 @@ float calculateHeightFogDepth(float worldYPos)
|
||||
else
|
||||
{
|
||||
// shouldn't happen,
|
||||
return 0;
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user