Compare commits

..

2 Commits

Author SHA1 Message Date
Jim C K Flaten b0e7c31964 Comment 2026-01-05 21:32:41 +01:00
Jim C K Flaten 2e906b57c4 Channel name should be 20 chars or shorter for compatibility with old version of Minecraft. 2026-01-03 23:36:46 +01:00
34 changed files with 236 additions and 834 deletions
@@ -32,7 +32,9 @@ public final class ModInfo
/** Incremented every time any packets are added, changed or removed, with a few exceptions. */
public static final int PROTOCOL_VERSION = 13;
public static final String WRAPPER_PACKET_PATH = "message";
/** The full plugin channel name (RESOURCE_NAMESPACE:WRAPPER_PACKET_PATH) must be 20 characters or fewer for compatibility with <1.13. */
public static final String WRAPPER_PACKET_PATH = "msg";
/** The internal mod name */
public static final String NAME = "DistantHorizons";
@@ -23,9 +23,8 @@ import com.seibel.distanthorizons.api.DhApi;
import com.seibel.distanthorizons.api.enums.config.EDhApiMcRenderingFadeMode;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
import com.seibel.distanthorizons.api.methods.events.abstractEvents.*;
import com.seibel.distanthorizons.api.objects.DhApiResult;
import com.seibel.distanthorizons.core.api.internal.rendering.DhRenderState;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.enums.EMinecraftColor;
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.network.messages.MessageRegistry;
@@ -33,15 +32,14 @@ import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.render.DhApiRenderProxy;
import com.seibel.distanthorizons.core.render.renderer.*;
import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.util.objects.Pair;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.coreapi.DependencyInjection.ApiEventInjector;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.network.messages.AbstractNetworkMessage;
import com.seibel.distanthorizons.core.network.session.NetworkSession;
import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiDebugRendering;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRendererMode;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.level.IServerKeyedClientLevel;
@@ -55,23 +53,11 @@ import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapp
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.joml.Matrix4f;
import org.joml.Vector4f;
import org.lwjgl.glfw.GLFW;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL46;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.*;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Function;
/**
* This holds the methods that should be called
@@ -167,10 +153,10 @@ public class ClientApi
if (Config.Common.Logging.Warning.showReplayWarningOnStartup.get())
{
MC_CLIENT.sendChatMessage(MinecraftTextFormat.ORANGE + "Distant Horizons: Replay detected." + MinecraftTextFormat.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage(EMinecraftColor.ORANGE + "Distant Horizons: Replay detected." + EMinecraftColor.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage("DH may behave strangely or have missing functionality.");
MC_CLIENT.sendChatMessage("In order to use pre-generated LODs, put your DH database(s) in:");
MC_CLIENT.sendChatMessage(MinecraftTextFormat.GRAY +".Minecraft" + File.separator + ClientOnlySaveStructure.SERVER_DATA_FOLDER_NAME + File.separator + ClientOnlySaveStructure.REPLAY_SERVER_FOLDER_NAME + File.separator + "DIMENSION_NAME"+ MinecraftTextFormat.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage(EMinecraftColor.GRAY +".Minecraft" + File.separator + ClientOnlySaveStructure.SERVER_DATA_FOLDER_NAME + File.separator + ClientOnlySaveStructure.REPLAY_SERVER_FOLDER_NAME + File.separator + "DIMENSION_NAME"+EMinecraftColor.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage("This can be disabled in DH's config under Advanced -> Logging.");
MC_CLIENT.sendChatMessage("");
}
@@ -343,27 +329,10 @@ public class ClientApi
*/
public void pluginMessageReceived(@NotNull AbstractNetworkMessage message)
{
@Nullable ThreadPoolExecutor executor = ThreadPoolUtil.networkClientHandlerExecutor();
if (executor == null)
NetworkSession networkSession = this.pluginChannelApi.networkSession;
if (networkSession != null)
{
LOGGER.warn("warn");
return;
}
try
{
executor.execute(() ->
{
NetworkSession networkSession = this.pluginChannelApi.networkSession;
if (networkSession != null)
{
networkSession.tryHandleMessage(message);
}
});
}
catch (RejectedExecutionException e)
{
LOGGER.warn("Plugin message executor rejected");
networkSession.tryHandleMessage(message);
}
}
@@ -539,10 +508,10 @@ public class ClientApi
this.rendererDisabledBecauseOfExceptions = true;
LOGGER.error("Unexpected Renderer error in render pass [" + renderPass + "]. Error: " + e.getMessage(), e);
MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "" + MinecraftTextFormat.BOLD + "ERROR: Distant Horizons renderer has encountered an exception!" + MinecraftTextFormat.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "Renderer disabled to try preventing GL state corruption." + MinecraftTextFormat.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "Toggle DH rendering via the config UI to re-activate DH rendering." + MinecraftTextFormat.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage(MinecraftTextFormat.DARK_RED + "Error: " + MinecraftTextFormat.CLEAR_FORMATTING + e);
MC_CLIENT.sendChatMessage(EMinecraftColor.DARK_RED + "" + EMinecraftColor.BOLD + "ERROR: Distant Horizons renderer has encountered an exception!" + EMinecraftColor.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage(EMinecraftColor.DARK_RED + "Renderer disabled to try preventing GL state corruption." + EMinecraftColor.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage(EMinecraftColor.DARK_RED + "Toggle DH rendering via the config UI to re-activate DH rendering." + EMinecraftColor.CLEAR_FORMATTING);
MC_CLIENT.sendChatMessage(EMinecraftColor.DARK_RED + "Error: " + EMinecraftColor.CLEAR_FORMATTING + e);
}
@@ -561,11 +530,8 @@ public class ClientApi
* The first fade pass.
* Called after MC finishes rendering the opaque passes.
*/
public void renderFadeOpaque() // TODO this is actually the transparent pass
public void renderFadeOpaque()
{
DepthCalculator.INSTANCE.getMcTransparentDepthTexture();
DepthCalculator.INSTANCE.tryCalculateAsync();
// only fade when DH is rendering
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT
&&
@@ -586,10 +552,8 @@ public class ClientApi
* Called after MC finishes rendering both opaque
* and transparent passes.
*/
public void renderFadeTransparent() // TODO this is actually the opaque pass
public void renderFadeTransparent()
{
DepthCalculator.INSTANCE.getMcOpaqueDepthTexture();
// only fade when DH is rendering
if (Config.Client.Advanced.Debugging.rendererMode.get() == EDhApiRendererMode.DEFAULT)
{
@@ -618,30 +582,27 @@ public class ClientApi
/** Trigger once on key press, with CLIENT PLAYER. */
public void keyPressedEvent(int glfwKey)
{
//if (!Config.Client.Advanced.Debugging.enableDebugKeybindings.get())
//{
// // keybindings are disabled
// return;
//}
if (!Config.Client.Advanced.Debugging.enableDebugKeybindings.get())
{
// keybindings are disabled
return;
}
if (glfwKey == GLFW.GLFW_KEY_F8)
{
DepthCalculator.INSTANCE.pause = true;
//Config.Client.Advanced.Debugging.debugRendering.set(EDhApiDebugRendering.next(Config.Client.Advanced.Debugging.debugRendering.get()));
//MC_CLIENT.sendChatMessage("F8: Set debug mode to " + Config.Client.Advanced.Debugging.debugRendering.get());
Config.Client.Advanced.Debugging.debugRendering.set(EDhApiDebugRendering.next(Config.Client.Advanced.Debugging.debugRendering.get()));
MC_CLIENT.sendChatMessage("F8: Set debug mode to " + Config.Client.Advanced.Debugging.debugRendering.get());
}
else if (glfwKey == GLFW.GLFW_KEY_F6)
{
DepthCalculator.INSTANCE.pause = true;
//Config.Client.Advanced.Debugging.rendererMode.set(EDhApiRendererMode.next(Config.Client.Advanced.Debugging.rendererMode.get()));
//MC_CLIENT.sendChatMessage("F6: Set rendering to " + Config.Client.Advanced.Debugging.rendererMode.get());
Config.Client.Advanced.Debugging.rendererMode.set(EDhApiRendererMode.next(Config.Client.Advanced.Debugging.rendererMode.get()));
MC_CLIENT.sendChatMessage("F6: Set rendering to " + Config.Client.Advanced.Debugging.rendererMode.get());
}
else if (glfwKey == GLFW.GLFW_KEY_P)
{
DepthCalculator.INSTANCE.pause = true;
//prefLoggerEnabled = !prefLoggerEnabled;
//MC_CLIENT.sendChatMessage("P: Debug Pref Logger is " + (prefLoggerEnabled ? "enabled" : "disabled"));
prefLoggerEnabled = !prefLoggerEnabled;
MC_CLIENT.sendChatMessage("P: Debug Pref Logger is " + (prefLoggerEnabled ? "enabled" : "disabled"));
}
}
@@ -695,7 +656,7 @@ public class ClientApi
// remind the user that this is a development build
String message =
MinecraftTextFormat.DARK_GREEN + "Distant Horizons: nightly/unstable build, version: [" + ModInfo.VERSION+"]." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
EMinecraftColor.DARK_GREEN + "Distant Horizons: nightly/unstable build, version: [" + ModInfo.VERSION+"]." +EMinecraftColor.CLEAR_FORMATTING + "\n" +
"Issues may occur with this version.\n" +
"Here be dragons!\n";
MC_CLIENT.sendChatMessage(message);
@@ -719,7 +680,7 @@ public class ClientApi
{
String message =
// orange text
MinecraftTextFormat.ORANGE + "Distant Horizons: Low memory detected." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
EMinecraftColor.ORANGE + "Distant Horizons: Low memory detected." + EMinecraftColor.CLEAR_FORMATTING + "\n" +
"Stuttering or low FPS may occur. \n" +
"Please increase Minecraft's available memory to 4 GB or more. \n" +
"This warning can be disabled in DH's config under Advanced -> Logging. \n";
@@ -741,12 +702,12 @@ public class ClientApi
this.lastStaticWarningMessageSentMsTime = System.currentTimeMillis();
String message =
MinecraftTextFormat.YELLOW + "Distant Horizons: High vanilla render distance detected." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
EMinecraftColor.YELLOW + "Distant Horizons: High vanilla render distance detected." + EMinecraftColor.CLEAR_FORMATTING + "\n" +
"Using a high vanilla render distance uses a lot of CPU power \n" +
"and doesn't improve graphics much after about 12.\n" +
"Lowering your vanilla render distance will give you better FPS\n" +
"Lowing your vanilla render distance will give you better FPS\n" +
"and reduce stuttering at a similar visual quality.\n" +
MinecraftTextFormat.GRAY + "A vanilla render distance of 8 is recommended." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
EMinecraftColor.GRAY + "A vanilla render distance of 8 is recommended." + EMinecraftColor.CLEAR_FORMATTING + "\n" +
"This message can be disabled in DH's config under Advanced -> Logging.\n";
MC_CLIENT.sendChatMessage(message);
}
@@ -1,11 +1,13 @@
package com.seibel.distanthorizons.core.api.internal.chunkUpdating;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.api.internal.SharedApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.enums.EMinecraftColor;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.multiplayer.client.SyncOnLoadRequestQueue;
import com.seibel.distanthorizons.core.pos.DhChunkPos;
import com.seibel.distanthorizons.core.world.EWorldEnvironment;
import com.seibel.distanthorizons.core.logging.DhLogger;
@@ -118,7 +120,7 @@ public class ChunkUpdateQueueManager
{
lastOverloadedLogMessageMsTime = System.currentTimeMillis();
String message = MinecraftTextFormat.ORANGE + "Distant Horizons overloaded, too many chunks queued for LOD processing. " + MinecraftTextFormat.CLEAR_FORMATTING +
String message = EMinecraftColor.ORANGE + "Distant Horizons overloaded, too many chunks queued for LOD processing. " + EMinecraftColor.CLEAR_FORMATTING +
"\nThis may result in holes in your LODs. " +
"\nFix: move through the world slower, decrease your vanilla render distance, slow down your world pre-generator (IE Chunky), or increase the Distant Horizons' CPU thread counts. " +
"\nMax queue count [" + SharedApi.CHUNK_UPDATE_QUEUE_MANAGER.maxSize + "] ([" + SharedApi.MAX_UPDATING_CHUNK_COUNT_PER_THREAD_AND_PLAYER + "] per thread+players).";
@@ -118,20 +118,6 @@ public class Config
public static ConfigEntry<Boolean> dynamicFadeUseOpaqueMcDepth = new ConfigEntry.Builder<Boolean>()
.set(true)
.comment(""
+ "")
.build();
public static ConfigEntry<String> dynamicFadeExportPath = new ConfigEntry.Builder<String>()
.set("C:/Users/James_Seibel/Desktop/")
.comment(""
+ "")
.build();
public static class Advanced
{
// common config links need to have their destination
@@ -471,15 +457,6 @@ public class Config
+ "")
.build();
public static ConfigEntry<Boolean> expandDistantBeacons = new ConfigEntry.Builder<Boolean>()
.set(true)
.comment(""
+ "If true LOD beacon beams will be rendered wider at extreme distances, \n"
+ "making them easier to see. \n"
+ "If false all LOD beacon beams will only ever be 1 block wide. \n"
+ "")
.build();
public static ConfigEntry<Boolean> enableCloudRendering = new ConfigEntry.Builder<Boolean>()
.set(true)
.comment(""
@@ -295,21 +295,24 @@ public class LodBufferContainer implements AutoCloseable
{
this.buffersUploaded = false;
for (GLVertexBuffer buffer : this.vbos)
GLProxy.queueRunningOnRenderThread(() ->
{
if (buffer != null)
for (GLVertexBuffer buffer : this.vbos)
{
buffer.destroyAsync();
if (buffer != null)
{
buffer.destroyAsync();
}
}
}
for (GLVertexBuffer buffer : this.vbosTransparent)
{
if (buffer != null)
for (GLVertexBuffer buffer : this.vbosTransparent)
{
buffer.destroyAsync();
if (buffer != null)
{
buffer.destroyAsync();
}
}
}
});
}
}
@@ -0,0 +1,47 @@
package com.seibel.distanthorizons.core.enums;
/**
* might be deprecated in the future? in that case we'll probably want a wrapper
* function to handle colors for new MC versions
* <br><br>
* source: https://minecraft.wiki/w/Formatting_codes
*/
public enum EMinecraftColor // TODO EMinecraftTextFormat
{
BLACK("\u00A70"),
DARK_BLUE("\u00A71"),
DARK_GREEN("\u00A72"),
DARK_AQUA("\u00A73"),
DARK_RED("\u00A74"),
DARK_PURPLE("\u00A75"),
ORANGE("\u00A76"),
GRAY("\u00A77"),
DARK_GRAY("\u00A78"),
BLUE("\u00A79"),
GREEN("\u00A7a"),
AQUA("\u00A7b"),
RED("\u00A7c"),
LIGHT_PURPLE("\u00A7d"),
YELLOW("\u00A7e"),
WHITE("\u00A7f"),
OBFUSCATED("\u00A7k"),
BOLD("\u00A7l"),
STRIKETHROUGH("\u00A7m"),
UNDERLINE("\u00A7n"),
ITALIC("\u00A7o"),
CLEAR_FORMATTING("\u00A7r");
public final String colorCode;
EMinecraftColor(String colorCode)
{
this.colorCode = colorCode;
}
@Override
public String toString() { return this.colorCode; }
}
@@ -1,35 +0,0 @@
package com.seibel.distanthorizons.core.enums;
/**
* might be deprecated in the future? in that case we'll probably want a wrapper
* function to handle colors for new MC versions
* <br><br>
* source: https://minecraft.wiki/w/Formatting_codes
*/
public class MinecraftTextFormat
{
public static final String BLACK = "\u00A70";
public static final String DARK_BLUE = "\u00A71";
public static final String DARK_GREEN = "\u00A72";
public static final String DARK_AQUA = "\u00A73";
public static final String DARK_RED = "\u00A74";
public static final String DARK_PURPLE = "\u00A75";
public static final String ORANGE = "\u00A76";
public static final String GRAY = "\u00A77";
public static final String DARK_GRAY = "\u00A78";
public static final String BLUE = "\u00A79";
public static final String GREEN = "\u00A7a";
public static final String AQUA = "\u00A7b";
public static final String RED = "\u00A7c";
public static final String LIGHT_PURPLE = "\u00A7d";
public static final String YELLOW = "\u00A7e";
public static final String WHITE = "\u00A7f";
public static final String OBFUSCATED = "\u00A7k";
public static final String BOLD = "\u00A7l";
public static final String STRIKETHROUGH = "\u00A7m";
public static final String UNDERLINE = "\u00A7n";
public static final String ITALIC = "\u00A7o";
public static final String CLEAR_FORMATTING = "\u00A7r";
}
@@ -104,7 +104,7 @@ public class JarUtils
*/
public static InputStream accessFile(String resource)
{
final ClassLoader loader = JarUtils.class.getClassLoader();
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
// this is the path within the jar file
InputStream input = loader.getResourceAsStream(resource);
if (input == null)
@@ -24,7 +24,7 @@ import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.listeners.IConfigListener;
import com.seibel.distanthorizons.core.config.types.ConfigEntry;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.enums.EMinecraftColor;
import com.seibel.distanthorizons.core.util.ThreadUtil;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.coreapi.ModInfo;
@@ -303,32 +303,32 @@ public class DhLogger implements IConfigListener
String prefix = "[" + ModInfo.READABLE_NAME + "] ";
if (logLevel == Level.ERROR)
{
prefix += MinecraftTextFormat.DARK_RED;
prefix += EMinecraftColor.DARK_RED;
}
else if (logLevel == Level.WARN)
{
prefix += MinecraftTextFormat.ORANGE;
prefix += EMinecraftColor.ORANGE;
}
else if (logLevel == Level.INFO)
{
prefix += MinecraftTextFormat.AQUA;
prefix += EMinecraftColor.AQUA;
}
else if (logLevel == Level.DEBUG)
{
prefix += MinecraftTextFormat.GREEN;
prefix += EMinecraftColor.GREEN;
}
else if (logLevel == Level.TRACE)
{
prefix += MinecraftTextFormat.DARK_GRAY;
prefix += EMinecraftColor.DARK_GRAY;
}
else
{
prefix += MinecraftTextFormat.WHITE;
prefix += EMinecraftColor.WHITE;
}
prefix += MinecraftTextFormat.BOLD + "" + MinecraftTextFormat.WHITE;
prefix += EMinecraftColor.BOLD + "" + EMinecraftColor.WHITE;
prefix += logLevel.name();
prefix += MinecraftTextFormat.CLEAR_FORMATTING + " ";
prefix += EMinecraftColor.CLEAR_FORMATTING + " ";
mc_client.sendChatMessage(prefix + message);
}
@@ -24,7 +24,6 @@ import com.seibel.distanthorizons.core.render.renderer.IDebugRenderable;
import com.seibel.distanthorizons.core.sql.dto.FullDataSourceV2DTO;
import com.seibel.distanthorizons.core.util.LodUtil;
import com.seibel.distanthorizons.core.util.ratelimiting.SupplierBasedRateLimiter;
import com.seibel.distanthorizons.core.util.threading.ThreadPoolUtil;
import com.seibel.distanthorizons.core.world.DhApiWorldProxy;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
@@ -218,18 +217,11 @@ public abstract class AbstractFullDataNetworkRequestQueue implements IDebugRende
FullDataSourceResponseMessage.class
);
requestTask.networkDataSourceFuture = dataSourceNetworkFuture;
Executor networkCompressionExecutor = ThreadPoolUtil.getNetworkCompressionExecutor();
if (networkCompressionExecutor == null)
{
return;
}
dataSourceNetworkFuture.handleAsync((FullDataSourceResponseMessage response, Throwable throwable) ->
dataSourceNetworkFuture.handle((FullDataSourceResponseMessage response, Throwable throwable) ->
{
this.handleNetResponse(requestTask, response, throwable);
return null;
}, networkCompressionExecutor);
});
}
private void handleNetResponse(NetRequestTask requestTask, FullDataSourceResponseMessage response, Throwable throwable)
{
@@ -1,10 +1,8 @@
package com.seibel.distanthorizons.core.multiplayer.client;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.config.listeners.ConfigChangeListener;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.multiplayer.config.SessionConfig;
@@ -97,15 +95,6 @@ public class ClientNetworkState implements Closeable
|| Math.abs(event.protocolVersion - ModInfo.PROTOCOL_VERSION) < this.closestProtocolVersion)
{
this.closestProtocolVersion = event.protocolVersion;
if (ModInfo.PROTOCOL_VERSION < event.protocolVersion)
{
ClientApi.INSTANCE.showChatMessageNextFrame(MinecraftTextFormat.ORANGE + "Distant Horizons: Your mod is outdated. Update to receive LODs on this server.");
}
else
{
ClientApi.INSTANCE.showChatMessageNextFrame(MinecraftTextFormat.ORANGE + "Distant Horizons: The server's mod is outdated. Ask the server's owner to update.");
}
}
});
@@ -45,7 +45,6 @@ public class FullDataPayloadReceiver implements AutoCloseable
return null;
}
message.buffer.readerIndex(0);
composite.addComponent(message.buffer);
composite.writerIndex(composite.writerIndex() + message.buffer.writerIndex());
LOGGER.debug("Updated full data buffer [" + message.bufferId + "]: [" + composite + "].");
@@ -2,7 +2,7 @@ package com.seibel.distanthorizons.core.pooling;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.enums.EMinecraftColor;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.logging.f3.F3Screen;
@@ -13,6 +13,7 @@ import com.seibel.distanthorizons.coreapi.util.StringUtil;
import it.unimi.dsi.fastutil.bytes.ByteArrayList;
import it.unimi.dsi.fastutil.longs.LongArrayList;
import it.unimi.dsi.fastutil.shorts.ShortArrayList;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
@@ -178,7 +179,7 @@ public class PhantomArrayListPool
{
lowMemoryWarningLogged = true;
String message = MinecraftTextFormat.ORANGE + "Distant Horizons: Insufficient memory detected." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
String message = EMinecraftColor.ORANGE + "Distant Horizons: Insufficient memory detected." + EMinecraftColor.CLEAR_FORMATTING + "\n" +
"This may cause stuttering or crashing. \n" +
"Potential causes: \n" +
"1. your allocated memory isn't high enough \n" +
@@ -78,11 +78,6 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
*/
private final ConcurrentLinkedQueue<Long> sectionsToReload = new ConcurrentLinkedQueue<>();
private final IDhClientLevel level;
/**
* Note: this doesn't lock all operations as some other threads/operations
* that may traverse the tree while it's being modified.
* IE {@link RenderBufferHandler} will walk through the tree each frame.
*/
private final ReentrantLock treeLock = new ReentrantLock();
private ArrayList<LodRenderSection> debugRenderSections = new ArrayList<>();
@@ -171,8 +166,7 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
// don't tick the tree if a modification is still going
// TODO is this lock necessary for anything beyond this tick method?
// don't traverse the tree if it is being modified
if (this.treeLock.tryLock())
{
// this shouldn't be updated while the tree is being iterated through
@@ -514,23 +508,28 @@ public class LodQuadTree extends QuadTree<LodRenderSection> implements IDebugRen
continue;
}
// the section only needs to be updated if a buffer is currently present
LodRenderSection renderSection = this.tryGetValue(pos);
if (renderSection != null)
try
{
if (renderSection.canRender())
// the section only needs to be updated if a buffer is currently present
LodRenderSection renderSection = this.getValue(pos);
if (renderSection != null)
{
if (renderSection.gpuUploadInProgress()
|| !renderSection.uploadRenderDataToGpuAsync())
if (renderSection.canRender())
{
// if a section is already loading or failed to start upload
// we need to wait to trigger it again
// if we don't trigger it again the LOD will be out of date
// and may be invisible/missing
positionsToRequeue.add(pos);
if (renderSection.gpuUploadInProgress()
|| !renderSection.uploadRenderDataToGpuAsync())
{
// if a section is already loading or failed to start upload
// we need to wait to trigger it again
// if we don't trigger it again the LOD will be out of date
// and may be invisible/missing
positionsToRequeue.add(pos);
}
}
}
}
catch (IndexOutOfBoundsException e)
{ /* the section is now out of bounds, it doesn't need to be reloaded */ }
}
this.sectionsToReload.addAll(positionsToRequeue);
}
@@ -77,7 +77,7 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
* contains the list of beacons currently being rendered in this section
* if this list is modified the {@link LodRenderSection#beaconRenderHandler} should be updated to match.
*/
private final ArrayList<BeaconBeamDTO> activeBeaconList = new ArrayList<>();
private final List<BeaconBeamDTO> activeBeaconList = new ArrayList<>();
@Nullable
public final BeaconRenderHandler beaconRenderHandler;
@Nullable
@@ -446,15 +446,20 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
// stop rendering current beacons
this.beaconRenderHandler.stopRenderingBeacons(this.activeBeaconList);
for (BeaconBeamDTO beam : this.activeBeaconList)
{
this.beaconRenderHandler.stopRenderingBeaconAtPos(beam.blockPos);
}
// swap old and new active beacon list
this.activeBeaconList.clear();
this.activeBeaconList.addAll(activeBeacons);
// start rendering new beacon list
byte absoluteDetailLevel = (byte)(DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
this.beaconRenderHandler.startRenderingBeacons(this.activeBeaconList, absoluteDetailLevel);
for (BeaconBeamDTO beam : this.activeBeaconList)
{
this.beaconRenderHandler.startRenderingBeacon(beam);
}
}
}
@@ -469,7 +474,10 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
synchronized (this.activeBeaconList)
{
this.beaconRenderHandler.stopRenderingBeacons(this.activeBeaconList);
for (BeaconBeamDTO beam : this.activeBeaconList)
{
this.beaconRenderHandler.stopRenderingBeaconAtPos(beam.blockPos);
}
}
}
@@ -484,8 +492,10 @@ public class LodRenderSection implements IDebugRenderable, AutoCloseable
synchronized (this.activeBeaconList)
{
byte absoluteDetailLevel = (byte)(DhSectionPos.getDetailLevel(this.pos) - DhSectionPos.SECTION_MINIMUM_DETAIL_LEVEL);
this.beaconRenderHandler.startRenderingBeacons(this.activeBeaconList, absoluteDetailLevel);
for (BeaconBeamDTO beam : this.activeBeaconList)
{
this.beaconRenderHandler.startRenderingBeacon(beam);
}
}
}
@@ -1,444 +0,0 @@
package com.seibel.distanthorizons.core.render.renderer;
import com.seibel.distanthorizons.api.enums.rendering.EDhApiRenderPass;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.logging.DhLogger;
import com.seibel.distanthorizons.core.logging.DhLoggerBuilder;
import com.seibel.distanthorizons.core.util.math.Mat4f;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftGLWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import org.joml.Matrix4f;
import org.joml.Vector4f;
import org.lwjgl.opengl.GL32;
import org.lwjgl.opengl.GL46;
import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.text.NumberFormat;
import java.util.Arrays;
import java.util.function.Function;
public class DepthCalculator
{
public static DepthCalculator INSTANCE = new DepthCalculator();
private static final DhLogger LOGGER = new DhLoggerBuilder().build();
private static final IMinecraftClientWrapper MC_CLIENT = SingletonInjector.INSTANCE.get(IMinecraftClientWrapper.class);
private static final IMinecraftRenderWrapper MC_RENDER = SingletonInjector.INSTANCE.get(IMinecraftRenderWrapper.class);
private static final IMinecraftGLWrapper GLMC = SingletonInjector.INSTANCE.get(IMinecraftGLWrapper.class);
private float[] mcOpaqueDepthTextureValues = new float[1];
private float[] mcTransparentDepthTextureValues = new float[1];
private float[] dhDepthTextureValues = new float[1];
private float[] outDepthTextureValues = new float[1];
private float lastClosestDhDepth = 1.0f;
public float actualMcBlockDistance = 8 * 16; // needs to be non-zero to start DH rendering
@Deprecated // Replace with thread pool and an AtomicBool for running state
public Thread thread = null;
public boolean pause = false;
private boolean gotDhDepthThisFrame = false;
private boolean gotMcDepthThisFrame = false;
//=============//
// constructor //
//=============//
private DepthCalculator() { }
//======//
// test //
//======//
public void getMcOpaqueDepthTexture() { this.gotMcDepthThisFrame = this.trySetDepthTexture(MC_RENDER.getDepthTextureId(), this.mcOpaqueDepthTextureValues); }
public void getMcTransparentDepthTexture() { this.gotMcDepthThisFrame = this.trySetDepthTexture(MC_RENDER.getDepthTextureId(), this.mcTransparentDepthTextureValues); }
public void trySetDhDepthTexture() { this.gotDhDepthThisFrame = this.trySetDepthTexture(LodRenderer.INSTANCE.getActiveDepthTextureId(), this.dhDepthTextureValues); }
private boolean trySetDepthTexture(int id, float[] outputRef)
{
// don't change the texture if a process is already running
if (this.thread != null)
{
return false;
}
this.resizeTexturesIfNeeded();
if (id == -1)
{
return false;
}
// FIXME this is slow and causes frame stuttering
GL46.glGetTextureImage(id, 0, GL32.GL_DEPTH_COMPONENT, GL32.GL_FLOAT, outputRef);
return true;
}
private void resizeTexturesIfNeeded()
{
int width = MC_RENDER.getTargetFramebufferViewportWidth();
int height = MC_RENDER.getTargetFramebufferViewportHeight();
int elementCount = width * height;
if (this.dhDepthTextureValues.length != elementCount)
{
this.mcOpaqueDepthTextureValues = new float[width * height];
this.mcTransparentDepthTextureValues = new float[width * height];
this.dhDepthTextureValues = new float[width * height];
this.outDepthTextureValues = new float[width * height];
}
}
public void tryCalculateAsync()
{
if (this.thread != null)
{
return;
}
//if (!this.gotDhDepthThisFrame
// || !this.gotMcDepthThisFrame)
//{
// return;
//}
if (this.pause)
{
int k = 0;
}
this.thread = new Thread(() ->
{
try
{
this.calculateDepth();
}
catch (Exception e)
{
LOGGER.error("async test: " + e.getMessage(), e);
}
finally
{
this.thread = null;
this.pause = false;
}
});
this.thread.start();
}
public void calculateDepth()
{
int width = MC_RENDER.getTargetFramebufferViewportWidth();
int height = MC_RENDER.getTargetFramebufferViewportHeight();
int elementCount = width * height;
// used to calculate the DH render matrices
RenderParams renderParams =
new RenderParams(
EDhApiRenderPass.OPAQUE,
ClientApi.RENDER_STATE.frameTime,
ClientApi.RENDER_STATE.mcProjectionMatrix, ClientApi.RENDER_STATE.mcModelViewMatrix,
ClientApi.RENDER_STATE.clientLevelWrapper
);
Mat4f dhInvProj = new Mat4f(renderParams.dhProjectionMatrix);
dhInvProj.invert();
Matrix4f dhInvProjJoml = dhInvProj.createJomlMatrix();
Mat4f dhInvMvm = new Mat4f(renderParams.dhModelViewMatrix);
dhInvMvm.invert();
Matrix4f dhInvMvmJoml = dhInvMvm.createJomlMatrix(); // TODO can we use JOML for MC 1.16?
float[] sampledDistances = new float[9];
// find the closest depth value MC hasn't drawn to
float closestDhDepth = 1.0f; float closestDhDistance = Float.MAX_VALUE;
int closeUIndex = 0; int closeVIndex = 0;
for (int u = 0; u < width; u++) // x
{
for (int v = 0; v < height; v++) // y
{
int invertedV = height - 1 - v;
int i = (invertedV * width) + u;
this.outDepthTextureValues[i] = 0.0f;
float mcDepth = Config.Client.dynamicFadeUseOpaqueMcDepth.get()
? this.mcOpaqueDepthTextureValues[i]
: this.mcTransparentDepthTextureValues[i];
if (mcDepth < 1.0f) // ignore positions MC has drawn to
{
continue;
}
float dhDepth = this.dhDepthTextureValues[i];
if (dhDepth == 1.0f) // ignore positions DH has NOT drawn to
{
continue;
}
this.outDepthTextureValues[i] = dhDepth;
// calculate this point's distance from the camera
float ndcU = u / (float)width;
float ndcV = invertedV / (float)height;
float dist = convertDepthToBlockDistance(ndcU, ndcV, dhInvProjJoml, dhInvMvmJoml, dhDepth);
//// sample the 9 surrounding pixels to account for off-by-one errors between the MC and DH depth textures
//int sampleIndex = 0;
//for (int relU = -1; relU < 1; relU++)
//{
// for (int relV = -1; relV < 1; relV++)
// {
// invertedV = height - 1 - v + relV;
// i = (invertedV * width) + u + relU;
// dhDepth = this.dhDepthTextureValues[i];
//
// if (v + relV < 0 || v + relV > height
// || u + relU < 0 || u + relU > width)
// {
// sampledDistances[sampleIndex] = 0.0f;
// sampleIndex++;
// }
//
// // calculate this point's distance from the camera
// float ndcU = u / (float)width;
// float ndcV = invertedV / (float)height;
// float dist = convertDepthToBlockDistance(ndcU, ndcV, dhInvProjJoml, dhInvMvmJoml, dhDepth);
//
// sampledDistances[sampleIndex] = dist;
// sampleIndex++;
// }
//}
//
//
//Arrays.sort(sampledDistances);
//// return the median element
//float dist = sampledDistances[sampledDistances.length / 2];
//if (dist == 0.0f)
//{
// // the median was 0, return the smallest non-zero element
// for (sampleIndex = 0; sampleIndex < sampledDistances.length; sampleIndex++)
// {
// if (sampledDistances[sampleIndex] != 0.0f)
// {
// dist = sampledDistances[sampleIndex];
// break;
// }
// }
//}
if (dist < closestDhDistance)
{
closestDhDepth = dhDepth;
closestDhDistance = dist;
closeUIndex = u;
closeVIndex = invertedV;
}
}
}
if (this.lastClosestDhDepth != closestDhDepth)
{
NumberFormat numForm = NumberFormat.getNumberInstance();
ClientApi.INSTANCE.showChatMessageNextFrame(
"closest: ["+numForm.format(closestDhDepth)+"]-b["+numForm.format(closestDhDistance)+"]c["+numForm.format(closestDhDistance/16)+"] ("+(closeUIndex)+","+(closeVIndex)+")" +
"");
this.actualMcBlockDistance = closestDhDistance;
}
this.lastClosestDhDepth = closestDhDepth;
if (this.pause)
{
// find the range of depth values used by both textures for clearer exporting
float closestMcDepth = 1.0f; float furthestMcDepth = 0.0f;
float minDhDepth = 1.0f; float maxDhDepth = 0.0f;
for (int i = 0; i < elementCount; i++)
{
float mcDepth = this.mcOpaqueDepthTextureValues[i];
if (mcDepth != 0.0f && mcDepth != 1.0f)
{
if (mcDepth < closestMcDepth)
{
closestMcDepth = mcDepth;
}
if (mcDepth > furthestMcDepth)
{
furthestMcDepth = mcDepth;
}
}
float dhDepth = this.dhDepthTextureValues[i];
if (dhDepth != 0.0f && dhDepth != 1.0f)
{
if (dhDepth < minDhDepth)
{
minDhDepth = dhDepth;
}
if (dhDepth > maxDhDepth)
{
maxDhDepth = dhDepth;
}
}
}
String exportPath = Config.Client.dynamicFadeExportPath.get();
ClientApi.INSTANCE.showChatMessageNextFrame("exporting debug textures to: ["+exportPath+"]...");
// mc opaque
createImg(
this.mcOpaqueDepthTextureValues,
closeUIndex, closeVIndex,
(Float depth) -> { return null; },
width, height, closestMcDepth, furthestMcDepth,
exportPath+"mc-opaque_depth.png");
// mc transparent
createImg(
this.mcTransparentDepthTextureValues,
closeUIndex, closeVIndex,
(Float depth) -> { return null; },
width, height, closestMcDepth, furthestMcDepth,
exportPath+"mc-tran_depth.png");
// dh
Function<Float, Color> customColorFunc = (Float depth) ->
{
if (depth == this.lastClosestDhDepth)
{
return Color.RED;
}
//else if (depth <= (lastClosestDhDepth + 0.01f))
//{
// return Color.ORANGE;
//}
return null;
};
createImg(
this.dhDepthTextureValues,
closeUIndex, closeVIndex,
customColorFunc,
width, height, minDhDepth, maxDhDepth,
exportPath+"dh_depth.png");
// temp
createImg(
this.outDepthTextureValues,
closeUIndex, closeVIndex,
(Float depth) -> { return null; },
width, height, minDhDepth, maxDhDepth,
exportPath+"temp_depth.png");
int breakPoint = 0;
}
}
/** NDC (Normalized Device Coordinates) must be between 0.0 and 1.0 (inclusive) */
private static float convertDepthToBlockDistance(float ndcU, float ndcV, Matrix4f invProj, Matrix4f invMvm, float depth)
{
// This assumes depth is scaled to [0, 1]
// Transform depth to clip space Z value
float z = depth * 2.0f - 1.0f;
// Create a vector in clip space
Vector4f clipSpacePosition = new Vector4f(ndcU, ndcV, z, 1.0f);
// Transform to world space
Vector4f worldSpacePosition = clipSpacePosition.mul(invProj);
worldSpacePosition.div(worldSpacePosition.w); // Perform perspective divide
// Finally apply the inverse model-view matrix to get world space coordinates
worldSpacePosition = worldSpacePosition.mul(invMvm);
// calculate distance from the camera
float distance = worldSpacePosition.distance(0, 0, 0, 0);
return distance;
}
private static void createImg(
float[] tex,
int nearestU, int nearestV,
Function<Float, Color> customColorFunc,
int width, int height,
float minDepth, float maxDepth,
String filePath)
{
// Create a BufferedImage
BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
for (int u = 0; u < width; u++)
{
for (int v = 0; v < height; v++)
{
int invertedY = height - 1 - v;
// Normalize the depth value to a grayscale pixel
float depthValue = tex[(invertedY * width) + u];
Color color = customColorFunc.apply(depthValue);
if (color == null)
{
float normalizedDepth = (depthValue - minDepth) / (maxDepth - minDepth); // Normalize to 0.0 to 1.0
normalizedDepth = Math.max(0.0f, Math.min(1.0f, normalizedDepth)); // Clamp to valid range
int gray = (int) (normalizedDepth * 255); // Map to 0-255
gray = Math.max(0, Math.min(255, gray)); // Clamp to valid range
color = new Color(gray, gray, gray);
}
if (depthValue == 1.0f)
{
color = new Color(0,0,0,0);
}
if (u == nearestU)
{
color = Color.MAGENTA;
}
else if (invertedY == nearestV)
{
color = Color.ORANGE;
}
image.setRGB(u, v, color.getRGB());
}
}
// Write the image to a file
try
{
ImageIO.write(image, "png", new File(filePath));
}
catch (IOException e)
{
LOGGER.error("Unable to write texture to file, error: ["+e.getMessage()+"].", e);
}
}
}
@@ -204,17 +204,17 @@ public class DhTerrainShaderProgram extends ShaderProgram implements IDhApiShade
this.setUniform(this.uIsWhiteWorld, Config.Client.Advanced.Debugging.enableWhiteWorld.get());
// Clip Uniform
float dhNearClipDistance = 0.1f;//RenderUtil.getNearClipPlaneInBlocksForFading(renderParameters.partialTicks);
//if (!Config.Client.Advanced.Debugging.lodOnlyMode.get())
//{
// // this added value prevents the near clip plane and discard circle from touching, which looks bad
// dhNearClipDistance += 16f;
//}
float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocksForFading(renderParameters.partialTicks);
if (!Config.Client.Advanced.Debugging.lodOnlyMode.get())
{
// this added value prevents the near clip plane and discard circle from touching, which looks bad
dhNearClipDistance += 16f;
}
// if the player is very high up and the near clip plane has been modified, disable the distance clipping
// we're high enough that nothing will render on top of the player and this can cause issues otherwise
if (RenderUtil.getHeightBasedNearClipOverride() != -1)
{
dhNearClipDistance = 1.0f; // TODO does this actually disable anything?
dhNearClipDistance = 1.0f;
}
this.setUniform(this.uClipDistance, dhNearClipDistance);
}
@@ -222,8 +222,6 @@ public class LodRenderer
profiler.popPush("LOD Opaque");
this.renderLodPass(lodShaderProgram, renderBufferHandler, renderParams, /*opaquePass*/ true);
DepthCalculator.INSTANCE.trySetDhDepthTexture();
// custom objects with SSAO
if (Config.Client.Advanced.Graphics.GenericRendering.enableGenericRendering.get())
{
@@ -436,7 +434,7 @@ public class LodRenderer
// resize the textures if needed
if (MC_RENDER.getTargetFramebufferViewportWidth() != this.textureWidth
|| MC_RENDER.getTargetFramebufferViewportHeight() != this.textureHeight)
|| MC_RENDER.getTargetFramebufferViewportHeight() != this.textureHeight)
{
// just resizing the textures doesn't work when Optifine is present,
// so recreate the textures with the new size instead
@@ -538,7 +536,7 @@ public class LodRenderer
return true;
}
@SuppressWarnings( "deprecation" ) // done to ignore DhApiColorDepthTextureCreatedEvent
@SuppressWarnings( "deprecation" )
private void createAndBindTextures()
{
int oldWidth = this.textureWidth;
@@ -28,7 +28,6 @@ import com.seibel.distanthorizons.api.objects.render.DhApiRenderableBoxGroupShad
import com.seibel.distanthorizons.core.config.Config;
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.pos.blockPos.DhBlockPos;
import com.seibel.distanthorizons.core.sql.dto.BeaconBeamDTO;
import com.seibel.distanthorizons.core.util.LodUtil;
@@ -40,7 +39,8 @@ import com.seibel.distanthorizons.coreapi.ModInfo;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.NotNull;
import java.util.*;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.locks.ReentrantLock;
@@ -54,8 +54,6 @@ public class BeaconRenderHandler
/** how often should we check if a beacon should be culled? */
private static final int MAX_CULLING_FREQUENCY_IN_MS = 1_000;
private static final Comparator<BeaconBeamDTO> NEGATIVE_BLOCKPOS_COMPARATOR = new NegativeInfiniteBlockPosComparator();
private final ReentrantLock updateLock = new ReentrantLock();
@@ -91,74 +89,20 @@ public class BeaconRenderHandler
// render handling //
//=================//
public void startRenderingBeacons(ArrayList<BeaconBeamDTO> beaconList, byte detailLevel)
public void startRenderingBeacon(BeaconBeamDTO beacon)
{
try
{
this.updateLock.lock();
// how wide should each beacon be?
int beaconBlockWidth = 1;
if (Config.Client.Advanced.Graphics.GenericRendering.expandDistantBeacons.get())
if (this.beaconBlockPosSet.add(beacon.blockPos))
{
beaconBlockWidth = DhSectionPos.getBlockWidth(detailLevel);
}
ArrayList<BeaconBeamDTO> sortedBeaconList = new ArrayList<>(beaconList);
// merge distant beams if requested
if (Config.Client.Advanced.Graphics.GenericRendering.expandDistantBeacons.get())
{
// sort beacons from neg inf -> pos inf
// so we can consistently merge adjacent beacons
sortedBeaconList.sort(NEGATIVE_BLOCKPOS_COMPARATOR);
// go through each beacon...
for (int outerIndex = 0; outerIndex < sortedBeaconList.size(); outerIndex++)
{
BeaconBeamDTO outerBeacon = sortedBeaconList.get(outerIndex);
DhBlockPos outerBlockPos = outerBeacon.blockPos;
// ...and remove any beacons that are within the block width to prevent overlaps
for (int mergeIndex = outerIndex + 1; mergeIndex < sortedBeaconList.size(); mergeIndex++)
{
BeaconBeamDTO beaconToMerge = sortedBeaconList.get(mergeIndex);
DhBlockPos mergeBlockPos = beaconToMerge.blockPos;
int xDiff = mergeBlockPos.getX() - outerBlockPos.getX();
int zDiff = mergeBlockPos.getZ() - outerBlockPos.getZ();
// merge (remove) this beacon if
// it's close to the outer beacon
if (xDiff < beaconBlockWidth
&& zDiff < beaconBlockWidth)
{
sortedBeaconList.remove(mergeIndex);
mergeIndex--; // minus 1 so we don't go past the end of the array when incrementing in the for loop up top
}
}
}
}
// add each beacon to the renderer
for (int i = 0; i < sortedBeaconList.size(); i++)
{
BeaconBeamDTO beacon = sortedBeaconList.get(i);
if (!this.beaconBlockPosSet.add(beacon.blockPos))
{
continue;
}
int maxBeaconBeamHeight = Config.Client.Advanced.Graphics.GenericRendering.beaconRenderHeight.get();
DhApiRenderableBox beaconBox = new DhApiRenderableBox(
new DhApiVec3d(beacon.blockPos.getX(), beacon.blockPos.getY() + 1, beacon.blockPos.getZ()),
new DhApiVec3d(beacon.blockPos.getX() + beaconBlockWidth, maxBeaconBeamHeight, beacon.blockPos.getZ() + beaconBlockWidth),
beacon.color,
EDhApiBlockMaterial.ILLUMINATED
new DhApiVec3d(beacon.blockPos.getX(), beacon.blockPos.getY() + 1, beacon.blockPos.getZ()),
new DhApiVec3d(beacon.blockPos.getX() + 1, maxBeaconBeamHeight, beacon.blockPos.getZ() + 1),
beacon.color,
EDhApiBlockMaterial.ILLUMINATED
);
this.beaconBoxGroup.add(beaconBox);
@@ -172,26 +116,19 @@ public class BeaconRenderHandler
}
}
public void stopRenderingBeacons(ArrayList<BeaconBeamDTO> beaconList)
public void stopRenderingBeaconAtPos(DhBlockPos beaconPos)
{
try
{
this.updateLock.lock();
for (int i = 0; i < beaconList.size(); i++)
if (this.beaconBlockPosSet.remove(beaconPos))
{
BeaconBeamDTO beacon = beaconList.get(i);
DhBlockPos beaconPos = beacon.blockPos;
if (!this.beaconBlockPosSet.remove(beaconPos))
{
continue;
}
Predicate<DhApiRenderableBox> removePredicate = (DhApiRenderableBox box) ->
{
return box.minPos.x == beaconPos.getX()
&& box.minPos.y == beaconPos.getY() + 1 // plus 1 because the beam starts above the beacon
&& box.minPos.z == beaconPos.getZ();
&& box.minPos.y == beaconPos.getY() + 1 // plus 1 because the beam starts above the beacon
&& box.minPos.z == beaconPos.getZ();
};
this.beaconBoxGroup.removeIf(removePredicate);
this.fullBeaconBoxList.removeIf(removePredicate);
@@ -318,28 +255,4 @@ public class BeaconRenderHandler
}
//================//
// helper classes //
//================//
private static class NegativeInfiniteBlockPosComparator implements Comparator<BeaconBeamDTO>
{
@Override
public int compare(BeaconBeamDTO beacon1, BeaconBeamDTO beacon2)
{
DhBlockPos blockPos1 = beacon1.blockPos;
DhBlockPos blockPos2 = beacon2.blockPos;
// sort by X, then by Z
if (blockPos1.getX() != blockPos2.getX())
{
return Integer.compare(blockPos1.getX(), blockPos2.getX());
}
return Integer.compare(blockPos1.getZ(), blockPos2.getZ());
}
}
}
@@ -493,7 +493,7 @@ public class CloudRenderHandler
private static boolean[][] getCloudsFromTexture() throws FileNotFoundException, IOException
{
final ClassLoader loader = CloudRenderHandler.class.getClassLoader();
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
boolean[][] whitePixels = null;
try(InputStream imageInputStream = loader.getResourceAsStream(CLOUD_RESOURCE_TEXTURE_PATH))
@@ -19,7 +19,6 @@
package com.seibel.distanthorizons.core.render.renderer.shaders;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.glObject.shader.ShaderProgram;
@@ -115,7 +114,7 @@ public class VanillaFadeShader extends AbstractShaderRenderer
float dhNearClipDistance = RenderUtil.getNearClipPlaneInBlocksForFading(partialTicks);
// this added value prevents the near clip plane and discard circle from touching, which looks bad
//dhNearClipDistance += 16f;
dhNearClipDistance += 16f;
// measured in blocks
// these multipliers in James' tests should provide a fairly smooth transition
@@ -173,7 +173,7 @@ public class DatabaseUpdater
/** @throws NullPointerException if any of the script files failed to be read. */
private static ArrayList<SqlScript> getAutoUpdateScripts() throws NullPointerException, IOException
{
final ClassLoader loader = DatabaseUpdater.class.getClassLoader();
final ClassLoader loader = Thread.currentThread().getContextClassLoader();
// get the script list
@@ -76,9 +76,6 @@ public class BeaconBeamDTO implements IBaseDTO<DhBlockPos>, INetworkObject
public void close()
{ /* no closing needed */ }
@Override
public String toString() { return this.blockPos + " " + this.color; }
}
@@ -19,10 +19,8 @@
package com.seibel.distanthorizons.core.util;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.config.Config;
import com.seibel.distanthorizons.core.dependencyInjection.SingletonInjector;
import com.seibel.distanthorizons.core.render.renderer.DepthCalculator;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftClientWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.minecraft.IMinecraftRenderWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IClientLevelWrapper;
@@ -129,16 +127,13 @@ public class RenderUtil
}
public static float getNearClipPlaneInBlocksForFading(float partialTicks)
{
//float overdraw = getAutoOverdrawPrevention();
//return getNearClipPlaneDistanceInBlocks(partialTicks, overdraw);
return DepthCalculator.INSTANCE.actualMcBlockDistance;
float overdraw = getAutoOverdrawPrevention();
return getNearClipPlaneDistanceInBlocks(partialTicks, overdraw);
}
private static float getNearClipPlaneDistanceInBlocks(float partialTicks, float overdrawPreventionPercent)
{
int chunkRenderDistance = MC_RENDER.getRenderDistance();
//float chunkRenderDistance = ClientApi.actualMcBlockDistance / 16.0f;
float vanillaBlockRenderedDistance = chunkRenderDistance * LodUtil.CHUNK_WIDTH;
int vanillaBlockRenderedDistance = chunkRenderDistance * LodUtil.CHUNK_WIDTH;
float nearClipPlane;
if (Config.Client.Advanced.Debugging.lodOnlyMode.get())
@@ -31,10 +31,12 @@ import com.seibel.distanthorizons.coreapi.util.MathUtil;
import com.seibel.distanthorizons.core.util.gridList.MovableGridRingList;
import it.unimi.dsi.fastutil.longs.LongArrayFIFOQueue;
import it.unimi.dsi.fastutil.longs.LongIterator;
import com.seibel.distanthorizons.core.logging.DhLogger;
import org.jetbrains.annotations.Nullable;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.LongConsumer;
/**
@@ -96,27 +98,9 @@ public class QuadTree<T>
// getters and setters //
//=====================//
/** @return the value at the given section position. Null will be returned if the value is missing or the position is out of bounds. */
@Nullable
public final T tryGetValue(long pos)
{
QuadNode<T> node = this.tryGetNode(pos);
if (node != null)
{
return node.value;
}
return null;
}
/** @return the node at the given section position, null if out of bounds */
@Nullable
public final QuadNode<T> tryGetNode(long pos) { return this.getOrSetNode(pos, false, null, false); }
/** @return the node at the given section position */
@Nullable
public final QuadNode<T> getNode(long pos) throws IndexOutOfBoundsException { return this.getOrSetNode(pos, false, null, true); }
/** @return the value at the given section position */
@Nullable
public final T getValue(long pos) throws IndexOutOfBoundsException
@@ -138,24 +122,16 @@ public class QuadTree<T>
return previousValue;
}
/** @param throwIfOutOfBounds if false returns null */
/** @param runBoundaryChecks should only ever be set to true internally for removing out of bound nodes */
@Nullable
protected final QuadNode<T> getOrSetNode(long pos, boolean setNewValue, T newValue, boolean throwIfOutOfBounds) throws IndexOutOfBoundsException
protected final QuadNode<T> getOrSetNode(long pos, boolean setNewValue, T newValue, boolean runBoundaryChecks) throws IndexOutOfBoundsException
{
if (!this.isSectionPosInBounds(pos))
if (runBoundaryChecks && !this.isSectionPosInBounds(pos))
{
// how should out-of-bounds positions be handled?
if (throwIfOutOfBounds)
{
int radius = this.diameterInBlocks() / 2;
DhBlockPos2D minBlockPos = this.getCenterBlockPos().add(new DhBlockPos2D(-radius, -radius));
DhBlockPos2D maxBlockPos = this.getCenterBlockPos().add(new DhBlockPos2D(radius, radius));
throw new IndexOutOfBoundsException("QuadTree GetOrSet failed. Position out of bounds, min block pos: [" + minBlockPos + "], max block pos: [" + maxBlockPos + "], leaf detail level: [" + this.treeLeafDetailLevel + "], root detail level: [" + this.treeRootDetailLevel + "]. Requested section pos: [" + DhSectionPos.toString(pos) + "].");
}
else
{
return null;
}
int radius = this.diameterInBlocks() / 2;
DhBlockPos2D minPos = this.getCenterBlockPos().add(new DhBlockPos2D(-radius, -radius));
DhBlockPos2D maxPos = this.getCenterBlockPos().add(new DhBlockPos2D(radius, radius));
throw new IndexOutOfBoundsException("QuadTree GetOrSet failed. Position out of bounds, min pos: " + minPos + ", max pos: " + maxPos + ", min detail level: " + this.treeLeafDetailLevel + ", max detail level: " + this.treeRootDetailLevel + ". Given Position: [" + DhSectionPos.toString(pos) + "] = block pos: " + DhSectionPos.convertToDetailLevel(pos, LodUtil.BLOCK_DETAIL_LEVEL));
}
@@ -302,6 +278,46 @@ public class QuadTree<T>
removedItemConsumer.accept(quadNode.value);
}
});
// // remove out of bound nodes and clean up empty nodes
// // Note: this will iterate over a lot of unnecessary nodes, hopefully speed won't be an issue
// Iterator<DhSectionPos> rootNodePosIterator = this.rootNodePosIterator();
// while (rootNodePosIterator.hasNext())
// {
// // get the root node (regular nodeIterators won't return them if they are out of bounds)
// DhSectionPos rootPos = rootNodePosIterator.next();
// QuadNode<T> rootNode = this.getOrSetNode(rootPos, false, null, false);
// if (rootNode == null)
// {
// continue;
// }
//
// // remove any child nodes that are out of bounds
// Iterator<QuadNode<T>> nodeIterator = this.nodeIterator();
// while (nodeIterator.hasNext())
// {
// QuadNode<T> node = nodeIterator.next();
// if(!this.isSectionPosInBounds(node.sectionPos))
// {
// // node is out of bounds
//
// // FIXME(?) this appears to potentially return large nodes that are partially or entirely in bounds
//
// if (node.getNonNullChildCount() == 0)
// {
// // no child nodes, can be safely removed
// nodeIterator.remove();
// }
// else
// {
// // node can't be removed, but its value can be set to null
// node.value = null;
// }
// }
// }
// }
}
public final DhBlockPos2D getCenterBlockPos() { return this.centerBlockPos; }
@@ -528,9 +544,7 @@ public class QuadTree<T>
&& this.rootNodeIterator.hasNext())
{
long sectionPos = this.rootNodeIterator.nextLong();
// try-get to prevent concurrency errors if the tree is being moved while we walk through it
QuadNode<T> rootNode = QuadTree.this.tryGetNode(sectionPos);
QuadNode<T> rootNode = QuadTree.this.getNode(sectionPos);
if (rootNode != null)
{
nodeIterator = this.onlyReturnLeaves ? rootNode.getLeafNodeIterator() : rootNode.getNodeIterator(this.stopIteratingFunc);
@@ -30,7 +30,7 @@ import java.util.function.Consumer;
public class QuadTreeNodeIterator<T> implements Iterator<QuadNode<T>>
{
/** lowest numerical value, inclusive */
private final byte leafDetailLevel;
private final byte highestDetailLevel;
private final Queue<QuadNode<T>> validNodesForDetailLevel = new ArrayDeque<>();
@@ -48,7 +48,8 @@ public class QuadTreeNodeIterator<T> implements Iterator<QuadNode<T>>
{
this.onlyReturnLeafValues = onlyReturnLeafValues;
this.stopIteratingFunc = stopIteratingFunc;
this.leafDetailLevel = rootNode.parentTreeLeafDetailLevel;
// TODO the naming conversion for these are flipped in a lot of places
this.highestDetailLevel = rootNode.parentTreeLeafDetailLevel;
this.iteratorDetailLevel = DhSectionPos.getDetailLevel(rootNode.sectionPos);
@@ -109,9 +110,9 @@ public class QuadTreeNodeIterator<T> implements Iterator<QuadNode<T>>
@Override
public QuadNode<T> next()
{
if (this.iteratorDetailLevel < this.leafDetailLevel)
if (this.iteratorDetailLevel < this.highestDetailLevel)
{
throw new NoSuchElementException("Leaf detail level reached [" + this.leafDetailLevel + "].");
throw new NoSuchElementException("Highest detail level reached [" + this.highestDetailLevel + "].");
}
if (this.iteratorNodeQueue.size() == 0)
{
@@ -132,7 +133,7 @@ public class QuadTreeNodeIterator<T> implements Iterator<QuadNode<T>>
this.iteratorDetailLevel--;
// only continue if we can go down farther
if (this.iteratorDetailLevel >= this.leafDetailLevel)
if (this.iteratorDetailLevel >= this.highestDetailLevel)
{
Queue<QuadNode<T>> parentNodes = new LinkedList<>(this.validNodesForDetailLevel);
this.validNodesForDetailLevel.clear();
@@ -69,14 +69,10 @@ public class ThreadPoolUtil
@Nullable
public static ThreadPoolExecutor getBeaconCullingExecutor() { return beaconCullingThreadPool; }
// The main distinction between these thread pools is that one for compression has multiple threads and client handler is single-threaded
private static PriorityTaskPicker.Executor networkCompressionThreadPool;
@Nullable
public static PriorityTaskPicker.Executor getNetworkCompressionExecutor() { return networkCompressionThreadPool; }
private static ThreadPoolExecutor networkClientHandlerThreadPool;
@Nullable
public static ThreadPoolExecutor networkClientHandlerExecutor() { return networkClientHandlerThreadPool; }
public static final String FULL_DATA_MIGRATION_THREAD_NAME = "Full Data Migration";
@@ -107,8 +103,7 @@ public class ThreadPoolUtil
}
taskPicker = new PriorityTaskPicker();
networkCompressionThreadPool = taskPicker.createExecutor("Network Compression");
networkClientHandlerThreadPool = ThreadUtil.makeSingleThreadPool("Network Client Handler");
networkCompressionThreadPool = taskPicker.createExecutor("Network");
fileHandlerThreadPool = taskPicker.createExecutor("IO");
renderSectionLoadThreadPool = taskPicker.createExecutor("Render Loader");
chunkToLodBuilderThreadPool = taskPicker.createExecutor("LOD Builder");
@@ -138,7 +133,6 @@ public class ThreadPoolUtil
public static void shutdownThreadPools()
{
// standalone threads
networkClientHandlerThreadPool.shutdownNow();
taskPicker.shutdownNow();
beaconCullingThreadPool.shutdown();
fullDataMigrationThreadPool.shutdown();
@@ -20,7 +20,7 @@
package com.seibel.distanthorizons.core.world;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.enums.EMinecraftColor;
import com.seibel.distanthorizons.core.level.DhClientServerLevel;
import com.seibel.distanthorizons.core.util.TimerUtil;
import com.seibel.distanthorizons.core.util.LodUtil;
@@ -83,7 +83,7 @@ public class DhClientServerWorld extends AbstractDhServerWorld<DhClientServerLev
LOGGER.fatal("Failed to load client-server level, error: ["+e.getMessage()+"].", e);
ClientApi.INSTANCE.showChatMessageNextFrame(// red text
MinecraftTextFormat.RED + "Distant Horizons: ClientServer level loading failed." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
EMinecraftColor.RED + "Distant Horizons: ClientServer level loading failed." + EMinecraftColor.CLEAR_FORMATTING + "\n" +
"Unable to load level ["+levelWrapper.getDhIdentifier()+"], LODs may not appear. See log for more information.");
return null;
@@ -20,7 +20,7 @@
package com.seibel.distanthorizons.core.world;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.enums.EMinecraftColor;
import com.seibel.distanthorizons.core.file.structure.ClientOnlySaveStructure;
import com.seibel.distanthorizons.core.level.DhClientLevel;
import com.seibel.distanthorizons.core.level.IDhLevel;
@@ -96,7 +96,7 @@ public class DhClientWorld extends AbstractDhWorld implements IDhClientWorld
LOGGER.fatal("Failed to load client level, error: ["+e.getMessage()+"].", e);
ClientApi.INSTANCE.showChatMessageNextFrame(
MinecraftTextFormat.RED + "Distant Horizons: Client level loading failed." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
EMinecraftColor.RED + "Distant Horizons: Client level loading failed." + EMinecraftColor.CLEAR_FORMATTING + "\n" +
"Unable to load level ["+clientLevelWrapper.getDhIdentifier()+"], LODs may not appear. See log for more information.");
return null;
@@ -20,7 +20,7 @@
package com.seibel.distanthorizons.core.world;
import com.seibel.distanthorizons.core.api.internal.ClientApi;
import com.seibel.distanthorizons.core.enums.MinecraftTextFormat;
import com.seibel.distanthorizons.core.enums.EMinecraftColor;
import com.seibel.distanthorizons.core.level.DhServerLevel;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.ILevelWrapper;
import com.seibel.distanthorizons.core.wrapperInterfaces.world.IServerLevelWrapper;
@@ -64,7 +64,7 @@ public class DhServerWorld extends AbstractDhServerWorld<DhServerLevel>
LOGGER.fatal("Failed to load server level, error: ["+e.getMessage()+"].", e);
ClientApi.INSTANCE.showChatMessageNextFrame(
MinecraftTextFormat.RED + "Distant Horizons: Server level loading failed." + MinecraftTextFormat.CLEAR_FORMATTING + "\n" +
EMinecraftColor.RED + "Distant Horizons: Server level loading failed." + EMinecraftColor.CLEAR_FORMATTING + "\n" +
"Unable to load level ["+serverLevelWrapper.getDhIdentifier()+"], LODs may not appear. See log for more information.");
return null;
@@ -63,9 +63,7 @@ public interface IMinecraftRenderWrapper extends IBindable
/** @return -1 if no valid framebuffer is available yet */
int getTargetFramebuffer(); // Note: Iris is now hooking onto this for DH + Iris compat, try not to change (unless we wanna deal with some annoyances)
// Iris commit: https://github.com/IrisShaders/Iris/commit/a76a240527e93780bbcba57c09bef377419d47a7#diff-7b9ded0c79bbcdb130010373387756a28ee8d3640d522c0a5b7acd0abbfc20aeR16
/** @return -1 if there was an issue or no texture exists */
int getDepthTextureId();
/** @return -1 if there was an issue or no texture exists */
int getColorTextureId();
int getTargetFramebufferViewportWidth();
int getTargetFramebufferViewportHeight();
@@ -84,13 +84,7 @@
"Show The Options Button",
"distanthorizons.config.client.optionsButton.@tooltip":
"Show the config button to the left of the fov button",
"distanthorizons.config.client.dynamicFadeUseOpaqueMcDepth":
"Dynamic Fade Use MC Opaque Depth",
"distanthorizons.config.client.dynamicFadeExportPath":
"Dynamic Fade Export Path",
"distanthorizons.config.client.dynamicFadeExportPath.@tooltip":
"Press 'p' to export the depth textures for troubleshooting",
"distanthorizons.config.client.advanced":
@@ -233,10 +227,6 @@
"Beacon render height",
"distanthorizons.config.client.advanced.graphics.genericRendering.beaconRenderHeight.@tooltip":
"Sets the maximum height at which beacons will render. \nThis will only affect new beacons coming into LOD render distance. \nBeacons currently visible in LOD chunks will not be affected.",
"distanthorizons.config.client.advanced.graphics.genericRendering.expandDistantBeacons":
"Expand Distant Beacons",
"distanthorizons.config.client.advanced.graphics.genericRendering.expandDistantBeacons.@tooltip":
"If true LOD beacon beams will be rendered wider at extreme distances, \nmaking them easier to see. \nIf false all LOD beacon beams will only ever be 1 block wide.",
"distanthorizons.config.client.advanced.graphics.genericRendering.enableBeaconRendering.@tooltip":
"If true LOD beacon beams will be rendered.",
"distanthorizons.config.client.advanced.graphics.genericRendering.enableCloudRendering":
Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB